Skip to content

Commit 4ff207a

Browse files
authored
feat: iterate on ami, packer, ansible, ssh to investigate (#1928)
* feat: iterate on ami, packer, ansible, ssh to investigate * chore: remove stray char that I had put in to force failure for testing * fix: cleaning up function of running phases etc * docs: update docs
1 parent 5935952 commit 4ff207a

35 files changed

+3169
-9
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/

docs/ami-local-development.md

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

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/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)