Skip to content

Commit 1d38f21

Browse files
committed
feat: iterate on ami, packer, ansible, ssh to investigate
1 parent 6a8cd73 commit 1d38f21

37 files changed

+3016
-10
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,5 @@ common-nix.vars.pkr.hcl
3131
# pre-commit config is managed in nix
3232
.pre-commit-config.yaml
3333
nixos.qcow2
34+
nix/packages/pg-ami-builder/pg-ami-builder
35+
nix/packages/pg-ami-builder/vendor/

ansible/tasks/setup-system.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
ansible.builtin.apt:
1313
cache_valid_time: 3600
1414
pkg:
15-
- acl
15+
- acll
1616
- bwm-ng
1717
- fail2ban
1818
- htop

docs/ami-local-development.md

Lines changed: 272 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,272 @@
1+
# Local AMI Development with pg-ami-builder
2+
3+
This guide explains how to use `pg-ami-builder` for local AMI development and iteration.
4+
5+
## Prerequisites
6+
7+
### Required Tools
8+
9+
- AWS CLI v2
10+
- aws-vault (for credential management)
11+
- SSM Session Manager plugin
12+
- Git
13+
- Nix
14+
15+
### Installing SSM Session Manager Plugin
16+
17+
**macOS:**
18+
```bash
19+
brew install --cask session-manager-plugin
20+
```
21+
22+
**Linux:**
23+
See [AWS documentation](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html)
24+
25+
### AWS Permissions
26+
27+
Your AWS user/role needs these permissions:
28+
- EC2: RunInstances, TerminateInstances, DescribeInstances, CreateTags
29+
- EC2: CreateSecurityGroup, DeleteSecurityGroup, AuthorizeSecurityGroupIngress
30+
- SSM: StartSession, DescribeSessions
31+
- EC2: CreateImage, DescribeImages (if using --create-ami)
32+
33+
## Quick Start
34+
35+
### Building Phase 1
36+
37+
```bash
38+
# Run phase 1 build (launches instance and runs packer build)
39+
aws-vault exec dev -- nix run .#pg-ami-builder -- build phase1 --postgres-version 15
40+
41+
# If packer build fails, instance stays alive for debugging
42+
# SSH to investigate
43+
aws-vault exec dev -- nix run .#pg-ami-builder -- ssh
44+
45+
# Make local changes and re-run with file sync
46+
vim ansible/playbook.yml
47+
aws-vault exec dev -- nix run .#pg-ami-builder -- ansible-rerun phase1 --sync-files
48+
49+
# Cleanup when done
50+
aws-vault exec dev -- nix run .#pg-ami-builder -- cleanup
51+
```
52+
53+
### Building Phase 2
54+
55+
```bash
56+
# Run phase 2 with existing stage-1 AMI
57+
aws-vault exec dev -- nix run .#pg-ami-builder -- build phase2 \
58+
--source-ami ami-stage1-xyz \
59+
--postgres-version 15
60+
```
61+
62+
## Commands
63+
64+
### build phase1
65+
66+
Launch EC2 instance and run phase 1 ansible playbook.
67+
68+
```bash
69+
nix run .#pg-ami-builder -- build phase1 --postgres-version 15 [flags]
70+
```
71+
72+
**Flags:**
73+
- `--postgres-version` (required) - PostgreSQL major version (15, 16, 17)
74+
- `--region` - AWS region (default: us-east-1)
75+
- `--create-ami` - Create AMI on success (default: false)
76+
- `--ansible-args` - Additional ansible arguments
77+
- `--instance-type` - EC2 instance type (default: c6g.4xlarge)
78+
- `--state-file` - Custom state file path
79+
80+
### build phase2
81+
82+
Launch EC2 instance from stage-1 AMI and run phase 2 ansible playbook.
83+
84+
```bash
85+
nix run .#pg-ami-builder -- build phase2 --source-ami ami-xyz --postgres-version 15 [flags]
86+
```
87+
88+
**Flags:**
89+
- `--source-ami` (required) - Stage-1 AMI ID
90+
- `--postgres-version` (required) - PostgreSQL major version
91+
- `--git-sha` - Git SHA for nix packages (default: current HEAD)
92+
- Plus all flags from phase1
93+
94+
### ansible-rerun
95+
96+
Re-run ansible playbook on existing instance. Optionally sync local file changes first.
97+
98+
```bash
99+
nix run .#pg-ami-builder -- ansible-rerun phase1 [flags]
100+
```
101+
102+
**Flags:**
103+
- `--instance-id` - Target specific instance (default: from state file)
104+
- `--sync-files` - Sync local ansible/, scripts/, and migrations/ files before running (default: false)
105+
- `--ansible-args` - Additional ansible arguments
106+
- `--skip-tags` - Ansible tags to skip
107+
- `--region` - AWS region (default: us-east-1)
108+
109+
**Examples:**
110+
111+
```bash
112+
# Re-run without syncing files (use existing files on instance)
113+
nix run .#pg-ami-builder -- ansible-rerun phase1
114+
115+
# Re-run with local file changes
116+
nix run .#pg-ami-builder -- ansible-rerun phase1 --sync-files
117+
118+
# Re-run with skip tags
119+
nix run .#pg-ami-builder -- ansible-rerun phase1 --skip-tags migrations
120+
```
121+
122+
### ssh
123+
124+
Connect to instance via AWS SSM Session Manager (default) or EC2 Instance Connect.
125+
126+
```bash
127+
nix run .#pg-ami-builder -- ssh [flags]
128+
```
129+
130+
**Flags:**
131+
- `--instance-id` - Target specific instance for SSM (default: from state file)
132+
- `--region` - AWS region for SSM (default: us-east-1)
133+
- `--aws-ec2-connect-cmd` - Full AWS EC2 Instance Connect command string
134+
135+
**Examples:**
136+
137+
```bash
138+
# Connect via SSM (default)
139+
nix run .#pg-ami-builder -- ssh
140+
141+
# Connect via EC2 Instance Connect
142+
nix run .#pg-ami-builder -- ssh \
143+
--aws-ec2-connect-cmd "aws ec2-instance-connect ssh --instance-id i-024bba2db43e4b41f --region us-east-1"
144+
```
145+
146+
### cleanup
147+
148+
Terminate instance and remove associated resources.
149+
150+
```bash
151+
nix run .#pg-ami-builder -- cleanup [flags]
152+
```
153+
154+
**Flags:**
155+
- `--instance-id` - Target specific instance (default: from state file)
156+
- `--force` - Skip confirmation prompt
157+
158+
## Workflows
159+
160+
### Workflow 1: Develop and test phase 1 changes
161+
162+
```bash
163+
# Run phase 1 build (launches instance and runs packer build)
164+
aws-vault exec dev -- nix run .#pg-ami-builder -- build phase1 --postgres-version 15
165+
166+
# If packer fails, instance stays up for debugging
167+
# SSH to investigate
168+
aws-vault exec dev -- nix run .#pg-ami-builder -- ssh
169+
170+
# Make local changes to ansible files
171+
vim ansible/playbook.yml
172+
173+
# Re-run with your local changes
174+
aws-vault exec dev -- nix run .#pg-ami-builder -- ansible-rerun phase1 --sync-files
175+
176+
# Repeat until working, then create AMI
177+
aws-vault exec dev -- nix run .#pg-ami-builder -- build phase1 --postgres-version 15 --create-ami
178+
179+
# Cleanup
180+
aws-vault exec dev -- nix run .#pg-ami-builder -- cleanup
181+
```
182+
183+
### Workflow 2: Parallel builds for multiple postgres versions
184+
185+
```bash
186+
# Build PG 15
187+
aws-vault exec dev -- nix run .#pg-ami-builder -- build phase1 \
188+
--postgres-version 15 \
189+
--state-file ~/.pg-ami-build/pg15.json
190+
191+
# Build PG 16 in parallel
192+
aws-vault exec dev -- nix run .#pg-ami-builder -- build phase1 \
193+
--postgres-version 16 \
194+
--state-file ~/.pg-ami-build/pg16.json
195+
196+
# SSH into PG 15 instance
197+
aws-vault exec dev -- nix run .#pg-ami-builder -- ssh \
198+
--state-file ~/.pg-ami-build/pg15.json
199+
```
200+
201+
## Troubleshooting
202+
203+
### SSM Connection Fails
204+
205+
1. Check SSM agent status on the instance
206+
2. Verify instance profile has SSM permissions
207+
3. Ensure session-manager-plugin is installed
208+
209+
### Ansible Fails
210+
211+
The instance is kept running on failure. Check logs:
212+
213+
```bash
214+
# SSH into instance
215+
nix run .#pg-ami-builder -- ssh
216+
217+
# Check ansible logs
218+
sudo journalctl -u ansible-provisioner
219+
```
220+
221+
### State File Issues
222+
223+
If state file references non-existent instance:
224+
225+
```bash
226+
# Override with specific instance
227+
nix run .#pg-ami-builder -- ssh --instance-id i-xxxxx
228+
229+
# Or clear state and start fresh
230+
rm ~/.pg-ami-build/state.json
231+
```
232+
233+
## Advanced Usage
234+
235+
### Custom State Files for Parallel Builds
236+
237+
Use `--state-file` to manage multiple builds:
238+
239+
```bash
240+
nix run .#pg-ami-builder -- build phase1 \
241+
--postgres-version 15 \
242+
--state-file ~/.pg-ami-build/custom.json
243+
```
244+
245+
### Additional Ansible Arguments
246+
247+
Pass custom arguments to ansible:
248+
249+
```bash
250+
nix run .#pg-ami-builder -- build phase1 \
251+
--postgres-version 15 \
252+
--ansible-args="--skip-tags=migrations"
253+
```
254+
255+
## State File
256+
257+
Location: `~/.pg-ami-build/state.json`
258+
259+
The state file tracks the current build instance, allowing subsequent commands to operate on the same instance without specifying `--instance-id`.
260+
261+
Example state:
262+
```json
263+
{
264+
"instance_id": "i-1234567890abcdef0",
265+
"phase": "phase1",
266+
"execution_id": "1731672000-15",
267+
"region": "us-east-1",
268+
"postgres_version": "15",
269+
"timestamp": "2025-11-15T10:30:00Z",
270+
"git_sha": "abc123def456"
271+
}
272+
```

