chore: delete scaletest infrastructure (#19603)

We've successfully migrated the latest iteration of our scaletest
infrastructure (`scaletest/terraform/action`) to
https://github.com/coder/scaletest (private repo). This PR removes the
older iterations, and the scriptsfor spinning up & running the load
generators against that infrastructure (`scaletest.sh`). The tooling for
generating load against a Coder deployment remains untouched, as does
the public documentation for that tooling (i.e. `coder exp scaletest`).

If we ever need that old scaletest Terraform code, it's always in the
git history!
This commit is contained in:
Ethan
2025-08-28 19:07:50 +10:00
committed by GitHub
parent 74fb2aaf08
commit 43fe44db50
41 changed files with 1 additions and 4269 deletions
+1 -1
View File
@@ -44,7 +44,7 @@ environments.
> for your users.
> To avoid potential outages and orphaned resources, we recommend that you run
> scale tests on a secondary "staging" environment or a dedicated
> [Kubernetes playground cluster](https://github.com/coder/coder/tree/main/scaletest/terraform).
> Kubernetes playground cluster.
>
> Run it against a production environment at your own risk.
-109
View File
@@ -1,109 +0,0 @@
# Scale Testing
This folder contains CLI commands, Terraform code, and scripts to aid in performing load tests of Coder.
At a high level, it performs the following steps:
- Using the Terraform code in `./terraform`, stands up a preconfigured Google Cloud environment
consisting of a VPC, GKE Cluster, and CloudSQL instance.
> **Note: You must have an existing Google Cloud project available.**
- Creates a dedicated namespace for Coder and installs Coder using the Helm chart in this namespace.
- Configures the Coder deployment with random credentials and a predefined Kubernetes template.
> **Note:** These credentials are stored in `${PROJECT_ROOT}/scaletest/.coderv2/coder.env`.
- Creates a number of workspaces and waits for them to all start successfully. These workspaces
are ephemeral and do not contain any persistent resources.
- Waits for 10 minutes to allow things to settle and establish a baseline.
- Generates web terminal traffic to all workspaces for 30 minutes.
- Directly after traffic generation, captures goroutine and heap snapshots of the Coder deployment.
- Tears down all resources (unless `--skip-cleanup` is specified).
## Usage
The main entrypoint is the `scaletest.sh` script.
```console
$ scaletest.sh --help
Usage: scaletest.sh --name <name> --project <project> --num-workspaces <num-workspaces> --scenario <scenario> [--dry-run] [--skip-cleanup]
```
### Required arguments
- `--name`: Name for the loadtest. This is added as a prefix to resources created by Terraform (e.g. `joe-big-loadtest`).
- `--project`: Google Cloud project in which to create the resources (example: `my-loadtest-project`).
- `--num-workspaces`: Number of workspaces to create (example: `10`).
- `--scenario`: Deployment scenario to use (example: `small`). See `terraform/scenario-*.tfvars`.
> **Note:** In order to capture Prometheus metrics, you must define the environment variables
> `SCALETEST_PROMETHEUS_REMOTE_WRITE_USER` and `SCALETEST_PROMETHEUS_REMOTE_WRITE_PASSWORD`.
### Optional arguments
- `--dry-run`: Do not perform any action and instead print what would be executed.
- `--skip-cleanup`: Do not perform any cleanup. You will be responsible for deleting any resources this creates.
### Environment Variables
All of the above arguments may be specified as environment variables. Consult the script for details.
### Prometheus Metrics
To capture Prometheus metrics from the loadtest, two environment variables are required:
- `SCALETEST_PROMETHEUS_REMOTE_WRITE_USER`
- `SCALETEST_PROMETHEUS_REMOTE_WRITE_PASSWORD`
### Enterprise License
To add an Enterprise license, set the `SCALETEST_CODER_LICENSE` environment variable to the JWT string
## Scenarios
A scenario defines a number of variables that override the default Terraform variables.
A number of existing scenarios are provided in `scaletest/terraform/scenario-*.tfvars`.
For example, `scenario-small.tfvars` includes the following variable definitions:
```hcl
nodepool_machine_type_coder = "t2d-standard-2"
nodepool_machine_type_workspaces = "t2d-standard-2"
coder_cpu = "1000m" # Leaving 1 CPU for system workloads
coder_mem = "4Gi" # Leaving 4GB for system workloads
```
To create your own scenario, simply add a new file `terraform/scenario-$SCENARIO_NAME.tfvars`.
In this file, override variables as required, consulting `vars.tf` as needed.
You can then use this scenario by specifying `--scenario $SCENARIO_NAME`.
For example, if your scenario file were named `scenario-big-whopper2x.tfvars`, you would specify
`--scenario=big-whopper2x`.
## Utility scripts
A number of utility scripts are provided in `lib`, and are used by `scaletest.sh`:
- `coder_shim.sh`: a convenience script to run the `coder` binary with a predefined config root.
This is intended to allow running Coder CLI commands against the loadtest cluster without
modifying a user's existing Coder CLI configuration.
- `coder_init.sh`: Performs first-time user setup of an existing Coder instance, generating
a random password for the admin user. The admin user is named `admin@coder.com` by default.
Credentials are written to `scaletest/.coderv2/coder.env`.
- `coder_workspacetraffic.sh`: Runs traffic generation against the loadtest cluster and creates
a monitoring manifest for the traffic generation pod. This pod will restart automatically
after the traffic generation has completed.
## Grafana Dashboard
A sample Grafana dashboard is provided in `scaletest_dashboard.json`. This dashboard is intended
to be imported into an existing Grafana instance. It provides a number of useful metrics:
- **Control Plane Resources**: CPU, memory, and network usage for the Coder deployment, as well as the number of pod restarts.
- **Database**: Rows inserted/updated/deleted/returned, active connections, and transactions per second. Fine-grained `sqlQuerier` metrics are provided for Coder's database as well, broken down my query method.
- **HTTP requests**: Number of HTTP requests per second, broken down by status code and path.
- **Workspace Resources**: CPU, memory, and network usage for all workspaces.
- **Workspace Agents**: Workspace agent network usage, connection latency, and number of active connections.
- **Workspace Traffic**: Statistics related to workspace traffic generation.
- **Internals**: Provisioner job timings, concurrency, workspace builds, and AuthZ duration.
A subset of these metrics may be useful for a production deployment, but some are only useful
for load testing.
> **Note:** in particular, `sqlQuerier` metrics produce a large number of time series and may cause
> increased charges in your metrics provider.
-240
View File
@@ -1,240 +0,0 @@
#!/usr/bin/env bash
[[ -n ${VERBOSE:-} ]] && set -x
set -euo pipefail
PROJECT_ROOT="$(git rev-parse --show-toplevel)"
# shellcheck source=scripts/lib.sh
source "${PROJECT_ROOT}/scripts/lib.sh"
DRY_RUN="${DRY_RUN:-0}"
SCALETEST_NAME="${SCALETEST_NAME:-}"
SCALETEST_NUM_WORKSPACES="${SCALETEST_NUM_WORKSPACES:-}"
SCALETEST_SCENARIO="${SCALETEST_SCENARIO:-}"
SCALETEST_PROJECT="${SCALETEST_PROJECT:-}"
SCALETEST_PROMETHEUS_REMOTE_WRITE_USER="${SCALETEST_PROMETHEUS_REMOTE_WRITE_USER:-}"
SCALETEST_PROMETHEUS_REMOTE_WRITE_PASSWORD="${SCALETEST_PROMETHEUS_REMOTE_WRITE_PASSWORD:-}"
SCALETEST_CODER_LICENSE="${SCALETEST_CODER_LICENSE:-}"
SCALETEST_SKIP_CLEANUP="${SCALETEST_SKIP_CLEANUP:-0}"
SCALETEST_CREATE_CONCURRENCY="${SCALETEST_CREATE_CONCURRENCY:-10}"
SCALETEST_TRAFFIC_BYTES_PER_TICK="${SCALETEST_TRAFFIC_BYTES_PER_TICK:-1024}"
SCALETEST_TRAFFIC_TICK_INTERVAL="${SCALETEST_TRAFFIC_TICK_INTERVAL:-10s}"
SCALETEST_DESTROY="${SCALETEST_DESTROY:-0}"
script_name=$(basename "$0")
args="$(getopt -o "" -l create-concurrency:,destroy,dry-run,help,name:,num-workspaces:,project:,scenario:,skip-cleanup,traffic-bytes-per-tick:,traffic-tick-interval:, -- "$@")"
eval set -- "$args"
while true; do
case "$1" in
--create-concurrency)
SCALETEST_CREATE_CONCURRENCY="$2"
shift 2
;;
--destroy)
SCALETEST_DESTROY=1
shift
;;
--dry-run)
DRY_RUN=1
shift
;;
--help)
echo "Usage: $script_name --name <name> --project <project> --num-workspaces <num-workspaces> --scenario <scenario> [--create-concurrency <create-concurrency>] [--destroy] [--dry-run] [--skip-cleanup] [--traffic-bytes-per-tick <number>] [--traffic-tick-interval <duration>]"
exit 1
;;
--name)
SCALETEST_NAME="$2"
shift 2
;;
--num-workspaces)
SCALETEST_NUM_WORKSPACES="$2"
shift 2
;;
--project)
SCALETEST_PROJECT="$2"
shift 2
;;
--scenario)
SCALETEST_SCENARIO="$2"
shift 2
;;
--skip-cleanup)
SCALETEST_SKIP_CLEANUP=1
shift
;;
--traffic-bytes-per-tick)
SCALETEST_TRAFFIC_BYTES_PER_TICK="$2"
shift 2
;;
--traffic-tick-interval)
SCALETEST_TRAFFIC_TICK_INTERVAL="$2"
shift 2
;;
--)
shift
break
;;
*)
error "Unrecognized option: $1"
;;
esac
done
dependencies gcloud kubectl terraform
if [[ -z "${SCALETEST_NAME}" ]]; then
echo "Must specify --name"
exit 1
fi
if [[ -z "${SCALETEST_PROJECT}" ]]; then
echo "Must specify --project"
exit 1
fi
if [[ -z "${SCALETEST_NUM_WORKSPACES}" ]]; then
echo "Must specify --num-workspaces"
exit 1
fi
if [[ -z "${SCALETEST_SCENARIO}" ]]; then
echo "Must specify --scenario"
exit 1
fi
if [[ -z "${SCALETEST_PROMETHEUS_REMOTE_WRITE_USER}" ]] || [[ -z "${SCALETEST_PROMETHEUS_REMOTE_WRITE_PASSWORD}" ]]; then
echo "SCALETEST_PROMETHEUS_REMOTE_WRITE_USER or SCALETEST_PROMETHEUS_REMOTE_WRITE_PASSWORD not specified."
echo "No prometheus metrics will be collected!"
read -p "Continue (y/N)? " -n1 -r
if [[ "${REPLY}" != [yY] ]]; then
exit 1
fi
fi
SCALETEST_SCENARIO_VARS="${PROJECT_ROOT}/scaletest/terraform/scenario-${SCALETEST_SCENARIO}.tfvars"
if [[ ! -f "${SCALETEST_SCENARIO_VARS}" ]]; then
echo "Scenario ${SCALETEST_SCENARIO_VARS} not found."
echo "Please create it or choose another scenario:"
find "${PROJECT_ROOT}/scaletest/terraform" -type f -name 'scenario-*.tfvars'
exit 1
fi
if [[ "${SCALETEST_SKIP_CLEANUP}" == 1 ]]; then
log "WARNING: you told me to not clean up after myself, so this is now your job!"
fi
CONFIG_DIR="${PROJECT_ROOT}/scaletest/.coderv2"
if [[ -d "${CONFIG_DIR}" ]] && files=$(ls -qAH -- "${CONFIG_DIR}") && [[ -z "$files" ]]; then
echo "Cleaning previous configuration"
maybedryrun "$DRY_RUN" rm -fv "${CONFIG_DIR}/*"
fi
maybedryrun "$DRY_RUN" mkdir -p "${CONFIG_DIR}"
SCALETEST_SCENARIO_VARS="${PROJECT_ROOT}/scaletest/terraform/scenario-${SCALETEST_SCENARIO}.tfvars"
SCALETEST_SECRETS="${PROJECT_ROOT}/scaletest/terraform/secrets.tfvars"
SCALETEST_SECRETS_TEMPLATE="${PROJECT_ROOT}/scaletest/terraform/secrets.tfvars.tpl"
log "Writing scaletest secrets to file."
SCALETEST_NAME="${SCALETEST_NAME}" \
SCALETEST_PROJECT="${SCALETEST_PROJECT}" \
SCALETEST_PROMETHEUS_REMOTE_WRITE_USER="${SCALETEST_PROMETHEUS_REMOTE_WRITE_USER}" \
SCALETEST_PROMETHEUS_REMOTE_WRITE_PASSWORD="${SCALETEST_PROMETHEUS_REMOTE_WRITE_PASSWORD}" \
envsubst <"${SCALETEST_SECRETS_TEMPLATE}" >"${SCALETEST_SECRETS}"
pushd "${PROJECT_ROOT}/scaletest/terraform"
echo "Initializing terraform."
maybedryrun "$DRY_RUN" terraform init
echo "Setting up infrastructure."
maybedryrun "$DRY_RUN" terraform apply --var-file="${SCALETEST_SCENARIO_VARS}" --var-file="${SCALETEST_SECRETS}" --var state=started --auto-approve
if [[ "${DRY_RUN}" != 1 ]]; then
SCALETEST_CODER_URL=$(<"${CONFIG_DIR}/url")
else
SCALETEST_CODER_URL="http://coder.dryrun.local:3000"
fi
KUBECONFIG="${PROJECT_ROOT}/scaletest/.coderv2/${SCALETEST_NAME}-cluster.kubeconfig"
echo "Waiting for Coder deployment at ${SCALETEST_CODER_URL} to become ready"
max_attempts=10
for attempt in $(seq 1 $max_attempts); do
maybedryrun "$DRY_RUN" curl --silent --fail --output /dev/null "${SCALETEST_CODER_URL}/api/v2/buildinfo"
curl_status=$?
if [[ $curl_status -eq 0 ]]; then
break
fi
if attempt -eq $max_attempts; then
echo
echo "Coder deployment failed to become ready in time!"
exit 1
fi
echo "Coder deployment not ready yet (${attempt}/${max_attempts}), sleeping 3 seconds"
maybedryrun "$DRY_RUN" sleep 3
done
echo "Initializing Coder deployment."
DRY_RUN="$DRY_RUN" "${PROJECT_ROOT}/scaletest/lib/coder_init.sh" "${SCALETEST_CODER_URL}"
if [[ -n "${SCALETEST_CODER_LICENSE}" ]]; then
echo "Applying Coder Enterprise License"
DRY_RUN="$DRY_RUN" "${PROJECT_ROOT}/scaletest/lib/coder_shim.sh" license add -l "${SCALETEST_CODER_LICENSE}"
fi
echo "Creating ${SCALETEST_NUM_WORKSPACES} workspaces."
DRY_RUN="$DRY_RUN" "${PROJECT_ROOT}/scaletest/lib/coder_shim.sh" exp scaletest create-workspaces \
--count "${SCALETEST_NUM_WORKSPACES}" \
--template=kubernetes \
--concurrency "${SCALETEST_CREATE_CONCURRENCY}" \
--no-cleanup
echo "Sleeping 10 minutes to establish a baseline measurement."
maybedryrun "$DRY_RUN" sleep 600
echo "Sending traffic to workspaces"
maybedryrun "$DRY_RUN" "${PROJECT_ROOT}/scaletest/lib/coder_workspacetraffic.sh" \
--name "${SCALETEST_NAME}" \
--traffic-bytes-per-tick "${SCALETEST_TRAFFIC_BYTES_PER_TICK}" \
--traffic-tick-interval "${SCALETEST_TRAFFIC_TICK_INTERVAL}"
maybedryrun "$DRY_RUN" kubectl --kubeconfig="${KUBECONFIG}" -n "coder-${SCALETEST_NAME}" wait pods coder-scaletest-workspace-traffic --for condition=Ready
echo "Sleeping 15 minutes for traffic generation"
maybedryrun "$DRY_RUN" sleep 900
echo "Starting pprof"
maybedryrun "$DRY_RUN" kubectl -n "coder-${SCALETEST_NAME}" port-forward deployment/coder 6061:6060 &
pfpid=$!
maybedryrun "$DRY_RUN" trap "kill $pfpid" EXIT
echo "Waiting for pprof endpoint to become available"
pprof_attempt_counter=0
while ! maybedryrun "$DRY_RUN" timeout 1 bash -c "echo > /dev/tcp/localhost/6061"; do
if [[ $pprof_attempt_counter -eq 10 ]]; then
echo
echo "pprof failed to become ready in time!"
exit 1
fi
((pprof_attempt_counter += 1))
maybedryrun "$DRY_RUN" sleep 3
done
echo "Taking pprof snapshots"
maybedryrun "$DRY_RUN" curl --silent --fail --output "${SCALETEST_NAME}-heap.pprof.gz" http://localhost:6061/debug/pprof/heap
maybedryrun "$DRY_RUN" curl --silent --fail --output "${SCALETEST_NAME}-goroutine.pprof.gz" http://localhost:6061/debug/pprof/goroutine
# No longer need to port-forward
maybedryrun "$DRY_RUN" kill "$pfpid"
maybedryrun "$DRY_RUN" trap - EXIT
if [[ "${SCALETEST_SKIP_CLEANUP}" == 1 ]]; then
echo "Leaving resources up for you to inspect."
echo "Please don't forget to clean up afterwards:"
echo "cd terraform && terraform destroy --var-file=${SCALETEST_SCENARIO_VARS} --var-file=${SCALETEST_SECRETS} --auto-approve"
exit 0
fi
if [[ "${SCALETEST_DESTROY}" == 1 ]]; then
echo "Destroying infrastructure"
maybedryrun "$DRY_RUN" terraform destroy --var-file="${SCALETEST_SCENARIO_VARS}" --var-file="${SCALETEST_SECRETS}" --auto-approve
else
echo "Scaling down infrastructure"
maybedryrun "$DRY_RUN" terraform apply --var-file="${SCALETEST_SCENARIO_VARS}" --var-file="${SCALETEST_SECRETS}" --var state=stopped --auto-approve
fi
-1
View File
@@ -1 +0,0 @@
*.tfvars
-21
View File
@@ -1,21 +0,0 @@
data "cloudflare_zone" "domain" {
name = var.cloudflare_domain
}
resource "cloudflare_record" "coder" {
for_each = local.deployments
zone_id = data.cloudflare_zone.domain.zone_id
name = "${each.value.subdomain}.${var.cloudflare_domain}"
content = google_compute_address.coder[each.key].address
type = "A"
ttl = 3600
}
resource "cloudflare_record" "coder_wildcard" {
for_each = local.deployments
zone_id = data.cloudflare_zone.domain.id
name = each.value.wildcard_subdomain
content = cloudflare_record.coder[each.key].name
type = "CNAME"
ttl = 3600
}
@@ -1,120 +0,0 @@
coder:
workspaceProxy: ${workspace_proxy}
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: "cloud.google.com/gke-nodepool"
operator: "In"
values: ["${node_pool}"]
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
podAffinityTerm:
topologyKey: "kubernetes.io/hostname"
labelSelector:
matchExpressions:
- key: "app.kubernetes.io/instance"
operator: "In"
values: ["${release_name}"]
env:
%{~ if workspace_proxy ~}
- name: "CODER_ACCESS_URL"
value: "${access_url}"
- name: "CODER_WILDCARD_ACCESS_URL"
value: "${wildcard_access_url}"
- name: CODER_PRIMARY_ACCESS_URL
value: "${primary_url}"
- name: CODER_PROXY_SESSION_TOKEN
valueFrom:
secretKeyRef:
key: token
name: "${proxy_token}"
%{~ endif ~}
%{~ if provisionerd ~}
- name: "CODER_URL"
value: "${access_url}"
- name: "CODER_PROVISIONERD_TAGS"
value: "scope=organization,deployment=${deployment}"
- name: "CODER_PROVISIONER_DAEMON_NAME"
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: "CODER_CONFIG_DIR"
value: "/tmp/config"
%{~ endif ~}
%{~ if !workspace_proxy && !provisionerd ~}
- name: "CODER_ACCESS_URL"
value: "${access_url}"
- name: "CODER_WILDCARD_ACCESS_URL"
value: "${wildcard_access_url}"
- name: "CODER_PG_CONNECTION_URL"
valueFrom:
secretKeyRef:
name: "${db_secret}"
key: url
- name: "CODER_PROVISIONER_DAEMONS"
value: "0"
- name: CODER_PROVISIONER_DAEMON_PSK
valueFrom:
secretKeyRef:
key: psk
name: "${provisionerd_psk}"
- name: "CODER_PROMETHEUS_COLLECT_AGENT_STATS"
value: "true"
- name: "CODER_PROMETHEUS_COLLECT_DB_METRICS"
value: "true"
- name: "CODER_PPROF_ENABLE"
value: "true"
%{~ endif ~}
- name: "CODER_CACHE_DIRECTORY"
value: "/tmp/coder"
- name: "CODER_TELEMETRY_ENABLE"
value: "false"
- name: "CODER_LOGGING_HUMAN"
value: "/dev/null"
- name: "CODER_LOGGING_STACKDRIVER"
value: "/dev/stderr"
- name: "CODER_PROMETHEUS_ENABLE"
value: "true"
- name: "CODER_VERBOSE"
value: "true"
- name: "CODER_EXPERIMENTS"
value: "${experiments}"
- name: "CODER_DANGEROUS_DISABLE_RATE_LIMITS"
value: "true"
- name: "CODER_DANGEROUS_ALLOW_PATH_APP_SITE_OWNER_ACCESS"
value: "true"
image:
repo: ${image_repo}
tag: ${image_tag}
replicaCount: "${replicas}"
resources:
requests:
cpu: "${cpu_request}"
memory: "${mem_request}"
limits:
cpu: "${cpu_limit}"
memory: "${mem_limit}"
securityContext:
readOnlyRootFilesystem: true
%{~ if !provisionerd ~}
service:
enable: true
sessionAffinity: None
loadBalancerIP: "${ip_address}"
%{~ endif ~}
volumeMounts:
- mountPath: "/tmp"
name: cache
readOnly: false
volumes:
- emptyDir:
sizeLimit: 1024Mi
name: cache
%{~ if !provisionerd ~}
tls:
secretNames:
- "${tls_secret_name}"
%{~ endif ~}
-102
View File
@@ -1,102 +0,0 @@
data "http" "coder_healthy" {
url = local.deployments.primary.url
// Wait up to 5 minutes for DNS to propagate
retry {
attempts = 30
min_delay_ms = 10000
}
lifecycle {
postcondition {
condition = self.status_code == 200
error_message = "${self.url} returned an unhealthy status code"
}
}
depends_on = [helm_release.coder_primary, cloudflare_record.coder["primary"]]
}
resource "null_resource" "api_key" {
provisioner "local-exec" {
interpreter = ["/bin/bash", "-c"]
command = <<EOF
set -e
curl '${local.deployments.primary.url}/api/v2/users/first' \
--data-raw $'{"email":"${local.coder_admin_email}","password":"${local.coder_admin_password}","username":"${local.coder_admin_user}","name":"${local.coder_admin_full_name}","trial":false}' \
--insecure --silent --output /dev/null
session_token=$(curl '${local.deployments.primary.url}/api/v2/users/login' \
--data-raw $'{"email":"${local.coder_admin_email}","password":"${local.coder_admin_password}"}' \
--insecure --silent | jq -r .session_token)
echo -n $${session_token} > ${path.module}/.coderv2/session_token
api_key=$(curl '${local.deployments.primary.url}/api/v2/users/me/keys/tokens' \
-H "Coder-Session-Token: $${session_token}" \
--data-raw '{"token_name":"terraform","scope":"all"}' \
--insecure --silent | jq -r .key)
echo -n $${api_key} > ${path.module}/.coderv2/api_key
EOF
}
depends_on = [data.http.coder_healthy]
}
data "local_file" "api_key" {
filename = "${path.module}/.coderv2/api_key"
depends_on = [null_resource.api_key]
}
resource "null_resource" "license" {
provisioner "local-exec" {
interpreter = ["/bin/bash", "-c"]
command = <<EOF
curl '${local.deployments.primary.url}/api/v2/licenses' \
-H "Coder-Session-Token: ${trimspace(data.local_file.api_key.content)}" \
--data-raw '{"license":"${var.coder_license}"}' \
--insecure --silent --output /dev/null
EOF
}
}
resource "null_resource" "europe_proxy_token" {
provisioner "local-exec" {
interpreter = ["/bin/bash", "-c"]
command = <<EOF
curl '${local.deployments.primary.url}/api/v2/workspaceproxies' \
-H "Coder-Session-Token: ${trimspace(data.local_file.api_key.content)}" \
--data-raw '{"name":"europe","display_name":"Europe","icon":"/emojis/1f950.png"}' \
--insecure --silent \
| jq -r .proxy_token > ${path.module}/.coderv2/europe_proxy_token
EOF
}
depends_on = [null_resource.license]
}
data "local_file" "europe_proxy_token" {
filename = "${path.module}/.coderv2/europe_proxy_token"
depends_on = [null_resource.europe_proxy_token]
}
resource "null_resource" "asia_proxy_token" {
provisioner "local-exec" {
interpreter = ["/bin/bash", "-c"]
command = <<EOF
curl '${local.deployments.primary.url}/api/v2/workspaceproxies' \
-H "Coder-Session-Token: ${trimspace(data.local_file.api_key.content)}" \
--data-raw '{"name":"asia","display_name":"Asia","icon":"/emojis/1f35b.png"}' \
--insecure --silent \
| jq -r .proxy_token > ${path.module}/.coderv2/asia_proxy_token
EOF
}
depends_on = [null_resource.license]
}
data "local_file" "asia_proxy_token" {
filename = "${path.module}/.coderv2/asia_proxy_token"
depends_on = [null_resource.asia_proxy_token]
}
@@ -1,340 +0,0 @@
resource "local_file" "kubernetes_template" {
filename = "${path.module}/.coderv2/templates/kubernetes/main.tf"
content = <<EOF
terraform {
required_providers {
coder = {
source = "coder/coder"
version = "~> 2.1.0"
}
kubernetes = {
source = "hashicorp/kubernetes"
version = "~> 2.30"
}
}
}
provider "coder" {}
provider "kubernetes" {
config_path = null # always use host
}
data "coder_workspace" "me" {}
data "coder_workspace_owner" "me" {}
resource "coder_agent" "main" {
os = "linux"
arch = "amd64"
}
resource "coder_script" "websocat" {
agent_id = coder_agent.main.id
display_name = "websocat"
script = <<EOF2
curl -sSL -o /tmp/websocat https://github.com/vi/websocat/releases/download/v1.12.0/websocat.x86_64-unknown-linux-musl
chmod +x /tmp/websocat
/tmp/websocat --exit-on-eof --binary ws-l:127.0.0.1:1234 mirror:
EOF2
run_on_start = true
}
resource "coder_app" "wsecho" {
agent_id = coder_agent.main.id
slug = "wsec"
display_name = "WebSocket Echo"
url = "http://localhost:1234"
share = "authenticated"
}
resource "kubernetes_pod" "main" {
count = data.coder_workspace.me.start_count
metadata {
name = "coder-$${lower(data.coder_workspace_owner.me.name)}-$${lower(data.coder_workspace.me.name)}"
namespace = "${local.coder_namespace}"
labels = {
"app.kubernetes.io/name" = "coder-workspace"
"app.kubernetes.io/instance" = "coder-workspace-$${lower(data.coder_workspace_owner.me.name)}-$${lower(data.coder_workspace.me.name)}"
}
}
spec {
security_context {
run_as_user = "1000"
fs_group = "1000"
}
container {
name = "dev"
image = "${var.workspace_image}"
image_pull_policy = "Always"
command = ["sh", "-c", coder_agent.main.init_script]
security_context {
run_as_user = "1000"
}
env {
name = "CODER_AGENT_TOKEN"
value = coder_agent.main.token
}
resources {
requests = {
"cpu" = "${local.scenarios[var.scenario].workspaces.cpu_request}"
"memory" = "${local.scenarios[var.scenario].workspaces.mem_request}"
}
limits = {
"cpu" = "${local.scenarios[var.scenario].workspaces.cpu_limit}"
"memory" = "${local.scenarios[var.scenario].workspaces.mem_limit}"
}
}
}
affinity {
node_affinity {
required_during_scheduling_ignored_during_execution {
node_selector_term {
match_expressions {
key = "cloud.google.com/gke-nodepool"
operator = "In"
values = ["${google_container_node_pool.node_pool["primary_workspaces"].name}","${google_container_node_pool.node_pool["europe_workspaces"].name}","${google_container_node_pool.node_pool["asia_workspaces"].name}"]
}
}
}
}
}
}
}
EOF
}
resource "kubernetes_config_map" "template_primary" {
provider = kubernetes.primary
metadata {
name = "coder-template"
namespace = kubernetes_namespace.coder_primary.metadata.0.name
}
data = {
"main.tf" = local_file.kubernetes_template.content
}
}
resource "kubernetes_job" "push_template_primary" {
provider = kubernetes.primary
metadata {
name = "${var.name}-push-template"
namespace = kubernetes_namespace.coder_primary.metadata.0.name
labels = {
"app.kubernetes.io/name" = "${var.name}-push-template"
}
}
spec {
completions = 1
template {
metadata {}
spec {
affinity {
node_affinity {
required_during_scheduling_ignored_during_execution {
node_selector_term {
match_expressions {
key = "cloud.google.com/gke-nodepool"
operator = "In"
values = ["${google_container_node_pool.node_pool["primary_misc"].name}"]
}
}
}
}
}
container {
name = "cli"
image = "${var.coder_image_repo}:${var.coder_image_tag}"
command = [
"/opt/coder",
"--verbose",
"--url=${local.deployments.primary.url}",
"--token=${trimspace(data.local_file.api_key.content)}",
"templates",
"push",
"--directory=/home/coder/template",
"--provisioner-tag=scope=organization",
"--provisioner-tag=deployment=primary",
"--yes",
"kubernetes-primary"
]
volume_mount {
name = "coder-template"
mount_path = "/home/coder/template/main.tf"
sub_path = "main.tf"
}
}
volume {
name = "coder-template"
config_map {
name = kubernetes_config_map.template_primary.metadata.0.name
}
}
restart_policy = "Never"
}
}
}
wait_for_completion = true
depends_on = [helm_release.provisionerd_primary]
}
resource "kubernetes_config_map" "template_europe" {
provider = kubernetes.europe
metadata {
name = "coder-template"
namespace = kubernetes_namespace.coder_europe.metadata.0.name
}
data = {
"main.tf" = local_file.kubernetes_template.content
}
}
resource "kubernetes_job" "push_template_europe" {
provider = kubernetes.europe
metadata {
name = "${var.name}-push-template"
namespace = kubernetes_namespace.coder_europe.metadata.0.name
labels = {
"app.kubernetes.io/name" = "${var.name}-push-template"
}
}
spec {
completions = 1
template {
metadata {}
spec {
affinity {
node_affinity {
required_during_scheduling_ignored_during_execution {
node_selector_term {
match_expressions {
key = "cloud.google.com/gke-nodepool"
operator = "In"
values = ["${google_container_node_pool.node_pool["europe_misc"].name}"]
}
}
}
}
}
container {
name = "cli"
image = "${var.coder_image_repo}:${var.coder_image_tag}"
command = [
"/opt/coder",
"--verbose",
"--url=${local.deployments.primary.url}",
"--token=${trimspace(data.local_file.api_key.content)}",
"templates",
"push",
"--directory=/home/coder/template",
"--provisioner-tag=scope=organization",
"--provisioner-tag=deployment=europe",
"--yes",
"kubernetes-europe"
]
volume_mount {
name = "coder-template"
mount_path = "/home/coder/template/main.tf"
sub_path = "main.tf"
}
}
volume {
name = "coder-template"
config_map {
name = kubernetes_config_map.template_europe.metadata.0.name
}
}
restart_policy = "Never"
}
}
}
wait_for_completion = true
depends_on = [helm_release.provisionerd_europe]
}
resource "kubernetes_config_map" "template_asia" {
provider = kubernetes.asia
metadata {
name = "coder-template"
namespace = kubernetes_namespace.coder_asia.metadata.0.name
}
data = {
"main.tf" = local_file.kubernetes_template.content
}
}
resource "kubernetes_job" "push_template_asia" {
provider = kubernetes.asia
metadata {
name = "${var.name}-push-template"
namespace = kubernetes_namespace.coder_asia.metadata.0.name
labels = {
"app.kubernetes.io/name" = "${var.name}-push-template"
}
}
spec {
completions = 1
template {
metadata {}
spec {
affinity {
node_affinity {
required_during_scheduling_ignored_during_execution {
node_selector_term {
match_expressions {
key = "cloud.google.com/gke-nodepool"
operator = "In"
values = ["${google_container_node_pool.node_pool["asia_misc"].name}"]
}
}
}
}
}
container {
name = "cli"
image = "${var.coder_image_repo}:${var.coder_image_tag}"
command = [
"/opt/coder",
"--verbose",
"--url=${local.deployments.primary.url}",
"--token=${trimspace(data.local_file.api_key.content)}",
"templates",
"push",
"--directory=/home/coder/template",
"--provisioner-tag=scope=organization",
"--provisioner-tag=deployment=asia",
"--yes",
"kubernetes-asia"
]
volume_mount {
name = "coder-template"
mount_path = "/home/coder/template/main.tf"
sub_path = "main.tf"
}
}
volume {
name = "coder-template"
config_map {
name = kubernetes_config_map.template_asia.metadata.0.name
}
}
restart_policy = "Never"
}
}
}
wait_for_completion = true
depends_on = [helm_release.provisionerd_asia]
}
-228
View File
@@ -1,228 +0,0 @@
locals {
wait_baseline_duration = "5m"
bytes_per_tick = 1024
tick_interval = "100ms"
traffic_types = {
ssh = {
duration = "30m"
job_timeout = "35m"
flags = [
"--ssh",
]
}
webterminal = {
duration = "25m"
job_timeout = "30m"
flags = []
}
app = {
duration = "20m"
job_timeout = "25m"
flags = [
"--app=wsec",
]
}
}
}
resource "time_sleep" "wait_baseline" {
depends_on = [
kubernetes_job.create_workspaces_primary,
kubernetes_job.create_workspaces_europe,
kubernetes_job.create_workspaces_asia,
helm_release.prometheus_chart_primary,
helm_release.prometheus_chart_europe,
helm_release.prometheus_chart_asia,
]
create_duration = local.wait_baseline_duration
}
resource "kubernetes_job" "workspace_traffic_primary" {
provider = kubernetes.primary
for_each = local.traffic_types
metadata {
name = "${var.name}-workspace-traffic-${each.key}"
namespace = kubernetes_namespace.coder_primary.metadata.0.name
labels = {
"app.kubernetes.io/name" = "${var.name}-workspace-traffic-${each.key}"
}
}
spec {
completions = 1
backoff_limit = 0
template {
metadata {}
spec {
affinity {
node_affinity {
required_during_scheduling_ignored_during_execution {
node_selector_term {
match_expressions {
key = "cloud.google.com/gke-nodepool"
operator = "In"
values = ["${google_container_node_pool.node_pool["primary_misc"].name}"]
}
}
}
}
}
container {
name = "cli"
image = "${var.coder_image_repo}:${var.coder_image_tag}"
command = concat([
"/opt/coder",
"--verbose",
"--url=${local.deployments.primary.url}",
"--token=${trimspace(data.local_file.api_key.content)}",
"exp",
"scaletest",
"workspace-traffic",
"--template=kubernetes-primary",
"--concurrency=0",
"--bytes-per-tick=${local.bytes_per_tick}",
"--tick-interval=${local.tick_interval}",
"--scaletest-prometheus-wait=30s",
"--job-timeout=${local.traffic_types[each.key].duration}",
], local.traffic_types[each.key].flags)
}
restart_policy = "Never"
}
}
}
wait_for_completion = true
timeouts {
create = local.traffic_types[each.key].job_timeout
}
depends_on = [time_sleep.wait_baseline]
}
resource "kubernetes_job" "workspace_traffic_europe" {
provider = kubernetes.europe
for_each = local.traffic_types
metadata {
name = "${var.name}-workspace-traffic-${each.key}"
namespace = kubernetes_namespace.coder_europe.metadata.0.name
labels = {
"app.kubernetes.io/name" = "${var.name}-workspace-traffic-${each.key}"
}
}
spec {
completions = 1
backoff_limit = 0
template {
metadata {}
spec {
affinity {
node_affinity {
required_during_scheduling_ignored_during_execution {
node_selector_term {
match_expressions {
key = "cloud.google.com/gke-nodepool"
operator = "In"
values = ["${google_container_node_pool.node_pool["europe_misc"].name}"]
}
}
}
}
}
container {
name = "cli"
image = "${var.coder_image_repo}:${var.coder_image_tag}"
command = concat([
"/opt/coder",
"--verbose",
"--url=${local.deployments.primary.url}",
"--token=${trimspace(data.local_file.api_key.content)}",
"exp",
"scaletest",
"workspace-traffic",
"--template=kubernetes-europe",
"--concurrency=0",
"--bytes-per-tick=${local.bytes_per_tick}",
"--tick-interval=${local.tick_interval}",
"--scaletest-prometheus-wait=30s",
"--job-timeout=${local.traffic_types[each.key].duration}",
"--workspace-proxy-url=${local.deployments.europe.url}",
], local.traffic_types[each.key].flags)
}
restart_policy = "Never"
}
}
}
wait_for_completion = true
timeouts {
create = local.traffic_types[each.key].job_timeout
}
depends_on = [time_sleep.wait_baseline]
}
resource "kubernetes_job" "workspace_traffic_asia" {
provider = kubernetes.asia
for_each = local.traffic_types
metadata {
name = "${var.name}-workspace-traffic-${each.key}"
namespace = kubernetes_namespace.coder_asia.metadata.0.name
labels = {
"app.kubernetes.io/name" = "${var.name}-workspace-traffic-${each.key}"
}
}
spec {
completions = 1
backoff_limit = 0
template {
metadata {}
spec {
affinity {
node_affinity {
required_during_scheduling_ignored_during_execution {
node_selector_term {
match_expressions {
key = "cloud.google.com/gke-nodepool"
operator = "In"
values = ["${google_container_node_pool.node_pool["asia_misc"].name}"]
}
}
}
}
}
container {
name = "cli"
image = "${var.coder_image_repo}:${var.coder_image_tag}"
command = concat([
"/opt/coder",
"--verbose",
"--url=${local.deployments.primary.url}",
"--token=${trimspace(data.local_file.api_key.content)}",
"exp",
"scaletest",
"workspace-traffic",
"--template=kubernetes-asia",
"--concurrency=0",
"--bytes-per-tick=${local.bytes_per_tick}",
"--tick-interval=${local.tick_interval}",
"--scaletest-prometheus-wait=30s",
"--job-timeout=${local.traffic_types[each.key].duration}",
"--workspace-proxy-url=${local.deployments.asia.url}",
], local.traffic_types[each.key].flags)
}
restart_policy = "Never"
}
}
}
wait_for_completion = true
timeouts {
create = local.traffic_types[each.key].job_timeout
}
depends_on = [time_sleep.wait_baseline]
}
@@ -1,180 +0,0 @@
locals {
create_workspace_timeout = "30m"
}
resource "kubernetes_job" "create_workspaces_primary" {
provider = kubernetes.primary
metadata {
name = "${var.name}-create-workspaces"
namespace = kubernetes_namespace.coder_primary.metadata.0.name
labels = {
"app.kubernetes.io/name" = "${var.name}-create-workspaces"
}
}
spec {
completions = 1
backoff_limit = 0
template {
metadata {}
spec {
affinity {
node_affinity {
required_during_scheduling_ignored_during_execution {
node_selector_term {
match_expressions {
key = "cloud.google.com/gke-nodepool"
operator = "In"
values = ["${google_container_node_pool.node_pool["primary_misc"].name}"]
}
}
}
}
}
container {
name = "cli"
image = "${var.coder_image_repo}:${var.coder_image_tag}"
command = [
"/opt/coder",
"--verbose",
"--url=${local.deployments.primary.url}",
"--token=${trimspace(data.local_file.api_key.content)}",
"exp",
"scaletest",
"create-workspaces",
"--count=${local.scenarios[var.scenario].workspaces.count_per_deployment}",
"--template=kubernetes-primary",
"--concurrency=${local.scenarios[var.scenario].provisionerd.replicas}",
"--no-cleanup"
]
}
restart_policy = "Never"
}
}
}
wait_for_completion = true
timeouts {
create = local.create_workspace_timeout
}
depends_on = [kubernetes_job.push_template_primary]
}
resource "kubernetes_job" "create_workspaces_europe" {
provider = kubernetes.europe
metadata {
name = "${var.name}-create-workspaces"
namespace = kubernetes_namespace.coder_europe.metadata.0.name
labels = {
"app.kubernetes.io/name" = "${var.name}-create-workspaces"
}
}
spec {
completions = 1
backoff_limit = 0
template {
metadata {}
spec {
affinity {
node_affinity {
required_during_scheduling_ignored_during_execution {
node_selector_term {
match_expressions {
key = "cloud.google.com/gke-nodepool"
operator = "In"
values = ["${google_container_node_pool.node_pool["europe_misc"].name}"]
}
}
}
}
}
container {
name = "cli"
image = "${var.coder_image_repo}:${var.coder_image_tag}"
command = [
"/opt/coder",
"--verbose",
"--url=${local.deployments.primary.url}",
"--token=${trimspace(data.local_file.api_key.content)}",
"exp",
"scaletest",
"create-workspaces",
"--count=${local.scenarios[var.scenario].workspaces.count_per_deployment}",
"--template=kubernetes-europe",
"--concurrency=${local.scenarios[var.scenario].provisionerd.replicas}",
"--no-cleanup"
]
}
restart_policy = "Never"
}
}
}
wait_for_completion = true
timeouts {
create = local.create_workspace_timeout
}
depends_on = [kubernetes_job.push_template_europe]
}
resource "kubernetes_job" "create_workspaces_asia" {
provider = kubernetes.asia
metadata {
name = "${var.name}-create-workspaces"
namespace = kubernetes_namespace.coder_asia.metadata.0.name
labels = {
"app.kubernetes.io/name" = "${var.name}-create-workspaces"
}
}
spec {
completions = 1
backoff_limit = 0
template {
metadata {}
spec {
affinity {
node_affinity {
required_during_scheduling_ignored_during_execution {
node_selector_term {
match_expressions {
key = "cloud.google.com/gke-nodepool"
operator = "In"
values = ["${google_container_node_pool.node_pool["asia_misc"].name}"]
}
}
}
}
}
container {
name = "cli"
image = "${var.coder_image_repo}:${var.coder_image_tag}"
command = [
"/opt/coder",
"--verbose",
"--url=${local.deployments.primary.url}",
"--token=${trimspace(data.local_file.api_key.content)}",
"exp",
"scaletest",
"create-workspaces",
"--count=${local.scenarios[var.scenario].workspaces.count_per_deployment}",
"--template=kubernetes-asia",
"--concurrency=${local.scenarios[var.scenario].provisionerd.replicas}",
"--no-cleanup"
]
}
restart_policy = "Never"
}
}
}
wait_for_completion = true
timeouts {
create = local.create_workspace_timeout
}
depends_on = [kubernetes_job.push_template_asia]
}
-162
View File
@@ -1,162 +0,0 @@
data "google_compute_default_service_account" "default" {
project = var.project_id
depends_on = [google_project_service.api["compute.googleapis.com"]]
}
locals {
deployments = {
primary = {
subdomain = "primary.${var.name}"
wildcard_subdomain = "*.primary.${var.name}"
url = "https://primary.${var.name}.${var.cloudflare_domain}"
wildcard_access_url = "*.primary.${var.name}.${var.cloudflare_domain}"
region = "us-east1"
zone = "us-east1-c"
subnet = "scaletest"
}
europe = {
subdomain = "europe.${var.name}"
wildcard_subdomain = "*.europe.${var.name}"
url = "https://europe.${var.name}.${var.cloudflare_domain}"
wildcard_access_url = "*.europe.${var.name}.${var.cloudflare_domain}"
region = "europe-west1"
zone = "europe-west1-b"
subnet = "scaletest"
}
asia = {
subdomain = "asia.${var.name}"
wildcard_subdomain = "*.asia.${var.name}"
url = "https://asia.${var.name}.${var.cloudflare_domain}"
wildcard_access_url = "*.asia.${var.name}.${var.cloudflare_domain}"
region = "asia-southeast1"
zone = "asia-southeast1-a"
subnet = "scaletest"
}
}
node_pools = {
primary_coder = {
name = "coder"
cluster = "primary"
}
primary_workspaces = {
name = "workspaces"
cluster = "primary"
}
primary_misc = {
name = "misc"
cluster = "primary"
}
europe_coder = {
name = "coder"
cluster = "europe"
}
europe_workspaces = {
name = "workspaces"
cluster = "europe"
}
europe_misc = {
name = "misc"
cluster = "europe"
}
asia_coder = {
name = "coder"
cluster = "asia"
}
asia_workspaces = {
name = "workspaces"
cluster = "asia"
}
asia_misc = {
name = "misc"
cluster = "asia"
}
}
}
resource "google_container_cluster" "cluster" {
for_each = local.deployments
name = "${var.name}-${each.key}"
location = each.value.zone
project = var.project_id
network = google_compute_network.network.name
subnetwork = google_compute_subnetwork.subnetwork[each.key].name
networking_mode = "VPC_NATIVE"
default_max_pods_per_node = 256
ip_allocation_policy { # Required with networking_mode=VPC_NATIVE
cluster_secondary_range_name = local.secondary_ip_range_k8s_pods
services_secondary_range_name = local.secondary_ip_range_k8s_services
}
release_channel {
# Setting release channel as STABLE can cause unexpected cluster upgrades.
channel = "UNSPECIFIED"
}
initial_node_count = 1
remove_default_node_pool = true
network_policy {
enabled = true
}
depends_on = [
google_project_service.api["container.googleapis.com"]
]
monitoring_config {
enable_components = ["SYSTEM_COMPONENTS"]
managed_prometheus {
enabled = false
}
}
workload_identity_config {
workload_pool = "${data.google_project.project.project_id}.svc.id.goog"
}
lifecycle {
ignore_changes = [
maintenance_policy,
release_channel,
remove_default_node_pool
]
}
}
resource "google_container_node_pool" "node_pool" {
for_each = local.node_pools
name = each.value.name
location = local.deployments[each.value.cluster].zone
project = var.project_id
cluster = google_container_cluster.cluster[each.value.cluster].name
node_count = local.scenarios[var.scenario][each.value.name].nodepool_size
node_config {
oauth_scopes = [
"https://www.googleapis.com/auth/logging.write",
"https://www.googleapis.com/auth/monitoring",
"https://www.googleapis.com/auth/trace.append",
"https://www.googleapis.com/auth/devstorage.read_only",
"https://www.googleapis.com/auth/service.management.readonly",
"https://www.googleapis.com/auth/servicecontrol",
]
disk_size_gb = 100
machine_type = local.scenarios[var.scenario][each.value.name].machine_type
image_type = "cos_containerd"
service_account = data.google_compute_default_service_account.default.email
tags = ["gke-node", "${var.project_id}-gke"]
labels = {
env = var.project_id
}
metadata = {
disable-legacy-endpoints = "true"
}
kubelet_config {
cpu_manager_policy = ""
cpu_cfs_quota = false
pod_pids_limit = 0
}
}
lifecycle {
ignore_changes = [
management[0].auto_repair,
management[0].auto_upgrade,
timeouts,
node_config[0].resource_labels
]
}
}
-89
View File
@@ -1,89 +0,0 @@
resource "google_sql_database_instance" "db" {
name = "${var.name}-coder"
project = var.project_id
region = local.deployments.primary.region
database_version = "POSTGRES_14"
deletion_protection = false
depends_on = [google_service_networking_connection.private_vpc_connection]
settings {
tier = local.scenarios[var.scenario].cloudsql.tier
activation_policy = "ALWAYS"
availability_type = "ZONAL"
location_preference {
zone = local.deployments.primary.zone
}
database_flags {
name = "max_connections"
value = local.scenarios[var.scenario].cloudsql.max_connections
}
ip_configuration {
ipv4_enabled = false
private_network = google_compute_network.network.id
}
insights_config {
query_insights_enabled = true
query_string_length = 1024
record_application_tags = false
record_client_address = false
}
}
lifecycle {
ignore_changes = [deletion_protection, timeouts]
}
}
resource "google_sql_database" "coder" {
project = var.project_id
instance = google_sql_database_instance.db.id
name = "${var.name}-coder"
# required for postgres, otherwise db fails to delete
deletion_policy = "ABANDON"
lifecycle {
ignore_changes = [deletion_policy]
}
}
resource "random_password" "coder_postgres_password" {
length = 12
}
resource "random_password" "prometheus_postgres_password" {
length = 12
}
resource "google_sql_user" "coder" {
project = var.project_id
instance = google_sql_database_instance.db.id
name = "${var.name}-coder"
type = "BUILT_IN"
password = random_password.coder_postgres_password.result
# required for postgres, otherwise user fails to delete
deletion_policy = "ABANDON"
lifecycle {
ignore_changes = [deletion_policy, password]
}
}
resource "google_sql_user" "prometheus" {
project = var.project_id
instance = google_sql_database_instance.db.id
name = "${var.name}-prometheus"
type = "BUILT_IN"
password = random_password.prometheus_postgres_password.result
# required for postgres, otherwise user fails to delete
deletion_policy = "ABANDON"
lifecycle {
ignore_changes = [deletion_policy, password]
}
}
locals {
coder_db_url = "postgres://${google_sql_user.coder.name}:${urlencode(random_password.coder_postgres_password.result)}@${google_sql_database_instance.db.private_ip_address}/${google_sql_database.coder.name}?sslmode=disable"
}
-27
View File
@@ -1,27 +0,0 @@
locals {
project_apis = [
"cloudtrace",
"compute",
"container",
"logging",
"monitoring",
"servicemanagement",
"servicenetworking",
"sqladmin",
"stackdriver",
"storage-api",
]
}
data "google_project" "project" {
project_id = var.project_id
}
resource "google_project_service" "api" {
for_each = toset(local.project_apis)
project = data.google_project.project.project_id
service = "${each.value}.googleapis.com"
disable_dependent_services = false
disable_on_destroy = false
}
-154
View File
@@ -1,154 +0,0 @@
locals {
# Generate a /14 for each deployment.
cidr_networks = cidrsubnets(
"172.16.0.0/12",
2,
2,
2,
)
networks = {
alpha = local.cidr_networks[0]
bravo = local.cidr_networks[1]
charlie = local.cidr_networks[2]
}
# Generate a bunch of /18s within the subnet we're using from the above map.
cidr_subnetworks = cidrsubnets(
local.networks[var.name],
4, # PSA
4, # primary subnetwork
4, # primary k8s pod network
4, # primary k8s services network
4, # europe subnetwork
4, # europe k8s pod network
4, # europe k8s services network
4, # asia subnetwork
4, # asia k8s pod network
4, # asia k8s services network
)
psa_range_address = split("/", local.cidr_subnetworks[0])[0]
psa_range_prefix_length = tonumber(split("/", local.cidr_subnetworks[0])[1])
subnetworks = {
primary = local.cidr_subnetworks[1]
europe = local.cidr_subnetworks[4]
asia = local.cidr_subnetworks[7]
}
cluster_ranges = {
primary = {
pods = local.cidr_subnetworks[2]
services = local.cidr_subnetworks[3]
}
europe = {
pods = local.cidr_subnetworks[5]
services = local.cidr_subnetworks[6]
}
asia = {
pods = local.cidr_subnetworks[8]
services = local.cidr_subnetworks[9]
}
}
secondary_ip_range_k8s_pods = "k8s-pods"
secondary_ip_range_k8s_services = "k8s-services"
}
# Create a VPC for the deployment
resource "google_compute_network" "network" {
project = var.project_id
name = "${var.name}-scaletest"
description = "scaletest network for ${var.name}"
auto_create_subnetworks = false
}
# Create a subnetwork with a unique range for each region
resource "google_compute_subnetwork" "subnetwork" {
for_each = local.subnetworks
name = "${var.name}-${each.key}"
# Use the deployment region
region = local.deployments[each.key].region
network = google_compute_network.network.id
project = var.project_id
ip_cidr_range = each.value
private_ip_google_access = true
secondary_ip_range {
range_name = local.secondary_ip_range_k8s_pods
ip_cidr_range = local.cluster_ranges[each.key].pods
}
secondary_ip_range {
range_name = local.secondary_ip_range_k8s_services
ip_cidr_range = local.cluster_ranges[each.key].services
}
}
# Create a public IP for each region
resource "google_compute_address" "coder" {
for_each = local.deployments
project = var.project_id
region = each.value.region
name = "${var.name}-${each.key}-coder"
address_type = "EXTERNAL"
network_tier = "PREMIUM"
}
# Reserve an internal range for Google-managed services (PSA), used for Cloud
# SQL
resource "google_compute_global_address" "psa_peering" {
project = var.project_id
name = "${var.name}-sql-peering"
purpose = "VPC_PEERING"
address_type = "INTERNAL"
address = local.psa_range_address
prefix_length = local.psa_range_prefix_length
network = google_compute_network.network.self_link
}
resource "google_service_networking_connection" "private_vpc_connection" {
network = google_compute_network.network.id
service = "servicenetworking.googleapis.com"
reserved_peering_ranges = [google_compute_global_address.psa_peering.name]
}
# Join the new network to the observability network so we can talk to the
# Prometheus instance
data "google_compute_network" "observability" {
project = var.project_id
name = var.observability_cluster_vpc
}
resource "google_compute_network_peering" "scaletest_to_observability" {
name = "peer-${google_compute_network.network.name}-to-${data.google_compute_network.observability.name}"
network = google_compute_network.network.self_link
peer_network = data.google_compute_network.observability.self_link
import_custom_routes = true
export_custom_routes = true
}
resource "google_compute_network_peering" "observability_to_scaletest" {
name = "peer-${data.google_compute_network.observability.name}-to-${google_compute_network.network.name}"
network = data.google_compute_network.observability.self_link
peer_network = google_compute_network.network.self_link
import_custom_routes = true
export_custom_routes = true
}
# Allow traffic from the scaletest network into the observability network so we
# can connect to Prometheus
resource "google_compute_firewall" "observability_allow_from_scaletest" {
project = var.project_id
name = "allow-from-scaletest-${var.name}"
network = data.google_compute_network.observability.self_link
direction = "INGRESS"
source_ranges = [local.networks[var.name]]
allow {
protocol = "icmp"
}
allow {
protocol = "tcp"
ports = ["0-65535"]
}
}
@@ -1,131 +0,0 @@
resource "kubernetes_namespace" "coder_asia" {
provider = kubernetes.asia
metadata {
name = local.coder_namespace
}
lifecycle {
ignore_changes = [timeouts, wait_for_default_service_account]
}
depends_on = [google_container_node_pool.node_pool["asia_misc"]]
}
resource "kubernetes_secret" "provisionerd_psk_asia" {
provider = kubernetes.asia
type = "Opaque"
metadata {
name = "coder-provisioner-psk"
namespace = kubernetes_namespace.coder_asia.metadata.0.name
}
data = {
psk = random_password.provisionerd_psk.result
}
lifecycle {
ignore_changes = [timeouts, wait_for_service_account_token]
}
}
resource "kubernetes_secret" "proxy_token_asia" {
provider = kubernetes.asia
type = "Opaque"
metadata {
name = "coder-proxy-token"
namespace = kubernetes_namespace.coder_asia.metadata.0.name
}
data = {
token = trimspace(data.local_file.asia_proxy_token.content)
}
lifecycle {
ignore_changes = [timeouts, wait_for_service_account_token]
}
}
resource "kubernetes_secret" "coder_tls_asia" {
provider = kubernetes.asia
type = "kubernetes.io/tls"
metadata {
name = "coder-tls"
namespace = kubernetes_namespace.coder_asia.metadata.0.name
}
data = {
"tls.crt" = data.kubernetes_secret.coder_tls["asia"].data["tls.crt"]
"tls.key" = data.kubernetes_secret.coder_tls["asia"].data["tls.key"]
}
lifecycle {
ignore_changes = [timeouts, wait_for_service_account_token]
}
}
resource "helm_release" "coder_asia" {
provider = helm.asia
repository = local.coder_helm_repo
chart = local.coder_helm_chart
name = local.coder_release_name
version = var.coder_chart_version
namespace = kubernetes_namespace.coder_asia.metadata.0.name
values = [templatefile("${path.module}/coder_helm_values.tftpl", {
workspace_proxy = true,
provisionerd = false,
primary_url = local.deployments.primary.url,
proxy_token = kubernetes_secret.proxy_token_asia.metadata.0.name,
db_secret = null,
ip_address = google_compute_address.coder["asia"].address,
provisionerd_psk = null,
access_url = local.deployments.asia.url,
wildcard_access_url = local.deployments.asia.wildcard_access_url,
node_pool = google_container_node_pool.node_pool["asia_coder"].name,
release_name = local.coder_release_name,
experiments = var.coder_experiments,
image_repo = var.coder_image_repo,
image_tag = var.coder_image_tag,
replicas = local.scenarios[var.scenario].coder.replicas,
cpu_request = local.scenarios[var.scenario].coder.cpu_request,
mem_request = local.scenarios[var.scenario].coder.mem_request,
cpu_limit = local.scenarios[var.scenario].coder.cpu_limit,
mem_limit = local.scenarios[var.scenario].coder.mem_limit,
deployment = "asia",
tls_secret_name = kubernetes_secret.coder_tls_asia.metadata.0.name,
})]
depends_on = [null_resource.license]
}
resource "helm_release" "provisionerd_asia" {
provider = helm.asia
repository = local.coder_helm_repo
chart = local.provisionerd_helm_chart
name = local.provisionerd_release_name
version = var.provisionerd_chart_version
namespace = kubernetes_namespace.coder_asia.metadata.0.name
values = [templatefile("${path.module}/coder_helm_values.tftpl", {
workspace_proxy = false,
provisionerd = true,
primary_url = null,
proxy_token = null,
db_secret = null,
ip_address = null,
provisionerd_psk = kubernetes_secret.provisionerd_psk_asia.metadata.0.name,
access_url = local.deployments.primary.url,
wildcard_access_url = null,
node_pool = google_container_node_pool.node_pool["asia_coder"].name,
release_name = local.coder_release_name,
experiments = var.coder_experiments,
image_repo = var.coder_image_repo,
image_tag = var.coder_image_tag,
replicas = local.scenarios[var.scenario].provisionerd.replicas,
cpu_request = local.scenarios[var.scenario].provisionerd.cpu_request,
mem_request = local.scenarios[var.scenario].provisionerd.mem_request,
cpu_limit = local.scenarios[var.scenario].provisionerd.cpu_limit,
mem_limit = local.scenarios[var.scenario].provisionerd.mem_limit,
deployment = "asia",
tls_secret_name = null,
})]
depends_on = [null_resource.license]
}
@@ -1,131 +0,0 @@
resource "kubernetes_namespace" "coder_europe" {
provider = kubernetes.europe
metadata {
name = local.coder_namespace
}
lifecycle {
ignore_changes = [timeouts, wait_for_default_service_account]
}
depends_on = [google_container_node_pool.node_pool["europe_misc"]]
}
resource "kubernetes_secret" "provisionerd_psk_europe" {
provider = kubernetes.europe
type = "Opaque"
metadata {
name = "coder-provisioner-psk"
namespace = kubernetes_namespace.coder_europe.metadata.0.name
}
data = {
psk = random_password.provisionerd_psk.result
}
lifecycle {
ignore_changes = [timeouts, wait_for_service_account_token]
}
}
resource "kubernetes_secret" "proxy_token_europe" {
provider = kubernetes.europe
type = "Opaque"
metadata {
name = "coder-proxy-token"
namespace = kubernetes_namespace.coder_europe.metadata.0.name
}
data = {
token = trimspace(data.local_file.europe_proxy_token.content)
}
lifecycle {
ignore_changes = [timeouts, wait_for_service_account_token]
}
}
resource "kubernetes_secret" "coder_tls_europe" {
provider = kubernetes.europe
type = "kubernetes.io/tls"
metadata {
name = "coder-tls"
namespace = kubernetes_namespace.coder_europe.metadata.0.name
}
data = {
"tls.crt" = data.kubernetes_secret.coder_tls["europe"].data["tls.crt"]
"tls.key" = data.kubernetes_secret.coder_tls["europe"].data["tls.key"]
}
lifecycle {
ignore_changes = [timeouts, wait_for_service_account_token]
}
}
resource "helm_release" "coder_europe" {
provider = helm.europe
repository = local.coder_helm_repo
chart = local.coder_helm_chart
name = local.coder_release_name
version = var.coder_chart_version
namespace = kubernetes_namespace.coder_europe.metadata.0.name
values = [templatefile("${path.module}/coder_helm_values.tftpl", {
workspace_proxy = true,
provisionerd = false,
primary_url = local.deployments.primary.url,
proxy_token = kubernetes_secret.proxy_token_europe.metadata.0.name,
db_secret = null,
ip_address = google_compute_address.coder["europe"].address,
provisionerd_psk = null,
access_url = local.deployments.europe.url,
wildcard_access_url = local.deployments.europe.wildcard_access_url,
node_pool = google_container_node_pool.node_pool["europe_coder"].name,
release_name = local.coder_release_name,
experiments = var.coder_experiments,
image_repo = var.coder_image_repo,
image_tag = var.coder_image_tag,
replicas = local.scenarios[var.scenario].coder.replicas,
cpu_request = local.scenarios[var.scenario].coder.cpu_request,
mem_request = local.scenarios[var.scenario].coder.mem_request,
cpu_limit = local.scenarios[var.scenario].coder.cpu_limit,
mem_limit = local.scenarios[var.scenario].coder.mem_limit,
deployment = "europe",
tls_secret_name = kubernetes_secret.coder_tls_europe.metadata.0.name,
})]
depends_on = [null_resource.license]
}
resource "helm_release" "provisionerd_europe" {
provider = helm.europe
repository = local.coder_helm_repo
chart = local.provisionerd_helm_chart
name = local.provisionerd_release_name
version = var.provisionerd_chart_version
namespace = kubernetes_namespace.coder_europe.metadata.0.name
values = [templatefile("${path.module}/coder_helm_values.tftpl", {
workspace_proxy = false,
provisionerd = true,
primary_url = null,
proxy_token = null,
db_secret = null,
ip_address = null,
provisionerd_psk = kubernetes_secret.provisionerd_psk_europe.metadata.0.name,
access_url = local.deployments.primary.url,
wildcard_access_url = null,
node_pool = google_container_node_pool.node_pool["europe_coder"].name,
release_name = local.coder_release_name,
experiments = var.coder_experiments,
image_repo = var.coder_image_repo,
image_tag = var.coder_image_tag,
replicas = local.scenarios[var.scenario].provisionerd.replicas,
cpu_request = local.scenarios[var.scenario].provisionerd.cpu_request,
mem_request = local.scenarios[var.scenario].provisionerd.mem_request,
cpu_limit = local.scenarios[var.scenario].provisionerd.cpu_limit,
mem_limit = local.scenarios[var.scenario].provisionerd.mem_limit,
deployment = "europe",
tls_secret_name = null,
})]
depends_on = [null_resource.license]
}
@@ -1,160 +0,0 @@
data "google_client_config" "default" {}
locals {
coder_admin_email = "admin@coder.com"
coder_admin_full_name = "Coder Admin"
coder_admin_user = "coder"
coder_admin_password = random_password.coder_admin_password.result
coder_helm_repo = "https://helm.coder.com/v2"
coder_helm_chart = "coder"
coder_namespace = "coder"
coder_release_name = "${var.name}-coder"
provisionerd_helm_chart = "coder-provisioner"
provisionerd_release_name = "${var.name}-provisionerd"
}
resource "random_password" "provisionerd_psk" {
length = 26
}
resource "random_password" "coder_admin_password" {
length = 16
special = true
}
resource "kubernetes_namespace" "coder_primary" {
provider = kubernetes.primary
metadata {
name = local.coder_namespace
}
lifecycle {
ignore_changes = [timeouts, wait_for_default_service_account]
}
depends_on = [google_container_node_pool.node_pool["primary_misc"]]
}
resource "kubernetes_secret" "coder_db" {
provider = kubernetes.primary
type = "Opaque"
metadata {
name = "coder-db-url"
namespace = kubernetes_namespace.coder_primary.metadata.0.name
}
data = {
url = local.coder_db_url
}
lifecycle {
ignore_changes = [timeouts, wait_for_service_account_token]
}
}
resource "kubernetes_secret" "provisionerd_psk_primary" {
provider = kubernetes.primary
type = "Opaque"
metadata {
name = "coder-provisioner-psk"
namespace = kubernetes_namespace.coder_primary.metadata.0.name
}
data = {
psk = random_password.provisionerd_psk.result
}
lifecycle {
ignore_changes = [timeouts, wait_for_service_account_token]
}
}
resource "kubernetes_secret" "coder_tls_primary" {
provider = kubernetes.primary
type = "kubernetes.io/tls"
metadata {
name = "coder-tls"
namespace = kubernetes_namespace.coder_primary.metadata.0.name
}
data = {
"tls.crt" = data.kubernetes_secret.coder_tls["primary"].data["tls.crt"]
"tls.key" = data.kubernetes_secret.coder_tls["primary"].data["tls.key"]
}
lifecycle {
ignore_changes = [timeouts, wait_for_service_account_token]
}
}
resource "helm_release" "coder_primary" {
provider = helm.primary
repository = local.coder_helm_repo
chart = local.coder_helm_chart
name = local.coder_release_name
version = var.coder_chart_version
namespace = kubernetes_namespace.coder_primary.metadata.0.name
values = [templatefile("${path.module}/coder_helm_values.tftpl", {
workspace_proxy = false,
provisionerd = false,
primary_url = null,
proxy_token = null,
db_secret = kubernetes_secret.coder_db.metadata.0.name,
ip_address = google_compute_address.coder["primary"].address,
provisionerd_psk = kubernetes_secret.provisionerd_psk_primary.metadata.0.name,
access_url = local.deployments.primary.url,
wildcard_access_url = local.deployments.primary.wildcard_access_url,
node_pool = google_container_node_pool.node_pool["primary_coder"].name,
release_name = local.coder_release_name,
experiments = var.coder_experiments,
image_repo = var.coder_image_repo,
image_tag = var.coder_image_tag,
replicas = local.scenarios[var.scenario].coder.replicas,
cpu_request = local.scenarios[var.scenario].coder.cpu_request,
mem_request = local.scenarios[var.scenario].coder.mem_request,
cpu_limit = local.scenarios[var.scenario].coder.cpu_limit,
mem_limit = local.scenarios[var.scenario].coder.mem_limit,
deployment = "primary",
tls_secret_name = kubernetes_secret.coder_tls_primary.metadata.0.name,
})]
}
resource "helm_release" "provisionerd_primary" {
provider = helm.primary
repository = local.coder_helm_repo
chart = local.provisionerd_helm_chart
name = local.provisionerd_release_name
version = var.provisionerd_chart_version
namespace = kubernetes_namespace.coder_primary.metadata.0.name
values = [templatefile("${path.module}/coder_helm_values.tftpl", {
workspace_proxy = false,
provisionerd = true,
primary_url = null,
proxy_token = null,
db_secret = null,
ip_address = null,
provisionerd_psk = kubernetes_secret.provisionerd_psk_primary.metadata.0.name,
access_url = local.deployments.primary.url,
wildcard_access_url = null,
node_pool = google_container_node_pool.node_pool["primary_coder"].name,
release_name = local.coder_release_name,
experiments = var.coder_experiments,
image_repo = var.coder_image_repo,
image_tag = var.coder_image_tag,
replicas = local.scenarios[var.scenario].provisionerd.replicas,
cpu_request = local.scenarios[var.scenario].provisionerd.cpu_request,
mem_request = local.scenarios[var.scenario].provisionerd.mem_request,
cpu_limit = local.scenarios[var.scenario].provisionerd.cpu_limit,
mem_limit = local.scenarios[var.scenario].provisionerd.mem_limit,
deployment = "primary",
tls_secret_name = null,
})]
depends_on = [null_resource.license]
}
output "coder_admin_password" {
description = "Randomly generated Coder admin password"
value = random_password.coder_admin_password.result
# Deliberately not sensitive, so it appears in terraform apply logs
}
@@ -1,17 +0,0 @@
apiVersion: v1
kind: Config
current-context: ${name}
clusters:
- name: ${name}
cluster:
certificate-authority-data: ${cluster_ca_certificate}
server: ${endpoint}
contexts:
- context:
cluster: ${name}
user: ${name}
name: ${name}
users:
- name: ${name}
user:
token: ${access_token}
-141
View File
@@ -1,141 +0,0 @@
terraform {
required_providers {
google = {
source = "hashicorp/google"
version = "~> 4.36"
}
random = {
source = "hashicorp/random"
version = "~> 3.5"
}
kubernetes = {
source = "hashicorp/kubernetes"
version = "~> 2.20"
}
// We use the kubectl provider to apply Custom Resources.
// The kubernetes provider requires the CRD is already present
// and would require a separate apply step beforehand.
// https://github.com/hashicorp/terraform-provider-kubernetes/issues/1367
kubectl = {
source = "alekc/kubectl"
version = ">= 2.0.0"
}
helm = {
source = "hashicorp/helm"
version = "~> 2.9"
}
tls = {
source = "hashicorp/tls"
version = "~> 4.0"
}
cloudflare = {
source = "cloudflare/cloudflare"
version = "~> 4.0"
}
}
required_version = ">= 1.9.0"
}
provider "google" {
}
data "google_secret_manager_secret_version_access" "cloudflare_api_token_dns" {
secret = "cloudflare-api-token-dns"
project = var.project_id
}
provider "cloudflare" {
api_token = coalesce(var.cloudflare_api_token, data.google_secret_manager_secret_version_access.cloudflare_api_token_dns.secret_data)
}
data "google_container_cluster" "observability" {
name = var.observability_cluster_name
location = var.observability_cluster_location
project = var.project_id
}
provider "kubernetes" {
alias = "primary"
host = "https://${google_container_cluster.cluster["primary"].endpoint}"
cluster_ca_certificate = base64decode(google_container_cluster.cluster["primary"].master_auth.0.cluster_ca_certificate)
token = data.google_client_config.default.access_token
}
provider "kubernetes" {
alias = "europe"
host = "https://${google_container_cluster.cluster["europe"].endpoint}"
cluster_ca_certificate = base64decode(google_container_cluster.cluster["europe"].master_auth.0.cluster_ca_certificate)
token = data.google_client_config.default.access_token
}
provider "kubernetes" {
alias = "asia"
host = "https://${google_container_cluster.cluster["asia"].endpoint}"
cluster_ca_certificate = base64decode(google_container_cluster.cluster["asia"].master_auth.0.cluster_ca_certificate)
token = data.google_client_config.default.access_token
}
provider "kubernetes" {
alias = "observability"
host = "https://${data.google_container_cluster.observability.endpoint}"
cluster_ca_certificate = base64decode(data.google_container_cluster.observability.master_auth.0.cluster_ca_certificate)
token = data.google_client_config.default.access_token
}
provider "kubectl" {
alias = "primary"
host = "https://${google_container_cluster.cluster["primary"].endpoint}"
cluster_ca_certificate = base64decode(google_container_cluster.cluster["primary"].master_auth.0.cluster_ca_certificate)
token = data.google_client_config.default.access_token
load_config_file = false
}
provider "kubectl" {
alias = "europe"
host = "https://${google_container_cluster.cluster["europe"].endpoint}"
cluster_ca_certificate = base64decode(google_container_cluster.cluster["europe"].master_auth.0.cluster_ca_certificate)
token = data.google_client_config.default.access_token
load_config_file = false
}
provider "kubectl" {
alias = "asia"
host = "https://${google_container_cluster.cluster["asia"].endpoint}"
cluster_ca_certificate = base64decode(google_container_cluster.cluster["asia"].master_auth.0.cluster_ca_certificate)
token = data.google_client_config.default.access_token
load_config_file = false
}
provider "helm" {
alias = "primary"
kubernetes {
host = "https://${google_container_cluster.cluster["primary"].endpoint}"
cluster_ca_certificate = base64decode(google_container_cluster.cluster["primary"].master_auth.0.cluster_ca_certificate)
token = data.google_client_config.default.access_token
}
}
provider "helm" {
alias = "europe"
kubernetes {
host = "https://${google_container_cluster.cluster["europe"].endpoint}"
cluster_ca_certificate = base64decode(google_container_cluster.cluster["europe"].master_auth.0.cluster_ca_certificate)
token = data.google_client_config.default.access_token
}
}
provider "helm" {
alias = "asia"
kubernetes {
host = "https://${google_container_cluster.cluster["asia"].endpoint}"
cluster_ca_certificate = base64decode(google_container_cluster.cluster["asia"].master_auth.0.cluster_ca_certificate)
token = data.google_client_config.default.access_token
}
}
-174
View File
@@ -1,174 +0,0 @@
locals {
prometheus_helm_repo = "https://prometheus-community.github.io/helm-charts"
prometheus_helm_chart = "kube-prometheus-stack"
prometheus_release_name = "prometheus"
prometheus_remote_write_send_interval = "15s"
prometheus_remote_write_metrics_regex = ".*"
prometheus_postgres_exporter_helm_repo = "https://prometheus-community.github.io/helm-charts"
prometheus_postgres_exporter_helm_chart = "prometheus-postgres-exporter"
prometheus_postgres_exporter_release_name = "prometheus-postgres-exporter"
}
resource "helm_release" "prometheus_chart_primary" {
provider = helm.primary
repository = local.prometheus_helm_repo
chart = local.prometheus_helm_chart
name = local.prometheus_release_name
namespace = kubernetes_namespace.coder_primary.metadata.0.name
values = [templatefile("${path.module}/prometheus_helm_values.tftpl", {
deployment_name = var.name,
nodepool = google_container_node_pool.node_pool["primary_misc"].name,
cluster = "primary",
prometheus_remote_write_url = var.prometheus_remote_write_url,
prometheus_remote_write_metrics_regex = local.prometheus_remote_write_metrics_regex,
prometheus_remote_write_send_interval = local.prometheus_remote_write_send_interval,
})]
}
resource "kubectl_manifest" "pod_monitor_primary" {
provider = kubectl.primary
yaml_body = <<YAML
apiVersion: monitoring.coreos.com/v1
kind: PodMonitor
metadata:
namespace: ${kubernetes_namespace.coder_primary.metadata.0.name}
name: coder-monitoring
spec:
selector:
matchLabels:
"app.kubernetes.io/name": coder
podMetricsEndpoints:
- port: prometheus-http
interval: 30s
YAML
depends_on = [helm_release.prometheus_chart_primary]
}
resource "kubernetes_secret" "prometheus_postgres_password" {
provider = kubernetes.primary
type = "kubernetes.io/basic-auth"
metadata {
name = "prometheus-postgres"
namespace = kubernetes_namespace.coder_primary.metadata.0.name
}
data = {
username = "${var.name}-prometheus"
password = random_password.prometheus_postgres_password.result
}
lifecycle {
ignore_changes = [timeouts, wait_for_service_account_token]
}
}
resource "helm_release" "prometheus_postgres_exporter" {
provider = helm.primary
repository = local.prometheus_postgres_exporter_helm_repo
chart = local.prometheus_postgres_exporter_helm_chart
name = local.prometheus_postgres_exporter_release_name
namespace = kubernetes_namespace.coder_primary.metadata.0.name
values = [<<EOF
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: "cloud.google.com/gke-nodepool"
operator: "In"
values: ["${google_container_node_pool.node_pool["primary_misc"].name}"]
config:
datasource:
host: "${google_sql_database_instance.db.private_ip_address}"
user: "${var.name}-prometheus"
database: "${var.name}-coder"
passwordSecret:
name: "${kubernetes_secret.prometheus_postgres_password.metadata.0.name}"
key: password
autoDiscoverDatabases: true
serviceMonitor:
enabled: true
EOF
]
depends_on = [helm_release.prometheus_chart_primary]
}
resource "helm_release" "prometheus_chart_europe" {
provider = helm.europe
repository = local.prometheus_helm_repo
chart = local.prometheus_helm_chart
name = local.prometheus_release_name
namespace = kubernetes_namespace.coder_europe.metadata.0.name
values = [templatefile("${path.module}/prometheus_helm_values.tftpl", {
deployment_name = var.name,
nodepool = google_container_node_pool.node_pool["europe_misc"].name,
cluster = "europe",
prometheus_remote_write_url = var.prometheus_remote_write_url,
prometheus_remote_write_metrics_regex = local.prometheus_remote_write_metrics_regex,
prometheus_remote_write_send_interval = local.prometheus_remote_write_send_interval,
})]
}
resource "kubectl_manifest" "pod_monitor_europe" {
provider = kubectl.europe
yaml_body = <<YAML
apiVersion: monitoring.coreos.com/v1
kind: PodMonitor
metadata:
namespace: ${kubernetes_namespace.coder_europe.metadata.0.name}
name: coder-monitoring
spec:
selector:
matchLabels:
"app.kubernetes.io/name": coder
podMetricsEndpoints:
- port: prometheus-http
interval: 30s
YAML
depends_on = [helm_release.prometheus_chart_europe]
}
resource "helm_release" "prometheus_chart_asia" {
provider = helm.asia
repository = local.prometheus_helm_repo
chart = local.prometheus_helm_chart
name = local.prometheus_release_name
namespace = kubernetes_namespace.coder_asia.metadata.0.name
values = [templatefile("${path.module}/prometheus_helm_values.tftpl", {
deployment_name = var.name,
nodepool = google_container_node_pool.node_pool["asia_misc"].name,
cluster = "asia",
prometheus_remote_write_url = var.prometheus_remote_write_url,
prometheus_remote_write_metrics_regex = local.prometheus_remote_write_metrics_regex,
prometheus_remote_write_send_interval = local.prometheus_remote_write_send_interval,
})]
}
resource "kubectl_manifest" "pod_monitor_asia" {
provider = kubectl.asia
yaml_body = <<YAML
apiVersion: monitoring.coreos.com/v1
kind: PodMonitor
metadata:
namespace: ${kubernetes_namespace.coder_asia.metadata.0.name}
name: coder-monitoring
spec:
selector:
matchLabels:
"app.kubernetes.io/name": coder
podMetricsEndpoints:
- port: prometheus-http
interval: 30s
YAML
depends_on = [helm_release.prometheus_chart_asia]
}
@@ -1,38 +0,0 @@
alertmanager:
enabled: false
grafana:
enabled: false
prometheusOperator:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: "cloud.google.com/gke-nodepool"
operator: "In"
values: ["${nodepool}"]
prometheus:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: "cloud.google.com/gke-nodepool"
operator: "In"
values: ["${nodepool}"]
prometheusSpec:
externalLabels:
deployment_name: "${deployment_name}"
cluster: "${cluster}"
podMonitorSelectorNilUsesHelmValues: false
serviceMonitorSelectorNilUsesHelmValues: false
remoteWrite:
- url: "${prometheus_remote_write_url}"
tlsConfig:
insecureSkipVerify: true
writeRelabelConfigs:
- sourceLabels: [__name__]
regex: "${prometheus_remote_write_metrics_regex}"
action: keep
metadataConfig:
sendInterval: "${prometheus_remote_write_send_interval}"
-74
View File
@@ -1,74 +0,0 @@
locals {
scenarios = {
large = {
coder = {
nodepool_size = 3
machine_type = "c2d-standard-8"
replicas = 3
cpu_request = "4000m"
mem_request = "12Gi"
cpu_limit = "4000m"
mem_limit = "12Gi"
}
provisionerd = {
replicas = 30
cpu_request = "100m"
mem_request = "256Mi"
cpu_limit = "1000m"
mem_limit = "1Gi"
}
workspaces = {
count_per_deployment = 100
nodepool_size = 3
machine_type = "c2d-standard-32"
cpu_request = "100m"
mem_request = "128Mi"
cpu_limit = "100m"
mem_limit = "128Mi"
}
misc = {
nodepool_size = 1
machine_type = "c2d-standard-32"
}
cloudsql = {
tier = "db-custom-2-7680"
max_connections = 500
}
}
small = {
coder = {
nodepool_size = 3
machine_type = "c2d-standard-8"
replicas = 3
cpu_request = "4000m"
mem_request = "12Gi"
cpu_limit = "4000m"
mem_limit = "12Gi"
}
provisionerd = {
replicas = 5
cpu_request = "100m"
mem_request = "256Mi"
cpu_limit = "1000m"
mem_limit = "1Gi"
}
workspaces = {
count_per_deployment = 10
nodepool_size = 3
machine_type = "c2d-standard-8"
cpu_request = "100m"
mem_request = "128Mi"
cpu_limit = "100m"
mem_limit = "128Mi"
}
misc = {
nodepool_size = 1
machine_type = "c2d-standard-8"
}
cloudsql = {
tier = "db-custom-2-7680"
max_connections = 100
}
}
}
}
-13
View File
@@ -1,13 +0,0 @@
locals {
coder_certs_namespace = "coder-certs"
}
# These certificates are managed by flux and cert-manager.
data "kubernetes_secret" "coder_tls" {
for_each = local.deployments
provider = kubernetes.observability
metadata {
name = "coder-${var.name}-${each.key}-tls"
namespace = local.coder_certs_namespace
}
}
-112
View File
@@ -1,112 +0,0 @@
variable "name" {
description = "The name all resources will be prefixed with. Must be one of alpha, bravo, or charlie."
validation {
condition = contains(["alpha", "bravo", "charlie"], var.name)
error_message = "Name must be one of alpha, bravo, or charlie."
}
}
variable "scenario" {
description = "The scenario to deploy"
validation {
condition = contains(["small", "medium", "large"], var.scenario)
error_message = "Scenario must be one of small, medium, or large"
}
}
// GCP
variable "project_id" {
description = "The project in which to provision resources"
default = "coder-scaletest"
}
variable "k8s_version" {
description = "Kubernetes version to provision."
default = "1.24"
}
// Cloudflare
variable "cloudflare_api_token" {
description = "Cloudflare API token."
sensitive = true
# only override if you want to change the cloudflare_domain; pulls the token for scaletest.dev from Google Secrets
# Manager if null.
default = null
}
variable "cloudflare_domain" {
description = "Cloudflare coder domain."
default = "scaletest.dev"
}
// Coder
variable "coder_license" {
description = "Coder license key."
sensitive = true
}
variable "coder_chart_version" {
description = "Version of the Coder Helm chart to install. Defaults to latest."
default = null
}
variable "coder_image_tag" {
description = "Tag to use for Coder image."
default = "latest"
}
variable "coder_image_repo" {
description = "Repository to use for Coder image."
default = "ghcr.io/coder/coder"
}
variable "coder_experiments" {
description = "Coder Experiments to enable."
default = ""
}
// Workspaces
variable "workspace_image" {
description = "Image and tag to use for workspaces."
default = "docker.io/codercom/enterprise-minimal:ubuntu"
}
variable "provisionerd_chart_version" {
description = "Version of the Provisionerd Helm chart to install. Defaults to latest."
default = null
}
variable "provisionerd_image_repo" {
description = "Repository to use for Provisionerd image."
default = "ghcr.io/coder/coder"
}
variable "provisionerd_image_tag" {
description = "Tag to use for Provisionerd image."
default = "latest"
}
variable "observability_cluster_name" {
description = "Name of the observability GKE cluster."
default = "observability"
}
variable "observability_cluster_location" {
description = "Location of the observability GKE cluster."
default = "us-east1-b"
}
variable "observability_cluster_vpc" {
description = "Name of the observability cluster VPC network to peer with."
default = "default"
}
variable "cloudflare_api_token_secret" {
description = "Name of the Google Secret Manager secret containing the Cloudflare API token."
default = "cloudflare-api-token-dns"
}
// Prometheus
variable "prometheus_remote_write_url" {
description = "URL to push prometheus metrics to."
}
-186
View File
@@ -1,186 +0,0 @@
data "google_compute_default_service_account" "default" {
project = var.project_id
}
locals {
abs_module_path = abspath(path.module)
rel_kubeconfig_path = "../../.coderv2/${var.name}-cluster.kubeconfig"
cluster_kubeconfig_path = abspath("${local.abs_module_path}/${local.rel_kubeconfig_path}")
}
resource "google_container_cluster" "primary" {
name = var.name
location = var.zone
project = var.project_id
network = google_compute_network.vpc.name
subnetwork = google_compute_subnetwork.subnet.name
networking_mode = "VPC_NATIVE"
default_max_pods_per_node = 256
ip_allocation_policy { # Required with networking_mode=VPC_NATIVE
}
release_channel {
# Setting release channel as STABLE can cause unexpected cluster upgrades.
channel = "UNSPECIFIED"
}
initial_node_count = 1
remove_default_node_pool = true
network_policy {
enabled = true
}
depends_on = [
google_project_service.api["container.googleapis.com"]
]
monitoring_config {
enable_components = ["SYSTEM_COMPONENTS"]
managed_prometheus {
enabled = false
}
}
workload_identity_config {
workload_pool = "${data.google_project.project.project_id}.svc.id.goog"
}
lifecycle {
ignore_changes = [
maintenance_policy,
release_channel,
remove_default_node_pool
]
}
}
resource "google_container_node_pool" "coder" {
name = "${var.name}-coder"
location = var.zone
project = var.project_id
cluster = google_container_cluster.primary.name
autoscaling {
min_node_count = 1
max_node_count = var.nodepool_size_coder
}
node_config {
oauth_scopes = [
"https://www.googleapis.com/auth/logging.write",
"https://www.googleapis.com/auth/monitoring",
"https://www.googleapis.com/auth/trace.append",
"https://www.googleapis.com/auth/devstorage.read_only",
"https://www.googleapis.com/auth/service.management.readonly",
"https://www.googleapis.com/auth/servicecontrol",
]
disk_size_gb = var.node_disk_size_gb
machine_type = var.nodepool_machine_type_coder
image_type = var.node_image_type
preemptible = var.node_preemptible
service_account = data.google_compute_default_service_account.default.email
tags = ["gke-node", "${var.project_id}-gke"]
labels = {
env = var.project_id
}
metadata = {
disable-legacy-endpoints = "true"
}
}
lifecycle {
ignore_changes = [management[0].auto_repair, management[0].auto_upgrade, timeouts]
}
}
resource "google_container_node_pool" "workspaces" {
name = "${var.name}-workspaces"
location = var.zone
project = var.project_id
cluster = google_container_cluster.primary.name
autoscaling {
min_node_count = 0
total_max_node_count = var.nodepool_size_workspaces
}
management {
auto_upgrade = false
}
node_config {
oauth_scopes = [
"https://www.googleapis.com/auth/logging.write",
"https://www.googleapis.com/auth/monitoring",
"https://www.googleapis.com/auth/trace.append",
"https://www.googleapis.com/auth/devstorage.read_only",
"https://www.googleapis.com/auth/service.management.readonly",
"https://www.googleapis.com/auth/servicecontrol",
]
disk_size_gb = var.node_disk_size_gb
machine_type = var.nodepool_machine_type_workspaces
image_type = var.node_image_type
preemptible = var.node_preemptible
service_account = data.google_compute_default_service_account.default.email
tags = ["gke-node", "${var.project_id}-gke"]
labels = {
env = var.project_id
}
metadata = {
disable-legacy-endpoints = "true"
}
}
lifecycle {
ignore_changes = [management[0].auto_repair, management[0].auto_upgrade, timeouts]
}
}
resource "google_container_node_pool" "misc" {
name = "${var.name}-misc"
location = var.zone
project = var.project_id
cluster = google_container_cluster.primary.name
node_count = var.state == "stopped" ? 0 : var.nodepool_size_misc
management {
auto_upgrade = false
}
node_config {
oauth_scopes = [
"https://www.googleapis.com/auth/logging.write",
"https://www.googleapis.com/auth/monitoring",
"https://www.googleapis.com/auth/trace.append",
"https://www.googleapis.com/auth/devstorage.read_only",
"https://www.googleapis.com/auth/service.management.readonly",
"https://www.googleapis.com/auth/servicecontrol",
]
disk_size_gb = var.node_disk_size_gb
machine_type = var.nodepool_machine_type_misc
image_type = var.node_image_type
preemptible = var.node_preemptible
service_account = data.google_compute_default_service_account.default.email
tags = ["gke-node", "${var.project_id}-gke"]
labels = {
env = var.project_id
}
metadata = {
disable-legacy-endpoints = "true"
}
}
lifecycle {
ignore_changes = [management[0].auto_repair, management[0].auto_upgrade, timeouts]
}
}
resource "null_resource" "cluster_kubeconfig" {
depends_on = [google_container_cluster.primary]
triggers = {
path = local.cluster_kubeconfig_path
name = google_container_cluster.primary.name
project_id = var.project_id
zone = var.zone
}
provisioner "local-exec" {
command = <<EOF
KUBECONFIG=${self.triggers.path} gcloud container clusters get-credentials ${self.triggers.name} --project=${self.triggers.project_id} --zone=${self.triggers.zone}
EOF
}
provisioner "local-exec" {
when = destroy
command = <<EOF
rm -f ${self.triggers.path}
EOF
}
}
-88
View File
@@ -1,88 +0,0 @@
resource "google_sql_database_instance" "db" {
name = var.name
region = var.region
database_version = var.cloudsql_version
deletion_protection = false
depends_on = [google_service_networking_connection.private_vpc_connection]
settings {
tier = var.cloudsql_tier
activation_policy = "ALWAYS"
availability_type = "ZONAL"
location_preference {
zone = var.zone
}
database_flags {
name = "max_connections"
value = var.cloudsql_max_connections
}
ip_configuration {
ipv4_enabled = false
private_network = google_compute_network.vpc.id
}
insights_config {
query_insights_enabled = true
query_string_length = 1024
record_application_tags = false
record_client_address = false
}
}
lifecycle {
ignore_changes = [deletion_protection, timeouts]
}
}
resource "google_sql_database" "coder" {
project = var.project_id
instance = google_sql_database_instance.db.id
name = "${var.name}-coder"
# required for postgres, otherwise db fails to delete
deletion_policy = "ABANDON"
lifecycle {
ignore_changes = [deletion_policy]
}
}
resource "random_password" "coder-postgres-password" {
length = 12
}
resource "random_password" "prometheus-postgres-password" {
length = 12
}
resource "google_sql_user" "coder" {
project = var.project_id
instance = google_sql_database_instance.db.id
name = "${var.name}-coder"
type = "BUILT_IN"
password = random_password.coder-postgres-password.result
# required for postgres, otherwise user fails to delete
deletion_policy = "ABANDON"
lifecycle {
ignore_changes = [deletion_policy, password]
}
}
resource "google_sql_user" "prometheus" {
project = var.project_id
instance = google_sql_database_instance.db.id
name = "${var.name}-prometheus"
type = "BUILT_IN"
password = random_password.prometheus-postgres-password.result
# required for postgres, otherwise user fails to delete
deletion_policy = "ABANDON"
lifecycle {
ignore_changes = [deletion_policy, password]
}
}
locals {
coder_db_url = "postgres://${google_sql_user.coder.name}:${urlencode(random_password.coder-postgres-password.result)}@${google_sql_database_instance.db.private_ip_address}/${google_sql_database.coder.name}?sslmode=disable"
}
-27
View File
@@ -1,27 +0,0 @@
locals {
project_apis = [
"cloudtrace",
"compute",
"container",
"logging",
"monitoring",
"servicemanagement",
"servicenetworking",
"sqladmin",
"stackdriver",
"storage-api",
]
}
data "google_project" "project" {
project_id = var.project_id
}
resource "google_project_service" "api" {
for_each = toset(local.project_apis)
project = data.google_project.project.project_id
service = "${each.value}.googleapis.com"
disable_dependent_services = false
disable_on_destroy = false
}
-39
View File
@@ -1,39 +0,0 @@
resource "google_compute_network" "vpc" {
project = var.project_id
name = var.name
auto_create_subnetworks = "false"
depends_on = [
google_project_service.api["compute.googleapis.com"]
]
}
resource "google_compute_subnetwork" "subnet" {
name = var.name
project = var.project_id
region = var.region
network = google_compute_network.vpc.name
ip_cidr_range = var.subnet_cidr
}
resource "google_compute_global_address" "sql_peering" {
project = var.project_id
name = "${var.name}-sql-peering"
purpose = "VPC_PEERING"
address_type = "INTERNAL"
prefix_length = 16
network = google_compute_network.vpc.id
}
resource "google_compute_address" "coder" {
project = var.project_id
region = var.region
name = "${var.name}-coder"
address_type = "EXTERNAL"
network_tier = "PREMIUM"
}
resource "google_service_networking_connection" "private_vpc_connection" {
network = google_compute_network.vpc.id
service = "servicenetworking.googleapis.com"
reserved_peering_ranges = [google_compute_global_address.sql_peering.name]
}
-20
View File
@@ -1,20 +0,0 @@
terraform {
required_providers {
google = {
source = "hashicorp/google"
version = "~> 4.36"
}
random = {
source = "hashicorp/random"
version = "~> 3.5"
}
}
required_version = "~> 1.5.0"
}
provider "google" {
region = var.region
project = var.project_id
}
-73
View File
@@ -1,73 +0,0 @@
output "coder_db_url" {
description = "URL of the database for Coder."
value = local.coder_db_url
sensitive = true
}
output "coder_address" {
description = "IP address to use for the Coder service."
value = google_compute_address.coder.address
}
output "kubernetes_kubeconfig_path" {
description = "Kubeconfig path."
value = local.cluster_kubeconfig_path
}
output "kubernetes_nodepool_coder" {
description = "Name of the nodepool on which to run Coder."
value = google_container_node_pool.coder.name
}
output "kubernetes_nodepool_misc" {
description = "Name of the nodepool on which to run everything else."
value = google_container_node_pool.misc.name
}
output "kubernetes_nodepool_workspaces" {
description = "Name of the nodepool on which to run workspaces."
value = google_container_node_pool.workspaces.name
}
output "prometheus_external_label_cluster" {
description = "Value for the Prometheus external label named cluster."
value = google_container_cluster.primary.name
}
output "prometheus_postgres_dbname" {
description = "Name of the database for Prometheus to monitor."
value = google_sql_database.coder.name
}
output "prometheus_postgres_host" {
description = "Hostname of the database for Prometheus to connect to."
value = google_sql_database_instance.db.private_ip_address
}
output "prometheus_postgres_password" {
description = "Postgres password for Prometheus."
value = random_password.prometheus-postgres-password.result
sensitive = true
}
output "prometheus_postgres_user" {
description = "Postgres username for Prometheus."
value = google_sql_user.prometheus.name
}
resource "local_file" "outputs" {
filename = "${path.module}/../../.coderv2/infra_outputs.tfvars"
content = <<EOF
coder_db_url = "${local.coder_db_url}"
coder_address = "${google_compute_address.coder.address}"
kubernetes_kubeconfig_path = "${local.cluster_kubeconfig_path}"
kubernetes_nodepool_coder = "${google_container_node_pool.coder.name}"
kubernetes_nodepool_misc = "${google_container_node_pool.misc.name}"
kubernetes_nodepool_workspaces = "${google_container_node_pool.workspaces.name}"
prometheus_external_label_cluster = "${google_container_cluster.primary.name}"
prometheus_postgres_dbname = "${google_sql_database.coder.name}"
prometheus_postgres_host = "${google_sql_database_instance.db.private_ip_address}"
prometheus_postgres_password = "${random_password.prometheus-postgres-password.result}"
prometheus_postgres_user = "${google_sql_user.prometheus.name}"
EOF
}
-107
View File
@@ -1,107 +0,0 @@
variable "state" {
description = "The state of the cluster. Valid values are 'started', and 'stopped'."
validation {
condition = contains(["started", "stopped"], var.state)
error_message = "value must be one of 'started' or 'stopped'"
}
default = "started"
}
variable "project_id" {
description = "The project in which to provision resources"
}
variable "name" {
description = "Adds a prefix to resources."
}
variable "region" {
description = "GCP region in which to provision resources."
default = "us-east1"
}
variable "zone" {
description = "GCP zone in which to provision resources."
default = "us-east1-c"
}
variable "subnet_cidr" {
description = "CIDR range for the subnet."
default = "10.200.0.0/24"
}
variable "k8s_version" {
description = "Kubernetes version to provision."
default = "1.24"
}
variable "node_disk_size_gb" {
description = "Size of the root disk for cluster nodes."
default = 100
}
variable "node_image_type" {
description = "Image type to use for cluster nodes."
default = "cos_containerd"
}
// Preemptible nodes are way cheaper, but can be pulled out
// from under you at any time. Caveat emptor.
variable "node_preemptible" {
description = "Use preemptible nodes."
default = false
}
// We create three nodepools:
// - One for the Coder control plane
// - One for workspaces
// - One for everything else (for example, load generation)
// These variables control the node pool dedicated to Coder.
variable "nodepool_machine_type_coder" {
description = "Machine type to use for Coder control plane nodepool."
default = "t2d-standard-4"
}
variable "nodepool_size_coder" {
description = "Number of cluster nodes for the Coder control plane nodepool."
default = 1
}
// These variables control the node pool dedicated to workspaces.
variable "nodepool_machine_type_workspaces" {
description = "Machine type to use for the workspaces nodepool."
default = "t2d-standard-4"
}
variable "nodepool_size_workspaces" {
description = "Number of cluster nodes for the workspaces nodepool."
default = 1
}
// These variables control the node pool for everything else.
variable "nodepool_machine_type_misc" {
description = "Machine type to use for the misc nodepool."
default = "t2d-standard-4"
}
variable "nodepool_size_misc" {
description = "Number of cluster nodes for the misc nodepool."
default = 1
}
// These variables control the size of the database to be used by Coder.
variable "cloudsql_version" {
description = "CloudSQL version to provision"
default = "POSTGRES_14"
}
variable "cloudsql_tier" {
description = "CloudSQL database tier."
default = "db-f1-micro"
}
variable "cloudsql_max_connections" {
description = "CloudSQL database max_connections"
default = 500
}
-67
View File
@@ -1,67 +0,0 @@
# Terraform configuration for cert-manaer
locals {
cert_manager_namespace = "cert-manager"
cert_manager_helm_repo = "https://charts.jetstack.io"
cert_manager_helm_chart = "cert-manager"
cert_manager_release_name = "cert-manager"
cert_manager_chart_version = "1.12.2"
cloudflare_issuer_private_key_secret_name = "cloudflare-issuer-private-key"
}
resource "kubernetes_secret" "cloudflare-api-key" {
metadata {
name = "cloudflare-api-key-secret"
namespace = local.cert_manager_namespace
}
data = {
api-token = var.cloudflare_api_token
}
}
resource "kubernetes_namespace" "cert-manager-namespace" {
metadata {
name = local.cert_manager_namespace
}
}
resource "helm_release" "cert-manager" {
repository = local.cert_manager_helm_repo
chart = local.cert_manager_helm_chart
name = local.cert_manager_release_name
namespace = kubernetes_namespace.cert-manager-namespace.metadata.0.name
values = [<<EOF
installCRDs: true
EOF
]
}
resource "kubernetes_manifest" "cloudflare-cluster-issuer" {
manifest = {
apiVersion = "cert-manager.io/v1"
kind = "ClusterIssuer"
metadata = {
name = "cloudflare-issuer"
}
spec = {
acme = {
email = var.cloudflare_email
privateKeySecretRef = {
name = local.cloudflare_issuer_private_key_secret_name
}
solvers = [
{
dns01 = {
cloudflare = {
apiTokenSecretRef = {
name = kubernetes_secret.cloudflare-api-key.metadata.0.name
key = "api-token"
}
}
}
}
]
}
}
}
}
-375
View File
@@ -1,375 +0,0 @@
data "google_client_config" "default" {}
locals {
coder_url = var.coder_access_url
coder_admin_email = "admin@coder.com"
coder_admin_user = "coder"
coder_helm_repo = "https://helm.coder.com/v2"
coder_helm_chart = "coder"
coder_namespace = "coder-${var.name}"
coder_release_name = var.name
provisionerd_helm_chart = "coder-provisioner"
provisionerd_release_name = "${var.name}-provisionerd"
}
resource "kubernetes_namespace" "coder_namespace" {
metadata {
name = local.coder_namespace
}
lifecycle {
ignore_changes = [timeouts, wait_for_default_service_account]
}
}
resource "random_password" "provisionerd_psk" {
length = 26
}
resource "kubernetes_secret" "coder-db" {
type = "Opaque"
metadata {
name = "coder-db-url"
namespace = kubernetes_namespace.coder_namespace.metadata.0.name
}
data = {
url = var.coder_db_url
}
lifecycle {
ignore_changes = [timeouts, wait_for_service_account_token]
}
}
resource "kubernetes_secret" "provisionerd_psk" {
type = "Opaque"
metadata {
name = "coder-provisioner-psk"
namespace = kubernetes_namespace.coder_namespace.metadata.0.name
}
data = {
psk = random_password.provisionerd_psk.result
}
lifecycle {
ignore_changes = [timeouts, wait_for_service_account_token]
}
}
# OIDC secret needs to be manually provisioned for now.
data "kubernetes_secret" "coder_oidc" {
metadata {
namespace = kubernetes_namespace.coder_namespace.metadata.0.name
name = "coder-oidc"
}
}
resource "kubernetes_manifest" "coder_certificate" {
manifest = {
apiVersion = "cert-manager.io/v1"
kind = "Certificate"
metadata = {
name = "${var.name}"
namespace = kubernetes_namespace.coder_namespace.metadata.0.name
}
spec = {
secretName = "${var.name}-tls"
dnsNames = regex("https?://([^/]+)", local.coder_url)
issuerRef = {
name = kubernetes_manifest.cloudflare-cluster-issuer.manifest.metadata.name
kind = "ClusterIssuer"
}
}
}
}
data "kubernetes_secret" "coder_tls" {
metadata {
namespace = kubernetes_namespace.coder_namespace.metadata.0.name
name = "${var.name}-tls"
}
depends_on = [kubernetes_manifest.coder_certificate]
}
resource "helm_release" "coder-chart" {
repository = local.coder_helm_repo
chart = local.coder_helm_chart
name = local.coder_release_name
version = var.coder_chart_version
namespace = kubernetes_namespace.coder_namespace.metadata.0.name
values = [<<EOF
coder:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: "cloud.google.com/gke-nodepool"
operator: "In"
values: ["${var.kubernetes_nodepool_coder}"]
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
podAffinityTerm:
topologyKey: "kubernetes.io/hostname"
labelSelector:
matchExpressions:
- key: "app.kubernetes.io/instance"
operator: "In"
values: ["${local.coder_release_name}"]
env:
- name: "CODER_ACCESS_URL"
value: "${local.coder_url}"
- name: "CODER_CACHE_DIRECTORY"
value: "/tmp/coder"
- name: "CODER_TELEMETRY_ENABLE"
value: "false"
- name: "CODER_LOGGING_HUMAN"
value: "/dev/null"
- name: "CODER_LOGGING_STACKDRIVER"
value: "/dev/stderr"
- name: "CODER_PG_CONNECTION_URL"
valueFrom:
secretKeyRef:
name: "${kubernetes_secret.coder-db.metadata.0.name}"
key: url
- name: "CODER_PPROF_ENABLE"
value: "true"
- name: "CODER_PROMETHEUS_ENABLE"
value: "true"
- name: "CODER_PROMETHEUS_COLLECT_AGENT_STATS"
value: "true"
- name: "CODER_PROMETHEUS_COLLECT_DB_METRICS"
value: "true"
- name: "CODER_VERBOSE"
value: "true"
- name: "CODER_EXPERIMENTS"
value: "${var.coder_experiments}"
- name: "CODER_DANGEROUS_DISABLE_RATE_LIMITS"
value: "true"
# Disabling built-in provisioner daemons
- name: "CODER_PROVISIONER_DAEMONS"
value: "0"
- name: CODER_PROVISIONER_DAEMON_PSK
valueFrom:
secretKeyRef:
key: psk
name: "${kubernetes_secret.provisionerd_psk.metadata.0.name}"
# Enable OIDC
- name: "CODER_OIDC_ISSUER_URL"
valueFrom:
secretKeyRef:
key: issuer-url
name: "${data.kubernetes_secret.coder_oidc.metadata.0.name}"
- name: "CODER_OIDC_EMAIL_DOMAIN"
valueFrom:
secretKeyRef:
key: email-domain
name: "${data.kubernetes_secret.coder_oidc.metadata.0.name}"
- name: "CODER_OIDC_CLIENT_ID"
valueFrom:
secretKeyRef:
key: client-id
name: "${data.kubernetes_secret.coder_oidc.metadata.0.name}"
- name: "CODER_OIDC_CLIENT_SECRET"
valueFrom:
secretKeyRef:
key: client-secret
name: "${data.kubernetes_secret.coder_oidc.metadata.0.name}"
# Send OTEL traces to the cluster-local collector to sample 10%
- name: "OTEL_EXPORTER_OTLP_ENDPOINT"
value: "http://${kubernetes_manifest.otel-collector.manifest.metadata.name}-collector.${kubernetes_namespace.coder_namespace.metadata.0.name}.svc.cluster.local:4317"
- name: "OTEL_TRACES_SAMPLER"
value: parentbased_traceidratio
- name: "OTEL_TRACES_SAMPLER_ARG"
value: "0.1"
image:
repo: ${var.coder_image_repo}
tag: ${var.coder_image_tag}
replicaCount: "${var.coder_replicas}"
resources:
requests:
cpu: "${var.coder_cpu_request}"
memory: "${var.coder_mem_request}"
limits:
cpu: "${var.coder_cpu_limit}"
memory: "${var.coder_mem_limit}"
securityContext:
readOnlyRootFilesystem: true
service:
enable: true
sessionAffinity: None
loadBalancerIP: "${var.coder_address}"
volumeMounts:
- mountPath: "/tmp"
name: cache
readOnly: false
volumes:
- emptyDir:
sizeLimit: 1024Mi
name: cache
EOF
]
}
resource "helm_release" "provisionerd-chart" {
repository = local.coder_helm_repo
chart = local.provisionerd_helm_chart
name = local.provisionerd_release_name
version = var.provisionerd_chart_version
namespace = kubernetes_namespace.coder_namespace.metadata.0.name
values = [<<EOF
coder:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: "cloud.google.com/gke-nodepool"
operator: "In"
values: ["${var.kubernetes_nodepool_coder}"]
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
podAffinityTerm:
topologyKey: "kubernetes.io/hostname"
labelSelector:
matchExpressions:
- key: "app.kubernetes.io/instance"
operator: "In"
values: ["${local.coder_release_name}"]
env:
- name: "CODER_URL"
value: "${local.coder_url}"
- name: "CODER_VERBOSE"
value: "true"
- name: "CODER_CACHE_DIRECTORY"
value: "/tmp/coder"
- name: "CODER_TELEMETRY_ENABLE"
value: "false"
- name: "CODER_LOGGING_HUMAN"
value: "/dev/null"
- name: "CODER_LOGGING_STACKDRIVER"
value: "/dev/stderr"
- name: "CODER_PROMETHEUS_ENABLE"
value: "true"
- name: "CODER_PROVISIONERD_TAGS"
value = "socpe=organization"
image:
repo: ${var.provisionerd_image_repo}
tag: ${var.provisionerd_image_tag}
replicaCount: "${var.provisionerd_replicas}"
resources:
requests:
cpu: "${var.provisionerd_cpu_request}"
memory: "${var.provisionerd_mem_request}"
limits:
cpu: "${var.provisionerd_cpu_limit}"
memory: "${var.provisionerd_mem_limit}"
securityContext:
readOnlyRootFilesystem: true
volumeMounts:
- mountPath: "/tmp"
name: cache
readOnly: false
volumes:
- emptyDir:
sizeLimit: 1024Mi
name: cache
EOF
]
}
resource "local_file" "kubernetes_template" {
filename = "${path.module}/../.coderv2/templates/kubernetes/main.tf"
content = <<EOF
terraform {
required_providers {
coder = {
source = "coder/coder"
version = "~> 0.23.0"
}
kubernetes = {
source = "hashicorp/kubernetes"
version = "~> 2.30"
}
}
}
provider "coder" {}
provider "kubernetes" {
config_path = null # always use host
}
data "coder_workspace" "me" {}
data "coder_workspace_owner" "me" {}
resource "coder_agent" "main" {
os = "linux"
arch = "amd64"
}
resource "kubernetes_pod" "main" {
count = data.coder_workspace.me.start_count
metadata {
name = "coder-$${lower(data.coder_workspace_owner.me.name)}-$${lower(data.coder_workspace.me.name)}"
namespace = "${local.coder_namespace}"
labels = {
"app.kubernetes.io/name" = "coder-workspace"
"app.kubernetes.io/instance" = "coder-workspace-$${lower(data.coder_workspace_owner.me.name)}-$${lower(data.coder_workspace.me.name)}"
}
}
spec {
security_context {
run_as_user = "1000"
fs_group = "1000"
}
container {
name = "dev"
image = "${var.workspace_image}"
image_pull_policy = "Always"
command = ["sh", "-c", coder_agent.main.init_script]
security_context {
run_as_user = "1000"
}
env {
name = "CODER_AGENT_TOKEN"
value = coder_agent.main.token
}
resources {
requests = {
"cpu" = "${var.workspace_cpu_request}"
"memory" = "${var.workspace_mem_request}"
}
limits = {
"cpu" = "${var.workspace_cpu_limit}"
"memory" = "${var.workspace_mem_limit}"
}
}
}
affinity {
node_affinity {
required_during_scheduling_ignored_during_execution {
node_selector_term {
match_expressions {
key = "cloud.google.com/gke-nodepool"
operator = "In"
values = ["${var.kubernetes_nodepool_workspaces}"]
}
}
}
}
}
}
}
EOF
}
resource "local_file" "output_vars" {
filename = "${path.module}/../../.coderv2/url"
content = local.coder_url
}
output "coder_url" {
description = "URL of the Coder deployment"
value = local.coder_url
}
-35
View File
@@ -1,35 +0,0 @@
terraform {
required_providers {
kubernetes = {
source = "hashicorp/kubernetes"
version = "~> 2.20"
}
helm = {
source = "hashicorp/helm"
version = "~> 2.9"
}
random = {
source = "hashicorp/random"
version = "~> 3.5"
}
tls = {
source = "hashicorp/tls"
version = "~> 4.0"
}
}
required_version = "~> 1.5.0"
}
provider "kubernetes" {
config_path = var.kubernetes_kubeconfig_path
}
provider "helm" {
kubernetes {
config_path = var.kubernetes_kubeconfig_path
}
}
-69
View File
@@ -1,69 +0,0 @@
# Terraform configuration for OpenTelemetry Operator
locals {
otel_namespace = "opentelemetry-operator-system"
otel_operator_helm_repo = "https://open-telemetry.github.io/opentelemetry-helm-charts"
otel_operator_helm_chart = "opentelemtry-operator"
otel_operator_release_name = "opentelemetry-operator"
otel_operator_chart_version = "0.34.1"
}
resource "kubernetes_namespace" "otel-namespace" {
metadata {
name = local.otel_namespace
}
lifecycle {
ignore_changes = [timeouts, wait_for_default_service_account]
}
}
resource "helm_release" "otel-operator" {
repository = local.otel_operator_helm_repo
chart = local.otel_operator_helm_chart
name = local.otel_operator_release_name
namespace = kubernetes_namespace.otel-namespace.metadata.0.name
# Default values
values = []
}
resource "kubernetes_manifest" "otel-collector" {
manifest = {
apiVersion = "opentelemetry.io/v1alpha1"
kind = "OpenTelemetryCollector"
metadata = {
namespace = kubernetes_namespace.coder_namespace.metadata.0.name
name = "otel"
}
spec = {
config = jsonencode({
receivers = {
otlp = {
protocols : {
grpc : {}
http : {}
}
}
}
exporters = {
googlecloud = {
logging = {
loglevel = "debug"
}
}
}
service = {
pipelines = {
traces = {
receivers = ["otlp"]
processors = []
exporters = ["logging", "googlecloud"]
}
}
}
image = "otel/open-telemetry-collector-contrib:latest"
mode = "deployment"
replicas = 1
})
}
}
}
-173
View File
@@ -1,173 +0,0 @@
locals {
prometheus_helm_repo = "https://charts.bitnami.com/bitnami"
prometheus_helm_chart = "kube-prometheus"
prometheus_exporter_helm_repo = "https://prometheus-community.github.io/helm-charts"
prometheus_exporter_helm_chart = "prometheus-postgres-exporter"
prometheus_release_name = "prometheus"
prometheus_exporter_release_name = "prometheus-postgres-exporter"
prometheus_namespace = "prometheus"
prometheus_remote_write_enabled = var.prometheus_remote_write_password != ""
}
# Create a namespace to hold our Prometheus deployment.
resource "kubernetes_namespace" "prometheus_namespace" {
metadata {
name = local.prometheus_namespace
}
lifecycle {
ignore_changes = [timeouts, wait_for_default_service_account]
}
}
# Create a secret to store the remote write key
resource "kubernetes_secret" "prometheus-credentials" {
count = local.prometheus_remote_write_enabled ? 1 : 0
type = "kubernetes.io/basic-auth"
metadata {
name = "prometheus-credentials"
namespace = kubernetes_namespace.prometheus_namespace.metadata.0.name
}
data = {
username = var.prometheus_remote_write_user
password = var.prometheus_remote_write_password
}
lifecycle {
ignore_changes = [timeouts, wait_for_service_account_token]
}
}
# Install Prometheus using the Bitnami Prometheus helm chart.
resource "helm_release" "prometheus-chart" {
repository = local.prometheus_helm_repo
chart = local.prometheus_helm_chart
name = local.prometheus_release_name
namespace = kubernetes_namespace.prometheus_namespace.metadata.0.name
values = [<<EOF
alertmanager:
enabled: false
blackboxExporter:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: "cloud.google.com/gke-nodepool"
operator: "In"
values: ["${var.kubernetes_nodepool_misc}"]
operator:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: "cloud.google.com/gke-nodepool"
operator: "In"
values: ["${var.kubernetes_nodepool_misc}"]
prometheus:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: "cloud.google.com/gke-nodepool"
operator: "In"
values: ["${var.kubernetes_nodepool_misc}"]
externalLabels:
cluster: "${var.prometheus_external_label_cluster}"
persistence:
enabled: true
storageClass: standard
%{if local.prometheus_remote_write_enabled~}
remoteWrite:
- url: "${var.prometheus_remote_write_url}"
basicAuth:
username:
name: "${kubernetes_secret.prometheus-credentials[0].metadata[0].name}"
key: username
password:
name: "${kubernetes_secret.prometheus-credentials[0].metadata[0].name}"
key: password
tlsConfig:
insecureSkipVerify: ${var.prometheus_remote_write_insecure_skip_verify}
writeRelabelConfigs:
- sourceLabels: [__name__]
regex: "${var.prometheus_remote_write_metrics_regex}"
action: keep
metadataConfig:
sendInterval: "${var.prometheus_remote_write_send_interval}"
%{endif~}
EOF
]
}
resource "kubernetes_secret" "prometheus-postgres-password" {
type = "kubernetes.io/basic-auth"
metadata {
name = "prometheus-postgres"
namespace = kubernetes_namespace.prometheus_namespace.metadata.0.name
}
data = {
username = var.prometheus_postgres_user
password = var.prometheus_postgres_password
}
lifecycle {
ignore_changes = [timeouts, wait_for_service_account_token]
}
}
# Install Prometheus Postgres exporter helm chart
resource "helm_release" "prometheus-exporter-chart" {
depends_on = [helm_release.prometheus-chart]
repository = local.prometheus_exporter_helm_repo
chart = local.prometheus_exporter_helm_chart
name = local.prometheus_exporter_release_name
namespace = local.prometheus_namespace
values = [<<EOF
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: "cloud.google.com/gke-nodepool"
operator: "In"
values: ["${var.kubernetes_nodepool_misc}"]
config:
datasource:
host: "${var.prometheus_postgres_host}"
user: "${var.prometheus_postgres_user}"
database: "${var.prometheus_postgres_dbname}"
passwordSecret:
name: "${kubernetes_secret.prometheus-postgres-password.metadata.0.name}"
key: password
autoDiscoverDatabases: true
serviceMonitor:
enabled: true
EOF
]
}
resource "kubernetes_manifest" "coder_monitoring" {
depends_on = [helm_release.prometheus-chart]
manifest = {
apiVersion = "monitoring.coreos.com/v1"
kind = "PodMonitor"
metadata = {
namespace = kubernetes_namespace.coder_namespace.metadata.0.name
name = "coder-monitoring"
}
spec = {
selector = {
matchLabels = {
"app.kubernetes.io/name" : "coder"
}
}
podMetricsEndpoints = [
{
port = "prometheus-http"
interval = "30s"
}
]
}
}
}
-219
View File
@@ -1,219 +0,0 @@
variable "state" {
description = "The state of the cluster. Valid values are 'started', and 'stopped'."
validation {
condition = contains(["started", "stopped"], var.state)
error_message = "value must be one of 'started' or 'stopped'"
}
default = "started"
}
variable "name" {
description = "Adds a prefix to resources."
}
variable "kubernetes_kubeconfig_path" {
description = "Path to kubeconfig to use to provision resources."
}
variable "kubernetes_nodepool_coder" {
description = "Name of the nodepool on which to run Coder."
}
variable "kubernetes_nodepool_workspaces" {
description = "Name of the nodepool on which to run workspaces."
}
variable "kubernetes_nodepool_misc" {
description = "Name of the nodepool on which to run everything else."
}
// These variables control the Coder deployment.
variable "coder_access_url" {
description = "Access URL for the Coder deployment."
}
variable "coder_replicas" {
description = "Number of Coder replicas to provision."
default = 1
}
variable "coder_address" {
description = "IP address to use for Coder service."
}
variable "coder_db_url" {
description = "URL of the database for Coder to use."
sensitive = true
}
// Ensure that requests allow for at least two replicas to be scheduled
// on a single node temporarily, otherwise deployments may fail due to
// lack of resources.
variable "coder_cpu_request" {
description = "CPU request to allocate to Coder."
default = "500m"
}
variable "coder_mem_request" {
description = "Memory request to allocate to Coder."
default = "512Mi"
}
variable "coder_cpu_limit" {
description = "CPU limit to allocate to Coder."
default = "1000m"
}
variable "coder_mem_limit" {
description = "Memory limit to allocate to Coder."
default = "1024Mi"
}
// Allow independently scaling provisionerd resources
variable "provisionerd_cpu_request" {
description = "CPU request to allocate to provisionerd."
default = "100m"
}
variable "provisionerd_mem_request" {
description = "Memory request to allocate to provisionerd."
default = "1Gi"
}
variable "provisionerd_cpu_limit" {
description = "CPU limit to allocate to provisionerd."
default = "1000m"
}
variable "provisionerd_mem_limit" {
description = "Memory limit to allocate to provisionerd."
default = "1Gi"
}
variable "provisionerd_replicas" {
description = "Number of Provisionerd replicas."
default = 1
}
variable "provisionerd_chart_version" {
description = "Version of the Provisionerd Helm chart to install. Defaults to latest."
default = null
}
variable "provisionerd_image_repo" {
description = "Repository to use for Provisionerd image."
default = "ghcr.io/coder/coder"
}
variable "provisionerd_image_tag" {
description = "Tag to use for Provisionerd image."
default = "latest"
}
variable "coder_chart_version" {
description = "Version of the Coder Helm chart to install. Defaults to latest."
default = null
}
variable "coder_image_repo" {
description = "Repository to use for Coder image."
default = "ghcr.io/coder/coder"
}
variable "coder_image_tag" {
description = "Tag to use for Coder image."
default = "latest"
}
variable "coder_experiments" {
description = "Coder Experiments to enable."
default = ""
}
// These variables control the default workspace template.
variable "workspace_image" {
description = "Image and tag to use for workspaces."
default = "docker.io/codercom/enterprise-minimal:ubuntu"
}
variable "workspace_cpu_request" {
description = "CPU request to allocate to workspaces."
default = "100m"
}
variable "workspace_cpu_limit" {
description = "CPU limit to allocate to workspaces."
default = "100m"
}
variable "workspace_mem_request" {
description = "Memory request to allocate to workspaces."
default = "128Mi"
}
variable "workspace_mem_limit" {
description = "Memory limit to allocate to workspaces."
default = "128Mi"
}
// These variables control the Prometheus deployment.
variable "prometheus_external_label_cluster" {
description = "Value for the Prometheus external label named cluster."
}
variable "prometheus_postgres_dbname" {
description = "Database for Postgres to monitor."
}
variable "prometheus_postgres_host" {
description = "Database hostname for Prometheus."
}
variable "prometheus_postgres_password" {
description = "Postgres password for Prometheus."
sensitive = true
}
variable "prometheus_postgres_user" {
description = "Postgres username for Prometheus."
}
variable "prometheus_remote_write_user" {
description = "Username for Prometheus remote write."
default = ""
}
variable "prometheus_remote_write_password" {
description = "Password for Prometheus remote write."
default = ""
sensitive = true
}
variable "prometheus_remote_write_url" {
description = "URL for Prometheus remote write. Defaults to stats.dev.c8s.io."
default = "https://stats.dev.c8s.io:9443/api/v1/write"
}
variable "prometheus_remote_write_insecure_skip_verify" {
description = "Skip TLS verification for Prometheus remote write."
default = true
}
variable "prometheus_remote_write_metrics_regex" {
description = "Allowlist regex of metrics for Prometheus remote write."
default = ".*"
}
variable "prometheus_remote_write_send_interval" {
description = "Prometheus remote write interval."
default = "15s"
}
variable "cloudflare_api_token" {
description = "Cloudflare API token."
sensitive = true
}
variable "cloudflare_email" {
description = "Cloudflare email address."
sensitive = true
}
@@ -1,9 +0,0 @@
nodepool_machine_type_coder = "t2d-standard-8"
nodepool_size_coder = 3
nodepool_machine_type_workspaces = "t2d-standard-8"
cloudsql_tier = "db-custom-2-7680"
coder_cpu_request = "3000m"
coder_mem_request = "12Gi"
coder_cpu_limit = "6000m" # Leaving 2 CPUs for system workloads
coder_mem_limit = "24Gi" # Leaving 8 GB for system workloads
coder_replicas = 3
@@ -1,7 +0,0 @@
nodepool_machine_type_coder = "t2d-standard-8"
nodepool_machine_type_workspaces = "t2d-standard-8"
cloudsql_tier = "db-custom-1-3840"
coder_cpu_request = "3000m"
coder_mem_request = "12Gi"
coder_cpu_limit = "6000m" # Leaving 2 CPUs for system workloads
coder_mem_limit = "24Gi" # Leaving 8 GB for system workloads
@@ -1,6 +0,0 @@
nodepool_machine_type_coder = "t2d-standard-4"
nodepool_machine_type_workspaces = "t2d-standard-4"
coder_cpu_request = "1000m"
coder_mem_request = "6Gi"
coder_cpu_limit = "2000m" # Leaving 2 CPUs for system workloads
coder_mem_limit = "12Gi" # Leaving 4GB for system workloads
-4
View File
@@ -1,4 +0,0 @@
name = "${SCALETEST_NAME}"
project_id = "${SCALETEST_PROJECT}"
prometheus_remote_write_user = "${SCALETEST_PROMETHEUS_REMOTE_WRITE_USER}"
prometheus_remote_write_password = "${SCALETEST_PROMETHEUS_REMOTE_WRITE_PASSWORD}"