Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions registry/coder/modules/agent-helper/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
---
display_name: Agent Helper
description: Building block for modules that need orchestrated script execution
icon: ../../../../.icons/coder.svg
verified: false
tags: [internal, library]
---

# Agent Helper

> [!CAUTION]
> We do not recommend using this module directly. It is intended primarily for internal use by Coder to create modules with orchestrated script execution.

The Agent Helper module is a building block for modules that need to run multiple scripts in a specific order. It uses `coder exp sync` for dependency management and is designed for orchestrating pre-install, install, post-install, and start scripts.

> [!NOTE]
>
> - The `agent_name` should be the same as that of the agentapi module's `agent_name` if used together.

```tf
module "agent_helper" {
source = "registry.coder.com/coder/agent-helper/coder"
version = "1.0.0"

agent_id = coder_agent.main.id
agent_name = "myagent"
module_dir_name = ".my-module"

pre_install_script = <<-EOT
#!/bin/bash
echo "Running pre-install tasks..."
# Your pre-install logic here
EOT

install_script = <<-EOT
#!/bin/bash
echo "Installing dependencies..."
# Your install logic here
EOT

post_install_script = <<-EOT
#!/bin/bash
echo "Running post-install configuration..."
# Your post-install logic here
EOT

start_script = <<-EOT
#!/bin/bash
echo "Starting the application..."
# Your start logic here
EOT
}
```

## Execution Order

The module orchestrates scripts in the following order:

1. **Log File Creation** - Creates module directory and log files
2. **Pre-Install Script** (optional) - Runs before installation
3. **Install Script** - Main installation
4. **Post-Install Script** (optional) - Runs after installation
5. **Start Script** - Starts the application

Each script waits for its prerequisites to complete before running using `coder exp sync` dependency management.
13 changes: 13 additions & 0 deletions registry/coder/modules/agent-helper/main.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { describe } from "bun:test";
import { runTerraformInit, testRequiredVariables } from "~test";

describe("agent-helper", async () => {
await runTerraformInit(import.meta.dir);

testRequiredVariables(import.meta.dir, {
agent_id: "test-agent-id",
agent_name: "test-agent",
module_dir_name: ".test-module",
start_script: "echo 'start'",
});
});
202 changes: 202 additions & 0 deletions registry/coder/modules/agent-helper/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
terraform {
required_version = ">= 1.0"

required_providers {
coder = {
source = "coder/coder"
version = ">= 2.13"
}
}
}

variable "agent_id" {
type = string
description = "The ID of a Coder agent."
}

data "coder_workspace" "me" {}

data "coder_workspace_owner" "me" {}

data "coder_task" "me" {}

variable "pre_install_script" {
type = string
description = "Custom script to run before installing the agent used by AgentAPI."
default = null
}

variable "install_script" {
type = string
description = "Script to install the agent used by AgentAPI."
default = ""
}

variable "post_install_script" {
type = string
description = "Custom script to run after installing the agent used by AgentAPI."
default = null
}

variable "start_script" {
type = string
description = "Script that starts AgentAPI."
}

variable "agent_name" {
type = string
description = "The name of the agent. This is used to construct unique script names for the experiment sync."

}

variable "module_dir_name" {
type = string
description = "The name of the module directory."
}

locals {
encoded_pre_install_script = var.pre_install_script != null ? base64encode(var.pre_install_script) : ""
encoded_install_script = var.install_script != null ? base64encode(var.install_script) : ""
encoded_post_install_script = var.post_install_script != null ? base64encode(var.post_install_script) : ""
encoded_start_script = base64encode(var.start_script)

log_file_creation_script_name = "${var.agent_name}-log_file_creation_script"
pre_install_script_name = "${var.agent_name}-pre_install_script"
install_script_name = "${var.agent_name}-install_script"
post_install_script_name = "${var.agent_name}-post_install_script"
start_script_name = "${var.agent_name}-start_script"

module_dir_path = "$HOME/${var.module_dir_name}"

pre_install_path = "${local.module_dir_path}/pre_install.sh"
install_path = "${local.module_dir_path}/install.sh"
post_install_path = "${local.module_dir_path}/post_install.sh"
start_path = "${local.module_dir_path}/start.sh"

pre_install_log_path = "$HOME/pre_install.log"
install_log_path = "$HOME/install.log"
post_install_log_path = "$HOME/post_install.log"
start_log_path = "$HOME/start.log"
}