nix/apps.nix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
update-readme = mkApp "update-readme" "update-readme";
2525
show-commands = mkApp "show-commands" "show-commands";
2626
build-test-ami = mkApp "build-test-ami" "build-test-ami";
27+
pg-ami-builder = mkApp "pg-ami-builder" "pg-ami-builder";
2728
run-testinfra = mkApp "run-testinfra" "run-testinfra";
2829
cleanup-ami = mkApp "cleanup-ami" "cleanup-ami";
2930
trigger-nix-build = mkApp "trigger-nix-build" "trigger-nix-build";

nix/checks.nix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,7 @@
395395
dbmate-tool
396396
packer
397397
pg_regress
398+
pg-ami-builder
398399
;
399400
}
400401
// pkgs.lib.optionalAttrs (system == "aarch64-linux") {

nix/devShells.nix

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,16 @@
4747
ansible-lint
4848
self'.packages.packer
4949

50+
# Go development tools
51+
go
52+
gopls
53+
gotools
54+
go-tools
55+
delve
56+
57+
# AWS tools
58+
awscli2
59+
5060
self'.packages.start-server
5161
self'.packages.start-client
5262
self'.packages.start-replica
@@ -55,6 +65,7 @@
5565
self'.packages.build-test-ami
5666
self'.packages.run-testinfra
5767
self'.packages.cleanup-ami
68+
self'.packages.pg-ami-builder
5869
dbmate
5970
nushell
6071
pythonEnv

