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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions plugins/cloudwatch-agent/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# cloudwatch-agent

Installs and starts the **Amazon CloudWatch agent** on your spawn instance to
publish host metrics (CPU, memory, disk) and, optionally, a log file to
CloudWatch Logs.

## ⚠️ Precondition: the instance IAM role must have CloudWatch access

The agent ships metrics/logs using the **instance's IAM role**. A plugin runs
post-launch and **cannot** set the instance profile — launch the instance with a
role granting `cloudwatch:PutMetricData` (and, if you use `log_file`,
`logs:CreateLogGroup`/`CreateLogStream`/`PutLogEvents`). The AWS-managed
`CloudWatchAgentServerPolicy` covers this.

## How it works

The plugin installs `amazon-cloudwatch-agent` (from the AL2023 repos, or the
official package elsewhere), writes a config publishing CPU/mem/disk under your
metrics namespace, optionally adds a log-file tail, and starts the agent via its
control script. A health check confirms the agent reports `running`.

## Config

| Key | Default | Description |
|-----|---------|-------------|
| `metrics_namespace` | `CWAgent` | CloudWatch metrics namespace. |
| `collection_interval` | `60` | Metrics collection interval (seconds). |
| `log_file` | *(none)* | Optional log file path to ship to CloudWatch Logs. |
| `log_group` | `/spore/instance` | Log group for `log_file`. |

## Install

```bash
spawn plugin install cloudwatch-agent --instance <id> \
--config log_file=/var/log/myapp.log --config log_group=/spore/myapp
```

## Notes

- spawn's autoscaler reads the **`CWAgent`** namespace — keep the default
namespace if you want autoscaling to see these metrics.
- Metrics/logs to CloudWatch incur AWS charges; a 60s interval on a few metrics
is inexpensive but not free.
- spawn's own telemetry goes through `spored`/Prometheus independently — this
plugin is for exporting to CloudWatch specifically (dashboards, alarms,
autoscaling).
122 changes: 122 additions & 0 deletions plugins/cloudwatch-agent/plugin.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
name: cloudwatch-agent
version: v1.0.0
description: "Install and start the Amazon CloudWatch agent for host metrics + logs"
author: spore-host
license: Apache-2.0

# Installs the Amazon CloudWatch agent and starts it with a config that publishes
# host metrics (CPU/mem/disk under the CWAgent namespace) and optionally tails a
# log file to CloudWatch Logs. Metrics/logs are sent using the INSTANCE'S IAM
# ROLE — a plugin cannot set the role, so it must already grant CloudWatch access
# (see README / the condition). No secret is passed.

permissions:
instance:
root: true # installs the agent package and manages its service
network: true # downloads the package + ships metrics/logs to CloudWatch
files:
- /opt/aws/amazon-cloudwatch-agent

config:
metrics_namespace:
type: string
required: false
default: "CWAgent"
description: "CloudWatch metrics namespace"
collection_interval:
type: int
required: false
default: 60
description: "Metrics collection interval (seconds)"
log_file:
type: string
required: false
default: ""
description: "Optional path to a log file to ship to CloudWatch Logs (empty = metrics only)"
log_group:
type: string
required: false
default: "/spore/instance"
description: "CloudWatch Logs group name for log_file"

conditions:
remote:
- type: platform
os: linux
message: "cloudwatch-agent plugin requires a Linux instance"

remote:
install:
- type: run
run: |
set -eu
# AL2023/Amazon Linux ship the agent in the default (GPG-verified) repos;
# fall back to the official S3 package elsewhere.
if dnf install -y amazon-cloudwatch-agent 2>/dev/null; then
:
elif command -v dnf >/dev/null; then
curl -fsSL -o /tmp/cwagent.rpm https://amazoncloudwatch-agent.s3.amazonaws.com/amazon_linux/amd64/latest/amazon-cloudwatch-agent.rpm
dnf install -y /tmp/cwagent.rpm
else
curl -fsSL -o /tmp/cwagent.deb https://amazoncloudwatch-agent.s3.amazonaws.com/ubuntu/amd64/latest/amazon-cloudwatch-agent.deb
apt-get install -y /tmp/cwagent.deb
fi

