Publish AMI artifact IDs to SSM
This commit is contained in:
@@ -58,6 +58,14 @@ The final AMI is tagged with:
|
|||||||
- `NixSnapshotId`
|
- `NixSnapshotId`
|
||||||
- `NixosSystemClosure`
|
- `NixosSystemClosure`
|
||||||
|
|
||||||
|
The publisher also writes SSM parameters that point to the AMI ID:
|
||||||
|
|
||||||
|
- `/quixos/amis/<name>/<architecture>/recommended`
|
||||||
|
- `/quixos/amis/<name>/<architecture>/by-builder-rev/<rev>`
|
||||||
|
- `/quixos/amis/<name>/<architecture>/by-input-hash/<hash>`
|
||||||
|
|
||||||
|
Central services read the `recommended` parameter by default.
|
||||||
|
|
||||||
Do not deregister the AMI or delete either snapshot while runtime deployments
|
Do not deregister the AMI or delete either snapshot while runtime deployments
|
||||||
use it.
|
use it.
|
||||||
|
|
||||||
@@ -69,6 +77,13 @@ List published AMIs and cross-reference qx-deploy runtime registry records:
|
|||||||
nix run ./tofu/ami-builder#ami-artifacts -- list
|
nix run ./tofu/ami-builder#ami-artifacts -- list
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Publish SSM parameters for an existing AMI, useful for AMIs created before SSM
|
||||||
|
publication was added:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nix run ./tofu/ami-builder#ami-artifacts -- publish-ssm --ami-id ami-...
|
||||||
|
```
|
||||||
|
|
||||||
Delete an unused AMI and its root and `/nix` snapshots:
|
Delete an unused AMI and its root and `/nix` snapshots:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
+76
-5
@@ -10,14 +10,15 @@ const repoRoot = process.env.QUIXOS_REPO_ROOT ?? path.resolve(sourceScriptDir, "
|
|||||||
const amiBuilderDir = process.env.QUIXOS_AMI_BUILDER_DIR ?? path.resolve(repoRoot, "tofu/ami-builder");
|
const amiBuilderDir = process.env.QUIXOS_AMI_BUILDER_DIR ?? path.resolve(repoRoot, "tofu/ami-builder");
|
||||||
const centralDir = path.resolve(amiBuilderDir, "../central");
|
const centralDir = path.resolve(amiBuilderDir, "../central");
|
||||||
|
|
||||||
const usage = `Usage: nix run ./tofu/ami-builder#ami-artifacts -- <list|delete> [options]
|
const usage = `Usage: nix run ./tofu/ami-builder#ami-artifacts -- <list|delete|publish-ssm> [options]
|
||||||
|
|
||||||
Commands:
|
Commands:
|
||||||
list List published AMI artifacts and deployment references
|
list List published AMI artifacts and deployment references
|
||||||
delete --ami-id <ami> Deregister an AMI and delete its root and /nix snapshots
|
delete --ami-id <ami> Deregister an AMI and delete its root and /nix snapshots
|
||||||
|
publish-ssm --ami-id <ami> Publish an existing AMI into the SSM artifact registry
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
--ami-id <ami> AMI to delete
|
--ami-id <ami> AMI to operate on
|
||||||
--aws-region <region> AWS region (default: AWS_REGION or us-west-2)
|
--aws-region <region> AWS region (default: AWS_REGION or us-west-2)
|
||||||
--name <name> AMI project tag (default: nixos-zfs-ec2)
|
--name <name> AMI project tag (default: nixos-zfs-ec2)
|
||||||
--architecture <arm64|x86_64> Filter by architecture
|
--architecture <arm64|x86_64> Filter by architecture
|
||||||
@@ -62,7 +63,7 @@ const parseArgs = (argv) => {
|
|||||||
console.error(usage.trimEnd());
|
console.error(usage.trimEnd());
|
||||||
process.exit(command ? 0 : 1);
|
process.exit(command ? 0 : 1);
|
||||||
}
|
}
|
||||||
if (command !== "list" && command !== "delete") {
|
if (command !== "list" && command !== "delete" && command !== "publish-ssm") {
|
||||||
throw new Error(`Unknown command: ${command}\n\n${usage}`);
|
throw new Error(`Unknown command: ${command}\n\n${usage}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -287,6 +288,72 @@ const deleteArtifact = (state) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getImage = (state) => {
|
||||||
|
const result = awsJson([
|
||||||
|
"ec2",
|
||||||
|
"describe-images",
|
||||||
|
"--region",
|
||||||
|
state.awsRegion,
|
||||||
|
"--owners",
|
||||||
|
"self",
|
||||||
|
"--image-ids",
|
||||||
|
state.amiId,
|
||||||
|
]);
|
||||||
|
const image = result?.Images?.[0];
|
||||||
|
if (!image) {
|
||||||
|
throw new Error(`AMI not found: ${state.amiId}`);
|
||||||
|
}
|
||||||
|
return image;
|
||||||
|
};
|
||||||
|
|
||||||
|
const putSsmParameter = (state, name, value) => {
|
||||||
|
run("aws", [
|
||||||
|
"ssm",
|
||||||
|
"put-parameter",
|
||||||
|
"--region",
|
||||||
|
state.awsRegion,
|
||||||
|
"--name",
|
||||||
|
name,
|
||||||
|
"--type",
|
||||||
|
"String",
|
||||||
|
"--value",
|
||||||
|
value,
|
||||||
|
"--overwrite",
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const publishSsmArtifact = (state) => {
|
||||||
|
if (!state.amiId) {
|
||||||
|
throw new Error("Missing --ami-id");
|
||||||
|
}
|
||||||
|
|
||||||
|
const image = getImage(state);
|
||||||
|
const tags = amiTags(image);
|
||||||
|
const architecture = tags.Architecture ?? image.Architecture ?? "";
|
||||||
|
const builderGitRev = tags.BuilderGitRev ?? "";
|
||||||
|
const inputHash = tags.InputHash ?? "";
|
||||||
|
if (!architecture) {
|
||||||
|
throw new Error(`AMI ${state.amiId} is missing Architecture metadata`);
|
||||||
|
}
|
||||||
|
if (!builderGitRev) {
|
||||||
|
throw new Error(`AMI ${state.amiId} is missing BuilderGitRev tag`);
|
||||||
|
}
|
||||||
|
if (!inputHash) {
|
||||||
|
throw new Error(`AMI ${state.amiId} is missing InputHash tag`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const prefix = `/quixos/amis/${state.name}/${architecture}`;
|
||||||
|
const parameters = [
|
||||||
|
`${prefix}/recommended`,
|
||||||
|
`${prefix}/by-builder-rev/${builderGitRev}`,
|
||||||
|
`${prefix}/by-input-hash/${inputHash}`,
|
||||||
|
];
|
||||||
|
for (const parameter of parameters) {
|
||||||
|
console.error(`Writing ${parameter} = ${state.amiId}`);
|
||||||
|
putSsmParameter(state, parameter, state.amiId);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const main = () => {
|
const main = () => {
|
||||||
const state = parseArgs(process.argv.slice(2));
|
const state = parseArgs(process.argv.slice(2));
|
||||||
requireCommand("aws");
|
requireCommand("aws");
|
||||||
@@ -296,6 +363,10 @@ const main = () => {
|
|||||||
listArtifacts(state);
|
listArtifacts(state);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (state.command === "publish-ssm") {
|
||||||
|
publishSsmArtifact(state);
|
||||||
|
return;
|
||||||
|
}
|
||||||
deleteArtifact(state);
|
deleteArtifact(state);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+18
-1
@@ -271,6 +271,20 @@ aws ec2 create-tags \
|
|||||||
"Key=RootSnapshotId,Value=${root_snapshot_id}" \
|
"Key=RootSnapshotId,Value=${root_snapshot_id}" \
|
||||||
"Key=NixSnapshotId,Value=${nix_snapshot_id}"
|
"Key=NixSnapshotId,Value=${nix_snapshot_id}"
|
||||||
|
|
||||||
|
ssm_ami_prefix="/quixos/amis/${name}/${architecture}"
|
||||||
|
for ssm_ami_parameter_name in \
|
||||||
|
"${ssm_ami_prefix}/recommended" \
|
||||||
|
"${ssm_ami_prefix}/by-builder-rev/${builder_git_rev}" \
|
||||||
|
"${ssm_ami_prefix}/by-input-hash/${input_hash}"
|
||||||
|
do
|
||||||
|
aws ssm put-parameter \
|
||||||
|
--region "$aws_region" \
|
||||||
|
--name "$ssm_ami_parameter_name" \
|
||||||
|
--type String \
|
||||||
|
--value "$image_id" \
|
||||||
|
--overwrite >/dev/null
|
||||||
|
done
|
||||||
|
|
||||||
aws ec2 deregister-image --region "$aws_region" --image-id "$capture_image_id" >/dev/null
|
aws ec2 deregister-image --region "$aws_region" --image-id "$capture_image_id" >/dev/null
|
||||||
|
|
||||||
jq -n \
|
jq -n \
|
||||||
@@ -285,6 +299,7 @@ jq -n \
|
|||||||
--arg rootDeviceName "$root_device_name" \
|
--arg rootDeviceName "$root_device_name" \
|
||||||
--arg nixDeviceName "$nix_device_name" \
|
--arg nixDeviceName "$nix_device_name" \
|
||||||
--arg nixPool "$nix_pool" \
|
--arg nixPool "$nix_pool" \
|
||||||
|
--arg ssmRecommendedParameter "${ssm_ami_prefix}/recommended" \
|
||||||
'{
|
'{
|
||||||
architecture: $architecture,
|
architecture: $architecture,
|
||||||
system: $system,
|
system: $system,
|
||||||
@@ -296,7 +311,8 @@ jq -n \
|
|||||||
nixSnapshotId: $nixSnapshotId,
|
nixSnapshotId: $nixSnapshotId,
|
||||||
rootDeviceName: $rootDeviceName,
|
rootDeviceName: $rootDeviceName,
|
||||||
nixDeviceName: $nixDeviceName,
|
nixDeviceName: $nixDeviceName,
|
||||||
nixPool: $nixPool
|
nixPool: $nixPool,
|
||||||
|
ssmRecommendedParameter: $ssmRecommendedParameter
|
||||||
}' > artifact.json
|
}' > artifact.json
|
||||||
|
|
||||||
if [ "$keep_builder" = false ]; then
|
if [ "$keep_builder" = false ]; then
|
||||||
@@ -313,3 +329,4 @@ if [ "$keep_builder" = false ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Published AMI: $image_id"
|
echo "Published AMI: $image_id"
|
||||||
|
echo "Published SSM parameter: ${ssm_ami_prefix}/recommended"
|
||||||
|
|||||||
Reference in New Issue
Block a user