terraform {
  required_providers {
    coder = {
      source = "coder/coder"
    }
    docker = {
      source = "kreuzwerker/docker"
    }
    artifactory = {
      source = "registry.terraform.io/jfrog/artifactory"
    }
  }
}

locals {
  # Make sure to use the same field as the username field in the Artifactory
  # It can be either the username or the email address.
  artifactory_username = data.coder_workspace_owner.me.email
  artifactory_repository_keys = {
    "npm"    = "npm"
    "python" = "python"
    "go"     = "go"
  }
  workspace_user = data.coder_workspace_owner.me.name
  jfrog_host     = replace(var.jfrog_url, "^https://", "")
}

data "coder_provisioner" "me" {}

provider "docker" {}

data "coder_workspace" "me" {}
data "coder_workspace_owner" "me" {}

variable "jfrog_url" {
  type        = string
  description = "JFrog instance URL. For example, https://jfrog.example.com."
  # validate the URL to ensure it starts with https:// or http://
  validation {
    condition     = can(regex("^https?://", var.jfrog_url))
    error_message = "JFrog URL must start with https:// or http://"
  }
}

variable "artifactory_admin_access_token" {
  type        = string
  description = "The admin-level access token to use for JFrog with scope applied-permissions/admin."
}

# Configure the Artifactory provider with the admin-level access token.
provider "artifactory" {
  url          = "${var.jfrog_url}/artifactory"
  access_token = var.artifactory_admin_access_token
}

resource "artifactory_scoped_token" "me" {
  # This is hacky, but on terraform plan the data source gives empty strings,
  # which fails validation.
  username = length(local.artifactory_username) > 0 ? local.artifactory_username : "plan"
}

resource "coder_agent" "main" {
  arch           = data.coder_provisioner.me.arch
  os             = "linux"
  startup_script = <<-EOT
    set -e

    # install and start code-server
    curl -fsSL https://code-server.dev/install.sh | sh -s -- --method=standalone --prefix=/tmp/code-server
    /tmp/code-server/bin/code-server --auth none --port 13337 >/tmp/code-server.log 2>&1 &

    # Install the JFrog VS Code extension.
    # Find the latest version number at
    # https://open-vsx.org/extension/JFrog/jfrog-vscode-extension.
    /tmp/code-server/bin/code-server --install-extension jfrog.jfrog-vscode-extension

    # The jf CLI checks $CI when determining whether to use interactive
    # flows.
    export CI=true

    jf c rm 0 || true
    echo ${artifactory_scoped_token.me.access_token} | \
      jf c add --access-token-stdin --url ${var.jfrog_url} 0

    # Configure the `npm` CLI to use the Artifactory "npm" repository.
    cat << EOF > ~/.npmrc
    email = ${data.coder_workspace_owner.me.email}
    registry = ${var.jfrog_url}/artifactory/api/npm/${local.artifactory_repository_keys["npm"]}
    EOF
    jf rt curl /api/npm/auth >> .npmrc

    # Configure the `pip` to use the Artifactory "python" repository.
    mkdir -p ~/.pip
    cat << EOF > ~/.pip/pip.conf
    [global]
    index-url = https://${local.artifactory_username}:${artifactory_scoped_token.me.access_token}@${local.jfrog_host}/artifactory/api/pypi/${local.artifactory_repository_keys["python"]}/simple
    EOF

  EOT
  # Set GOPROXY to use the Artifactory "go" repository.
  env = {
    GOPROXY : "https://${local.artifactory_username}:${artifactory_scoped_token.me.access_token}@${local.jfrog_host}/artifactory/api/go/${local.artifactory_repository_keys["go"]}"
    # Authenticate with JFrog extension.
    JFROG_IDE_URL : "${var.jfrog_url}"
    JFROG_IDE_USERNAME : "${local.artifactory_username}"
    JFROG_IDE_PASSWORD : "${artifactory_scoped_token.me.access_token}"
    JFROG_IDE_ACCESS_TOKEN : "${artifactory_scoped_token.me.access_token}"
    JFROG_IDE_STORE_CONNECTION : "true"
  }
}

resource "coder_app" "code-server" {
  agent_id     = coder_agent.main.id
  slug         = "code-server"
  display_name = "code-server"
  url          = "http://localhost:13337/?folder=/home/${local.workspace_user}"
  icon         = "/icon/code.svg"
  subdomain    = false
  share        = "owner"

  healthcheck {
    url       = "http://localhost:13337/healthz"
    interval  = 5
    threshold = 6
  }
}

resource "docker_volume" "home_volume" {
  name = "coder-${data.coder_workspace.me.id}-home"
  # Protect the volume from being deleted due to changes in attributes.
  lifecycle {
    ignore_changes = all
  }
}

resource "docker_image" "main" {
  name = "coder-${data.coder_workspace.me.id}"
  build {
    context = "${path.module}/build"
    build_args = {
      USER = local.workspace_user
    }
  }
  triggers = {
    dir_sha1 = sha1(join("", [for f in fileset(path.module, "build/*") : filesha1("${path.module}/${f}")]))
  }
}

resource "docker_container" "workspace" {
  count = data.coder_workspace.me.start_count
  image = docker_image.main.name
  # Uses lower() to avoid Docker restriction on container names.
  name = "coder-${data.coder_workspace_owner.me.name}-${lower(data.coder_workspace.me.name)}"
  # Hostname makes the shell more user friendly: coder@my-workspace:~$
  hostname   = data.coder_workspace.me.name
  entrypoint = ["sh", "-c", coder_agent.main.init_script]
  env        = ["CODER_AGENT_TOKEN=${coder_agent.main.token}"]
  host {
    host = "host.docker.internal"
    ip   = "host-gateway"
  }
  volumes {
    container_path = "/home/${local.workspace_user}"
    volume_name    = docker_volume.home_volume.name
    read_only      = false
  }
}
