diff --git a/agent/nix_builder_autoscaler/bootstrap/userdata.py b/agent/nix_builder_autoscaler/bootstrap/userdata.py index 91cefe9..bb487bf 100644 --- a/agent/nix_builder_autoscaler/bootstrap/userdata.py +++ b/agent/nix_builder_autoscaler/bootstrap/userdata.py @@ -1,71 +1,27 @@ -"""EC2 user-data template rendering for builder instance bootstrap. - -The generated script follows the NixOS AMI pattern: write config files -that existing systemd services (tailscale-autoconnect, nix-daemon) consume, -rather than calling ``tailscale up`` directly. -""" +"""EC2 user-data template rendering for builder instance bootstrap.""" from __future__ import annotations import textwrap -def render_userdata(slot_id: str, region: str, ssm_param: str = "/nix-builder/ts-authkey") -> str: - """Render a bash user-data script for builder instance bootstrap. +def render_userdata(slot_id: str, ssm_param: str = "/nix-builder/ts-authkey") -> str: + """Render user-data that seeds AMI bootstrap inputs only. - The returned string is a complete shell script. On NixOS AMIs the script - is executed by ``amazon-init.service``. The caller (EC2Runtime) passes it - to ``run_instances`` as ``UserData``; boto3 base64-encodes automatically. - - Args: - slot_id: Autoscaler slot identifier (used as Tailscale hostname suffix). - region: AWS region for SSM parameter lookup. - ssm_param: SSM parameter path containing the Tailscale auth key. + The AMI's buildbot-ami-bootstrap service consumes this env file and handles + SSM fetch + tailscale-autoconnect config generation. """ return textwrap.dedent(f"""\ #!/usr/bin/env bash set -euo pipefail SLOT_ID="{slot_id}" - REGION="{region}" SSM_PARAM="{ssm_param}" - # --- Fetch Tailscale auth key from SSM Parameter Store --- - mkdir -p /run/credentials - TS_AUTHKEY=$(aws ssm get-parameter \\ - --region "$REGION" \\ - --with-decryption \\ - --name "$SSM_PARAM" \\ - --query 'Parameter.Value' \\ - --output text) - printf '%s' "$TS_AUTHKEY" > /run/credentials/tailscale-auth-key - chmod 600 /run/credentials/tailscale-auth-key - - # --- Resolve instance identity from IMDSv2 for unique hostname --- - IMDS_TOKEN=$(curl -fsS -X PUT "http://169.254.169.254/latest/api/token" \\ - -H "X-aws-ec2-metadata-token-ttl-seconds: 21600" || true) - INSTANCE_ID=$(curl -fsS -H "X-aws-ec2-metadata-token: $IMDS_TOKEN" \\ - "http://169.254.169.254/latest/meta-data/instance-id" || true) - if [ -z "$INSTANCE_ID" ]; then - INSTANCE_ID="unknown" - fi - - # --- Write tailscale-autoconnect config --- - mkdir -p /etc/tailscale - cat > /etc/tailscale/autoconnect.conf < /run/nix-builder-ready + # Seed AMI bootstrap inputs only; buildbot-ami-bootstrap reads this file. + cat > /etc/nix-builder-bootstrap-env < None: """Launch a single slot. Transition to LAUNCHING on success, ERROR on failure.""" slot_id = slot["slot_id"] - user_data = render_userdata(slot_id, config.aws.region) + user_data = render_userdata(slot_id) try: instance_id = runtime.launch_spot(slot_id, user_data) metrics.counter("autoscaler_ec2_launch_total", {"result": "success"}, 1.0) diff --git a/agent/nix_builder_autoscaler/tests/test_bootstrap_userdata.py b/agent/nix_builder_autoscaler/tests/test_bootstrap_userdata.py new file mode 100644 index 0000000..72afb9d --- /dev/null +++ b/agent/nix_builder_autoscaler/tests/test_bootstrap_userdata.py @@ -0,0 +1,18 @@ +"""Unit tests for builder bootstrap user-data rendering.""" + +from nix_builder_autoscaler.bootstrap.userdata import render_userdata + + +def test_render_userdata_writes_bootstrap_env_inputs() -> None: + script = render_userdata("slot001", ssm_param="/nix-builder/ts-authkey") + assert "cat > /etc/nix-builder-bootstrap-env < None: + script = render_userdata("slot001", ssm_param="/nix-builder/ts-authkey") + assert "aws ssm get-parameter" not in script + assert "/etc/tailscale/autoconnect.conf" not in script + assert "TS_EXTRA_ARGS=" not in script