configure:
- type: run
run: |
set -eu
NS={{ config.metrics_namespace }}
INTERVAL={{ config.collection_interval }}
LOGFILE={{ config.log_file }}
LOGGROUP={{ config.log_group }}
# Base metrics config (CPU/mem/disk). Append a logs section only when a
# log_file is configured. jq isn't guaranteed present, so compose the JSON
# with a heredoc. NOTE: keep CloudWatch's own placeholder tokens to single
# braces or dollar form so spore's template validator (which scans every
# step string) doesn't mistake them for plugin template references.
CONF=/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json
mkdir -p "$(dirname "$CONF")"
LOGS_SECTION=""
if [ -n "$LOGFILE" ]; then
LOGS_SECTION=$(cat <<EOF
,"logs":{"logs_collected":{"files":{"collect_list":[
{"file_path":"$LOGFILE","log_group_name":"$LOGGROUP","log_stream_name":"{instance_id}"}
]}}}
EOF
)
fi
cat > "$CONF" <<EOF
{
"agent": {"metrics_collection_interval": $INTERVAL},
"metrics": {
"namespace": "$NS",
"append_dimensions": {"InstanceId": "\${aws:InstanceId}"},
"metrics_collected": {
"cpu": {"measurement": ["usage_active"]},
"mem": {"measurement": ["mem_used_percent"]},
"disk": {"measurement": ["used_percent"], "resources": ["/"]}
}
}$LOGS_SECTION
}
EOF

start:
- type: run
run: |
set -eu
/opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl \
-a fetch-config -m ec2 -s \
-c file:/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json

health:
interval: 30s
steps:
- type: run
run: |
/opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a status | grep -q '"status": "running"'

stop:
- type: run
run: |
/opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a stop || true
63 changes: 63 additions & 0 deletions plugins/code-server/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# code-server

Installs and starts **code-server** — a full VS Code IDE served in your browser —
on your spawn instance, so you get the VS Code editor, terminal, and extensions
on a machine sized for the work.

## How it works

The plugin runs the official code-server installer, writes a
`~/.config/code-server/config.yaml` for `ec2-user` with password auth, and starts
the `code-server@ec2-user` systemd service. code-server listens on
**127.0.0.1:8080** by default. A health check watches the service.

Everything runs **on the instance** — no controller-side setup. Works on RPM-based
(Amazon Linux/RHEL/Rocky) and Debian/Ubuntu AMIs, x86_64 and arm64.

## Config

| Key | Default | Description |
|-----|---------|-------------|
| `port` | `8080` | Port code-server listens on (loopback only). |
| `password` | `spore` | Password for the web UI. **Set your own.** |

## Install

At launch:

```yaml
# launch.yaml
plugins:
- name: code-server
config:
password: <choose-a-strong-password>
```

Or on a running instance:

```bash
spawn plugin install code-server --instance <id> --config password=<strong-password>
```

## Connecting

code-server binds to **loopback** (`127.0.0.1:8080`); the security group does not
open it to the internet. Reach it one of two safe ways:

**SSH tunnel (simplest):**

```bash
ssh -L 8080:localhost:8080 <instance>
# then open http://localhost:8080/ and log in with your password
```

**Tailscale:** add the [`tailscale`](../tailscale/) plugin and tunnel over SSH, or
adjust `bind-addr` to the Tailscale IP for mesh-only access. The simplest secure
default is the SSH tunnel.

## Notes