resource "coder_script" "log_file_creation_script" {
agent_id = var.agent_id
display_name = "Log File Creation Script"
run_on_start = true
script = <<-EOT
#!/bin/bash
set -o errexit
set -o pipefail

trap 'coder exp sync complete ${local.log_file_creation_script_name}' EXIT
coder exp sync start ${local.log_file_creation_script_name}

mkdir -p ${local.module_dir_path}
%{if var.pre_install_script != null~}
touch ${local.pre_install_log_path}
%{endif~}
touch ${local.install_log_path}
%{if var.post_install_script != null~}
touch ${local.post_install_log_path}
%{endif~}
touch ${local.start_log_path}
EOT
}

resource "coder_script" "pre_install_script" {
count = var.pre_install_script != null ? 1 : 0
depends_on = [coder_script.log_file_creation_script]
agent_id = var.agent_id
display_name = "Pre-Install Script"
run_on_start = true
log_path = local.pre_install_log_path
script = <<-EOT
#!/bin/bash
set -o errexit
set -o pipefail

trap 'coder exp sync complete ${local.pre_install_script_name}' EXIT
coder exp sync want ${local.pre_install_script_name} ${local.log_file_creation_script_name}
coder exp sync start ${local.pre_install_script_name}

echo -n '${local.encoded_pre_install_script}' | base64 -d > ${local.pre_install_path}
chmod +x ${local.pre_install_path}

${local.pre_install_path}
EOT
}

resource "coder_script" "install_script" {
agent_id = var.agent_id
depends_on = [coder_script.log_file_creation_script]
display_name = "Install Script"
log_path = local.install_log_path
run_on_start = true
script = <<-EOT
#!/bin/bash
set -o errexit
set -o pipefail

trap 'coder exp sync complete ${local.install_script_name}' EXIT
%{if var.pre_install_script != null~}
coder exp sync want ${local.install_script_name} ${local.pre_install_script_name}
%{else~}
coder exp sync want ${local.install_script_name} ${local.log_file_creation_script_name}
%{endif~}
coder exp sync start ${local.install_script_name}
echo -n '${local.encoded_install_script}' | base64 -d > ${local.install_path}
chmod +x ${local.install_path}

${local.install_path}
EOT
}

resource "coder_script" "post_install_script" {
count = var.post_install_script != null ? 1 : 0
depends_on = [coder_script.log_file_creation_script]
agent_id = var.agent_id
display_name = "Post-Install Script"
log_path = local.post_install_log_path
run_on_start = true
script = <<-EOT
#!/bin/bash
set -o errexit
set -o pipefail

trap 'coder exp sync complete ${local.post_install_script_name}' EXIT
coder exp sync want ${local.post_install_script_name} ${local.install_script_name}
coder exp sync start ${local.post_install_script_name}

echo -n '${local.encoded_post_install_script}' | base64 -d > ${local.post_install_path}
chmod +x ${local.post_install_path}

${local.post_install_path}
EOT
}

resource "coder_script" "start_script" {
agent_id = var.agent_id
depends_on = [coder_script.log_file_creation_script]
display_name = "Start Script"
log_path = local.start_log_path
run_on_start = true
script = <<-EOT
#!/bin/bash
set -o errexit
set -o pipefail

trap 'coder exp sync complete ${local.start_script_name}' EXIT

%{if var.post_install_script != null~}
coder exp sync want ${local.start_script_name} ${local.install_script_name} ${local.post_install_script_name}
%{else~}
coder exp sync want ${local.start_script_name} ${local.install_script_name}
%{endif~}
coder exp sync start ${local.start_script_name}

echo -n '${local.encoded_start_script}' | base64 -d > ${local.start_path}
chmod +x ${local.start_path}

${local.start_path}
EOT
}
Loading