nix/fmt.nix

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@
1111
package = pkgs.nixfmt-rfc-style;
1212
};
1313
ruff-format.enable = true;
14+
gofumpt = {
15+
enable = true;
16+
package = pkgs.gofumpt;
17+
};
1418
};
1519
};
1620
}

nix/packages/default.nix

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,12 @@
2828
in
2929
{
3030
packages = (
31-
{
31+
rec {
3232
build-test-ami = pkgs.callPackage ./build-test-ami.nix { };
33+
packer = pkgs.callPackage ./packer.nix { inherit inputs; };
34+
pg-ami-builder = inputs'.nixpkgs-go124.legacyPackages.callPackage ./pg-ami-builder.nix {
35+
inherit packer;
36+
};
3337
cleanup-ami = pkgs.callPackage ./cleanup-ami.nix { };
3438
dbmate-tool = pkgs.callPackage ./dbmate-tool.nix { inherit (self.supabase) defaults; };
3539
docs = pkgs.callPackage ./docs.nix { };
@@ -39,7 +43,6 @@
3943
mecab-naist-jdic = pkgs.callPackage ./mecab-naist-jdic.nix { };
4044
migrate-tool = pkgs.callPackage ./migrate-tool.nix { psql_15 = self'.packages."psql_15/bin"; };
4145
overlayfs-on-package = pkgs.callPackage ./overlayfs-on-package.nix { };
42-
packer = pkgs.callPackage ./packer.nix { inherit inputs; };
4346
pg-backrest = inputs.nixpkgs-pgbackrest.legacyPackages.${pkgs.system}.pgbackrest;
4447
pg-restore = pkgs.callPackage ./pg-restore.nix { psql_15 = self'.packages."psql_15/bin"; };
4548
pg_prove = pkgs.perlPackages.TAPParserSourceHandlerpgTAP;

0 commit comments

Comments
 (0)