- The installer sets up a per-user systemd template unit
(`code-server@ec2-user`); the plugin enables it for `ec2-user`.
- If you prefer VS Code's own Remote Tunnels instead of a browser IDE, see the
[`vscode-tunnel`](../vscode-tunnel/) plugin, which needs no port at all.
84 changes: 84 additions & 0 deletions plugins/code-server/plugin.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
name: code-server
version: v1.0.0
description: "Install and start code-server (VS Code in the browser)"
author: spore-host
license: Apache-2.0

# Installs code-server (a full VS Code IDE served over HTTP) via the official
# installer and runs it as a systemd service on 127.0.0.1:<port>. Like
# rstudio-server / jupyterlab, the UI is NOT exposed publicly — reach it via an
# SSH tunnel or Tailscale (see README). A password gates access.

permissions:
instance:
root: true # runs the official installer (dnf/apt) and manages a systemd unit
network: true # downloads the code-server package
ports:
- 8080 # code-server web UI (listens on loopback; reach via tunnel/Tailscale)
files:
- /home/ec2-user/.config/code-server/config.yaml

config:
port:
type: int
required: false
default: 8080
description: "Port code-server listens on (loopback only)"
password:
type: string
required: false
default: "spore"
description: "Password for the web UI. Set your own."

conditions:
remote:
- type: platform
os: linux
message: "code-server plugin requires a Linux instance"

remote:
install:
- type: run
run: |
set -eu
# Official installer; supports RPM/DEB and x86_64/arm64. It's a moving
# redirect (not a pinnable immutable asset), so — like rstudio-server —
# we install via the vendor's own script rather than a fetch+sha256 of a
# specific file. It installs a systemd template unit code-server@<user>.
curl -fsSL https://code-server.dev/install.sh | sh

configure:
- type: run
run: |
set -eu
PORT={{ config.port }}
PASSWORD={{ config.password }}
# code-server reads ~/.config/code-server/config.yaml of the service user.
install -d -o ec2-user -g ec2-user /home/ec2-user/.config/code-server
cat > /home/ec2-user/.config/code-server/config.yaml <<EOF
bind-addr: 127.0.0.1:$PORT
auth: password
password: $PASSWORD
cert: false
EOF
chown ec2-user:ec2-user /home/ec2-user/.config/code-server/config.yaml
chmod 600 /home/ec2-user/.config/code-server/config.yaml

start:
- type: run
run: |
systemctl enable --now code-server@ec2-user

health:
interval: 15s
steps:
- type: run
run: systemctl is-active code-server@ec2-user

stop:
- type: run
run: systemctl stop code-server@ec2-user

outputs:
url:
description: "code-server URL (http://localhost:<port> over an SSH tunnel)"
47 changes: 47 additions & 0 deletions plugins/docker/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# docker

Installs the **Docker engine** on your spawn instance and enables it as a
persistent service, so an interactive or long-lived box can build and run
containers.

## How it works

The plugin installs docker (from the AL2023/Amazon Linux repos, or the official
convenience script on Debian/Ubuntu), enables and starts the `docker` service,
and adds `ec2-user` to the `docker` group so you can run `docker` without `sudo`.
A health check runs `docker info`.

Everything runs **on the instance** — no controller-side setup, no config.

## When to use this vs. `task run --container`

- **This plugin** — you want Docker *always available* on a persistent instance
you SSH into and work on interactively (build images, run `docker compose`,
etc.).
- **`spawn task run --container <image>`** — you want to run a single containerized
job on an ephemeral instance; spawn installs docker on demand and tears the
instance down after. No plugin needed.

## Install

At launch:

```yaml
# launch.yaml
plugins:
- name: docker
```

Or on a running instance:

```bash
spawn plugin install docker --instance <id>
```

## Notes

- The `ec2-user` docker-group membership takes effect on your **next login** —
reconnect (or run `newgrp docker`) after install to use docker without sudo.
- For GPU containers, launch on a GPU instance type (spawn auto-selects an NVIDIA
driver AMI) — the driver + container toolkit come from that AMI; then
`docker run --gpus all …` works.
Loading
Loading