Skip to content

Instantly share code, notes, and snippets.

@fpoussin
Last active October 17, 2022 20:31
Show Gist options
  • Save fpoussin/b9da6254edd95a53e2853081779d72d1 to your computer and use it in GitHub Desktop.
Save fpoussin/b9da6254edd95a53e2853081779d72d1 to your computer and use it in GitHub Desktop.
Coder terraform issue
data "coder_workspace" "me" {}
resource "coder_agent" "main" {
os = "linux"
arch = "amd64"
dir = "/home/coder/projects"
startup_script = <<-EOT
#!/bin/bash
# install and start code-server
curl -fsSL https://code-server.dev/install.sh | bash | tee code-server-install.log
code-server --auth none --port 13337 | tee code-server-install.log &
EOT
}
resource "coder_app" "code-server" {
agent_id = coder_agent.main.id
name = "code-server"
icon = "/icon/code.svg"
url = "http://localhost:13337?folder=/home/coder"
}
resource "coder_app" "vim" {
agent_id = coder_agent.main.id
name = "Vim"
# TODO: revert to local icon when it's added
icon = "https://upload.wikimedia.org/wikipedia/commons/9/9f/Vimlogo.svg"
command = "vim"
}
resource "coder_metadata" "pod_info" {
count = data.coder_workspace.me.start_count
resource_id = kubernetes_deployment.main.id
item {
key = "CPU"
value = kubernetes_deployment.main.spec[0].template[0].spec[0].container[0].resources[0].limits.cpu
}
item {
key = "Memory"
value = kubernetes_deployment.main.spec[0].template[0].spec[0].container[0].resources[0].limits.memory
}
item {
key = "Image"
value = kubernetes_deployment.main.spec[0].template[0].spec[0].container[0].image
}
}
resource "coder_metadata" "home_info" {
count = data.coder_workspace.me.start_count
resource_id = kubernetes_persistent_volume_claim.home.id
item {
key = "Size"
value = kubernetes_persistent_volume_claim.home.spec[0].resources[0].requests.storage
}
}
resource "coder_metadata" "localstack_info" {
count = data.coder_workspace.me.start_count
resource_id = kubernetes_persistent_volume_claim.localstack.id
item {
key = "Size"
value = kubernetes_persistent_volume_claim.localstack.spec[0].resources[0].requests.storage
}
}
resource "coder_metadata" "postgres_info" {
count = data.coder_workspace.me.start_count
resource_id = kubernetes_persistent_volume_claim.postgres.id
item {
key = "Size"
value = kubernetes_persistent_volume_claim.postgres.spec[0].resources[0].requests.storage
}
}
resource "kubernetes_job" "prepare-home" {
metadata {
name = "coder-${local.basename}-pre"
namespace = var.namespace
labels = {
k8s-app = "coder-workspace"
workspace_name = local.name
workspace_owner = local.owner
workspace_type = "nodejs"
}
}
spec {
template {
metadata {
labels = {
k8s-app = "coder-workspace"
workspace_name = local.name
workspace_owner = local.owner
workspace_type = "nodejs"
}
}
spec {
security_context {
run_as_user = "1000"
fs_group = "1000"
}
container {
name = "prepare-home"
image = "xxx/coder:base"
image_pull_policy = "Always"
command = ["bash", "/usr/share/prepare-home.sh"]
volume_mount {
mount_path = "/home/coder"
name = "home"
read_only = false
}
resources {
limits = {
cpu = "1"
memory = "1Gi"
}
requests = {
cpu = "100m"
memory = "128m"
}
}
}
restart_policy = "Never"
volume {
name = "home"
persistent_volume_claim {
claim_name = kubernetes_persistent_volume_claim.home.metadata.0.name
read_only = false
}
}
}
}
backoff_limit = 4
}
wait_for_completion = true
# Pulling image can take ~1mn
timeouts {
create = "5m"
update = "5m"
}
lifecycle {
ignore_changes = [
spec,
]
}
}
resource "kubernetes_deployment" "main" {
metadata {
name = "coder-${local.basename}"
namespace = var.namespace
labels = {
workspace_name = local.name
workspace_owner = local.owner
}
}
spec {
strategy {
type = "Recreate"
}
replicas = data.coder_workspace.me.start_count
selector {
match_labels = {
k8s-app = "coder-workspace"
workspace_name = local.name
workspace_owner = local.owner
}
}
template {
metadata {
labels = {
k8s-app = "coder-workspace"
workspace_name = local.name
workspace_owner = local.owner
workspace_type = "ruby"
}
annotations = {}
}
spec {
security_context {
run_as_user = "1000"
fs_group = "1000"
}
init_container {
name = "setup"
image = "xxx/coder:base"
image_pull_policy = "Always" # default with "latest" tag, but we're not using it
command = ["bash", "/usr/share/git-ssh-setup.sh"]
security_context {
run_as_user = "1000"
}
env {
name = "CODER_AGENT_TOKEN"
value = coder_agent.main.token
}
volume_mount {
mount_path = "/home/coder"
name = "home"
read_only = false
}
resources {
limits = {
cpu = "1"
memory = "1Gi"
}
requests = {
cpu = "100m"
memory = "128m"
}
}
}
container {
name = "coder"
image = "xxx/coder:base"
image_pull_policy = "Always" # default with "latest" tag, but we're not using it
command = [
"sh",
"-c",
coder_agent.main.init_script
]
security_context {
run_as_user = "1000"
}
env {
name = "CODER_AGENT_TOKEN"
value = coder_agent.main.token
}
env {
name = "DATABASE_URL"
value = "postgres://coder:coder@postgres-${local.basename}.${var.namespace}.svc/coder"
}
volume_mount {
mount_path = "/home/coder"
name = "home"
read_only = false
}
resources {
limits = {
cpu = "4"
memory = "4Gi"
}
requests = {
cpu = "250m"
memory = "1Gi"
}
}
}
container {
name = "localstack"
image = "localstack/localstack:1.1.0"
security_context {
run_as_user = "1000"
}
port {
name = "edgeservices"
container_port = 4566
protocol = "TCP"
}
port {
name = "apiservices"
container_port = 4571
protocol = "TCP"
}
env {
name = "PERSISTENCE"
value = "1"
}
env {
name = "LOCALSTACK_VOLUME_DIR"
value = "/var/lib/localstack"
}
resources {
limits = {
cpu = "1"
memory = "1Gi"
}
requests = {
cpu = "100m"
memory = "256Mi"
}
}
volume_mount {
mount_path = "/var/lib/localstack"
name = "localstack"
read_only = false
}
}
container {
name = "redis"
image = "redis:6"
image_pull_policy = "Always"
security_context {
run_as_user = "1000"
}
port {
name = "redis"
container_port = 6379
protocol = "TCP"
}
resources {
limits = {
cpu = "500m"
memory = "512Mi"
}
requests = {
cpu = "100m"
memory = "128Mi"
}
}
}
volume {
name = "home"
persistent_volume_claim {
claim_name = kubernetes_persistent_volume_claim.home.metadata.0.name
read_only = false
}
}
volume {
name = "localstack"
persistent_volume_claim {
claim_name = kubernetes_persistent_volume_claim.localstack.metadata.0.name
read_only = false
}
}
}
}
}
depends_on = [kubernetes_job.prepare-home]
}
resource "kubernetes_deployment" "postgres" {
metadata {
name = "postgres-${local.basename}"
namespace = var.namespace
labels = {
workspace_name = local.name
workspace_owner = local.owner
}
}
spec {
strategy {
type = "Recreate"
}
replicas = data.coder_workspace.me.start_count
selector {
match_labels = {
k8s-app = "postgresql"
workspace_name = local.name
workspace_owner = local.owner
}
}
template {
metadata {
labels = {
k8s-app = "postgresql"
workspace_name = local.name
workspace_owner = local.owner
}
annotations = {}
}
spec {
container {
name = "postgres"
image = "postgres:13-bullseye"
image_pull_policy = "Always"
env {
name = "POSTGRES_USER"
value = "coder"
}
env {
name = "POSTGRES_PASSWORD"
value = "coder"
}
env {
name = "POSTGRES_DB"
value = "coder"
}
port {
name = "postgres"
container_port = 5432
protocol = "TCP"
}
resources {
limits = {
cpu = "1"
memory = "512Mi"
}
requests = {
cpu = "100m"
memory = "128Mi"
}
}
volume_mount {
mount_path = "/var/lib/postgresql"
name = "postgres"
read_only = false
}
}
volume {
name = "postgres"
persistent_volume_claim {
claim_name = kubernetes_persistent_volume_claim.postgres.metadata.0.name
read_only = false
}
}
}
}
}
depends_on = [kubernetes_job.prepare-home]
}
resource "kubernetes_service" "postgres" {
metadata {
name = "postgres-${local.basename}"
namespace = var.namespace
labels = {
k8s-app = "postgresql"
workspace_name = local.name
workspace_owner = local.owner
}
}
spec {
selector = {
k8s-app = "postgresql"
workspace_name = local.name
workspace_owner = local.owner
}
port {
port = 5432
}
type = "ClusterIP"
}
}
resource "kubernetes_persistent_volume_claim" "home" {
metadata {
name = "coder-${local.basename}-home"
namespace = var.namespace
labels = {
workspace_name = local.name
workspace_owner = local.owner
}
}
spec {
access_modes = ["ReadWriteOnce"]
resources {
requests = {
storage = "${var.home_disk_size}Gi"
}
}
}
wait_until_bound = false
# Don't destroy when storage is increased manually
lifecycle {
ignore_changes = [
spec,
]
}
}
resource "kubernetes_persistent_volume_claim" "localstack" {
metadata {
name = "localstack-${local.basename}"
namespace = var.namespace
labels = {
workspace_name = local.name
workspace_owner = local.owner
}
}
spec {
access_modes = ["ReadWriteOnce"]
resources {
requests = {
storage = "${var.localstack_disk_size}Gi"
}
}
}
wait_until_bound = false
# Don't destroy when storage is increased manually
lifecycle {
ignore_changes = [
spec,
]
}
}
resource "kubernetes_persistent_volume_claim" "postgres" {
metadata {
name = "postgres-${local.basename}"
namespace = var.namespace
labels = {
workspace_name = local.name
workspace_owner = local.owner
}
}
spec {
access_modes = ["ReadWriteOnce"]
resources {
requests = {
storage = "${var.postgres_disk_size}Gi"
}
}
}
wait_until_bound = false
# Don't destroy when storage is increased manually
lifecycle {
ignore_changes = [
spec,
]
}
}
terraform {
required_providers {
coder = {
source = "coder/coder"
version = "0.5.3"
}
kubernetes = {
source = "hashicorp/kubernetes"
version = "~> 2.13.1"
}
}
}
provider "kubernetes" {
// Use service account for kube config
config_path = null
}
provider "coder" {
url = "http://coder.coder.svc.cluster.local" # Replace ingress url with service for direct connection
}
locals {
name = lower(data.coder_workspace.me.name)
owner = lower(data.coder_workspace.me.owner)
basename = "${local.owner}-${local.name}"
}
variable "namespace" {
type = string
sensitive = true
description = "The namespace to create workspaces in (must exist prior to creating workspaces)"
default = "coder-workspaces"
}
variable "home_disk_size" {
type = number
description = "Home volume size in GB"
default = 10
validation {
condition = var.home_disk_size >= 1 && var.home_disk_size <= 20
error_message = "Value must be between 1 and 20"
}
}
variable "localstack_disk_size" {
type = number
description = "Localstack volume size in GB"
default = 1
validation {
condition = var.localstack_disk_size >= 1 && var.localstack_disk_size <= 5
error_message = "Value must be between 1 and 5"
}
}
variable "postgres_disk_size" {
type = number
description = "PostgreSQL volume size in GB"
default = 2
validation {
condition = var.postgres_disk_size >= 1 && var.postgres_disk_size <= 10
error_message = "Value must be between 1 and 10"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment