Skip to content

Commit

Permalink
try coldsnap
Browse files Browse the repository at this point in the history
  • Loading branch information
arianvp committed Apr 19, 2024
1 parent e0faf6e commit c88a12e
Show file tree
Hide file tree
Showing 6 changed files with 167 additions and 9 deletions.
13 changes: 5 additions & 8 deletions .github/workflows/upload-legacy-ami.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,26 +47,23 @@ jobs:
run: |
image_info='${{ steps.download_ami.outputs.image_info }}'
images_bucket='${{ vars.IMAGES_BUCKET }}'
image_ids=$(nix run .#upload-ami -- \
image_id=$(nix run .#upload-coldsnap -- \
--image-info "$image_info" \
--prefix "smoketest/" \
--s3-bucket "$images_bucket")
echo "image_ids=$image_ids" >> "$GITHUB_OUTPUT"
--prefix "smoketest-coldsnap/")
echo "image_id=$image_id" >> "$GITHUB_OUTPUT"
- name: Smoke test
id: smoke_test
# NOTE: make sure smoke test isn't cancelled. Such that instance gets cleaned up.
run: |
image_ids='${{ steps.upload_smoke_test_ami.outputs.image_ids }}'
image_id=$(echo "$image_ids" | jq -r '.["${{ vars.AWS_REGION }}"]')
image_id='${{ steps.upload_smoke_test_ami.outputs.image_id }}'
run_id='${{ github.run_id }}'
nix run .#smoke-test -- --image-id "$image_id"
- name: Clean up smoke test
if: ${{ cancelled() }}
run: |
image_ids='${{ steps.upload_smoke_test_ami.outputs.image_ids }}'
image_id=$(echo "$image_ids" | jq -r '.["${{ vars.AWS_REGION }}"]')
image_id='${{ steps.upload_smoke_test_ami.outputs.image_id }}'
run_id='${{ github.run_id }}'
nix run .#smoke-test -- --image-id "$image_id" --cancel
Expand Down
11 changes: 11 additions & 0 deletions tf/iam_github_actions.tf
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,17 @@ data "aws_iam_policy_document" "upload_ami" {
]
resources = ["*"]
}
statement {
effect = "Allow"
actions = [
"ebs:StartSnapshot",
"ebs:PutSnapshotBlock",
"ebs:ListChangedBlocks",
"ebs:ListSnapshotBlocks",
"ebs:CompleteSnapshot",
]
resources = ["arn:aws:ec2:*:*:snapshot/*"]
}
statement {
effect = "Allow"
actions = [
Expand Down
4 changes: 4 additions & 0 deletions upload-ami/default.nix
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{ buildPythonApplication
, python3Packages
, lib
, coldsnap
}:

let
Expand Down Expand Up @@ -37,6 +38,9 @@ buildPythonApplication {
python3Packages.black
];


makeWrapperArgs = [ "--prefix PATH : ${coldsnap}/bin" ];

propagatedBuildInputs = lib.flatten (map resolvePackages pyproject.project.dependencies);

checkPhase = ''
Expand Down
1 change: 1 addition & 0 deletions upload-ami/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@ disable-image-block-public-access = "upload_ami.disable_image_block_public_acces
enable-regions = "upload_ami.enable_regions:main"
request-public-ami-quota-increase = "upload_ami.request_public_ami_quota_increase:main"
describe-images = "upload_ami.describe_images:main"
upload-coldsnap = "upload_ami.upload_coldsnap:main"
[tool.mypy]
strict=true
5 changes: 4 additions & 1 deletion upload-ami/src/upload_ami/upload_ami.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,15 @@
import botocore.exceptions

from mypy_boto3_ec2.client import EC2Client
from mypy_boto3_ec2.literals import BootModeValuesType
from mypy_boto3_ec2.type_defs import RegionTypeDef
from mypy_boto3_s3.client import S3Client

from concurrent.futures import ThreadPoolExecutor

from typing import TypedDict

from mypy_boto3_ec2.literals import BootModeValuesType


class ImageInfo(TypedDict):
file: str
Expand Down
142 changes: 142 additions & 0 deletions upload-ami/src/upload_ami/upload_coldsnap.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import argparse
import json
import logging
from re import I
import boto3
import subprocess
from typing import Literal, TypedDict
from mypy_boto3_ec2 import EC2Client
from mypy_boto3_ec2.literals import BootModeValuesType


class ImageInfo(TypedDict):
file: str
label: str
system: str
boot_mode: BootModeValuesType
format: str


def register_image_if_not_exists(
ec2: EC2Client,
image_name: str,
image_info: ImageInfo,
snapshot_id: str,
public: bool,
) -> str:
"""
Register image if it doesn't exist yet
This function is idempotent because image_name is unique
"""
describe_images = ec2.describe_images(
Owners=["self"], Filters=[{"Name": "name", "Values": [image_name]}]
)
if len(describe_images["Images"]) != 0:
assert len(describe_images["Images"]) == 1
assert "ImageId" in describe_images["Images"][0]
image_id = describe_images["Images"][0]["ImageId"]
else:
architecture: Literal["x86_64", "arm64"]
assert "system" in image_info
if image_info["system"] == "x86_64-linux":
architecture = "x86_64"
elif image_info["system"] == "aarch64-linux":
architecture = "arm64"
else:
raise Exception("Unknown system: " + image_info["system"])

logging.info(f"Registering image {image_name} with snapshot {snapshot_id}")

# TODO(arianvp): Not all instance types support TPM 2.0 yet. We should
# upload two images, one with and one without TPM 2.0 support.

# if architecture == "x86_64" and image_info["boot_mode"] == "uefi":
# tpmsupport['TpmSupport'] = "v2.0"

register_image = ec2.register_image(
Name=image_name,
Architecture=architecture,
BootMode=image_info["boot_mode"],
BlockDeviceMappings=[
{
"DeviceName": "/dev/xvda",
"Ebs": {
"SnapshotId": snapshot_id,
"VolumeType": "gp3",
},
}
],
RootDeviceName="/dev/xvda",
VirtualizationType="hvm",
EnaSupport=True,
ImdsSupport="v2.0",
SriovNetSupport="simple",
)
image_id = register_image["ImageId"]

ec2.get_waiter("image_available").wait(ImageIds=[image_id])
if public:
logging.info(f"Making {image_id} public")
ec2.modify_image_attribute(
ImageId=image_id,
Attribute="launchPermission",
LaunchPermission={"Add": [{"Group": "all"}]},
)
return image_id


def upload_coldsnap(
*,
image_info: ImageInfo,
prefix: str,
) -> str:
logging.info(f"Uploading image to coldsnap")

snapshot_id = str(
subprocess.check_output(
[
"coldsnap",
"upload",
"--wait",
]
)
)

ec2 = boto3.client("ec2")
image_name = prefix + image_info["label"] + "-" + image_info["system"]

image_id = register_image_if_not_exists(
ec2=ec2,
image_name=image_name,
image_info=image_info,
snapshot_id=snapshot_id,
public=False,
)
return image_id


def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument("--image-info", help="Path to image info", required=True)
parser.add_argument("--prefix", help="Prefix for image name", required=True)
parser.add_argument("--debug", action="store_true")

args = parser.parse_args()

if args.debug:
level = logging.DEBUG
else:
level = logging.INFO
logging.basicConfig(level=level)

image_info: ImageInfo
with open(args.image_info) as f:
image_info = json.load(f)

print(
upload_coldsnap(
image_info=args.image_info,
prefix=args.prefix,
)
)

0 comments on commit c88a12e

Please sign in to comment.