973fa21b52
be used on HVM instances without needing nixops. Previously the grub setup was incorrect, so a plain 'nixos-rebuild switch' and a reboot would result in a broken system. Also added growing of the partition of the root disk in the initrd, so you can run resize2fs after initial boot, without needing an extra reboot. This is useful especially for nixops' deployment.ec2.ebsInitialRootDiskSize option. (cherry picked from commit 044a24e58bcf4cf48df02df936c542839fb08d90)
226 lines
8.5 KiB
Python
Executable file
226 lines
8.5 KiB
Python
Executable file
#! /usr/bin/env python
|
|
|
|
import os
|
|
import sys
|
|
import time
|
|
import argparse
|
|
import nixops.util
|
|
from nixops import deployment
|
|
from boto.ec2.blockdevicemapping import BlockDeviceMapping, BlockDeviceType
|
|
import boto.ec2
|
|
from nixops.statefile import StateFile, get_default_state_file
|
|
|
|
parser = argparse.ArgumentParser(description='Create an EBS-backed NixOS AMI')
|
|
parser.add_argument('--region', dest='region', required=True, help='EC2 region to create the image in')
|
|
parser.add_argument('--channel', dest='channel', default="13.10", help='Channel to use')
|
|
parser.add_argument('--keep', dest='keep', action='store_true', help='Keep NixOps machine after use')
|
|
parser.add_argument('--hvm', dest='hvm', action='store_true', help='Create HVM image')
|
|
parser.add_argument('--key', dest='key_name', action='store_true', help='Keypair used for HVM instance creation', default="rob")
|
|
args = parser.parse_args()
|
|
|
|
instance_type = "m3.medium" if args.hvm else "m1.small"
|
|
ebs_size = 8 if args.hvm else 20
|
|
|
|
|
|
# Start a NixOS machine in the given region.
|
|
f = open("ebs-creator-config.nix", "w")
|
|
f.write('''{{
|
|
resources.ec2KeyPairs.keypair.accessKeyId = "logicblox-dev";
|
|
resources.ec2KeyPairs.keypair.region = "{0}";
|
|
|
|
machine =
|
|
{{ pkgs, ... }}:
|
|
{{
|
|
deployment.ec2.accessKeyId = "logicblox-dev";
|
|
deployment.ec2.region = "{0}";
|
|
deployment.ec2.blockDeviceMapping."/dev/xvdg".size = pkgs.lib.mkOverride 10 {1};
|
|
}};
|
|
}}
|
|
'''.format(args.region, ebs_size))
|
|
f.close()
|
|
|
|
db = StateFile(get_default_state_file())
|
|
try:
|
|
depl = db.open_deployment("ebs-creator")
|
|
except Exception:
|
|
depl = db.create_deployment()
|
|
depl.name = "ebs-creator"
|
|
depl.auto_response = "y"
|
|
depl.nix_exprs = [os.path.abspath("./ebs-creator.nix"), os.path.abspath("./ebs-creator-config.nix")]
|
|
if not args.keep: depl.destroy_resources()
|
|
depl.deploy(allow_reboot=True)
|
|
|
|
m = depl.machines['machine']
|
|
|
|
# Do the installation.
|
|
device="/dev/xvdg"
|
|
if args.hvm:
|
|
m.run_command('parted -s /dev/xvdg -- mklabel msdos')
|
|
m.run_command('parted -s /dev/xvdg -- mkpart primary ext2 1M -1s')
|
|
device="/dev/xvdg1"
|
|
|
|
m.run_command("if mountpoint -q /mnt; then umount /mnt; fi")
|
|
m.run_command("mkfs.ext4 -L nixos {0}".format(device))
|
|
m.run_command("mkdir -p /mnt")
|
|
m.run_command("mount {0} /mnt".format(device))
|
|
m.run_command("touch /mnt/.ebs")
|
|
m.run_command("mkdir -p /mnt/etc/nixos")
|
|
|
|
m.run_command("nix-channel --add http://nixos.org/channels/nixos-{} nixos".format(args.channel))
|
|
m.run_command("nix-channel --update")
|
|
|
|
version = m.run_command("nix-instantiate --eval-only -A lib.nixpkgsVersion '<nixpkgs>'", capture_stdout=True).split(' ')[0].replace('"','').strip()
|
|
print >> sys.stderr, "NixOS version is {0}".format(version)
|
|
if args.hvm:
|
|
m.upload_file("./amazon-base-config.nix", "/mnt/etc/nixos/amazon-base-config.nix")
|
|
m.upload_file("./amazon-hvm-config.nix", "/mnt/etc/nixos/configuration.nix")
|
|
m.upload_file("./amazon-hvm-install-config.nix", "/mnt/etc/nixos/amazon-hvm-install-config.nix")
|
|
m.run_command("NIXOS_CONFIG=/etc/nixos/amazon-hvm-install-config.nix nixos-install")
|
|
m.run_command('nix-env -iA nixos.pkgs.grub')
|
|
m.run_command('cp /nix/store/*-grub-0.97*/lib/grub/i386-pc/* /mnt/boot/grub')
|
|
m.run_command('echo "(hd1) /dev/xvdg" > device.map')
|
|
m.run_command('echo -e "root (hd1,0)\nsetup (hd1)" | grub --device-map=device.map --batch')
|
|
else:
|
|
m.upload_file("./amazon-base-config.nix", "/mnt/etc/nixos/configuration.nix")
|
|
m.run_command("nixos-install")
|
|
|
|
m.run_command("umount /mnt")
|
|
|
|
if args.hvm:
|
|
ami_name = "nixos-{0}-x86_64-ebs-hvm".format(version)
|
|
description = "NixOS {0} (x86_64; EBS root; hvm)".format(version)
|
|
else:
|
|
ami_name = "nixos-{0}-x86_64-ebs".format(version)
|
|
description = "NixOS {0} (x86_64; EBS root)".format(version)
|
|
|
|
|
|
# Wait for the snapshot to finish.
|
|
def check():
|
|
status = snapshot.update()
|
|
print >> sys.stderr, "snapshot status is {0}".format(status)
|
|
return status == '100%'
|
|
|
|
m.connect()
|
|
volume = m._conn.get_all_volumes([], filters={'attachment.instance-id': m.resource_id, 'attachment.device': "/dev/sdg"})[0]
|
|
if args.hvm:
|
|
instance = m._conn.run_instances( image_id="ami-5f491f36"
|
|
, instance_type=instance_type
|
|
, key_name=args.key_name
|
|
, placement=m.zone
|
|
, security_groups=["eelco-test"]).instances[0]
|
|
nixops.util.check_wait(lambda: instance.update() == 'running', max_tries=120)
|
|
instance.stop()
|
|
nixops.util.check_wait(lambda: instance.update() == 'stopped', max_tries=120)
|
|
old_root_volume = m._conn.get_all_volumes([], filters={'attachment.instance-id': instance.id, 'attachment.device': "/dev/sda1"})[0]
|
|
old_root_volume.detach()
|
|
volume.detach()
|
|
nixops.util.check_wait(lambda: volume.update() == 'available', max_tries=120)
|
|
nixops.util.check_wait(lambda: old_root_volume.update() == 'available', max_tries=120)
|
|
volume.attach(instance.id, '/dev/sda1')
|
|
nixops.util.check_wait(lambda: volume.update() == 'in-use', max_tries=120)
|
|
|
|
ami_id = m._conn.create_image(instance.id, ami_name, description)
|
|
time.sleep(5)
|
|
image = m._conn.get_all_images([ami_id])[0]
|
|
nixops.util.check_wait(lambda: image.update() == 'available', max_tries=120)
|
|
instance.terminate()
|
|
|
|
else:
|
|
# Create a snapshot.
|
|
snapshot = volume.create_snapshot(description=description)
|
|
print >> sys.stderr, "created snapshot {0}".format(snapshot.id)
|
|
|
|
nixops.util.check_wait(check, max_tries=120)
|
|
|
|
m._conn.create_tags([snapshot.id], {'Name': ami_name})
|
|
|
|
if not args.keep: depl.destroy_resources()
|
|
|
|
# Register the image.
|
|
aki = m._conn.get_all_images(filters={'manifest-location': '*pv-grub-hd0_1.03-x86_64*'})[0]
|
|
print >> sys.stderr, "using kernel image {0} - {1}".format(aki.id, aki.location)
|
|
|
|
block_map = BlockDeviceMapping()
|
|
block_map['/dev/sda'] = BlockDeviceType(snapshot_id=snapshot.id, delete_on_termination=True)
|
|
block_map['/dev/sdb'] = BlockDeviceType(ephemeral_name="ephemeral0")
|
|
block_map['/dev/sdc'] = BlockDeviceType(ephemeral_name="ephemeral1")
|
|
block_map['/dev/sdd'] = BlockDeviceType(ephemeral_name="ephemeral2")
|
|
block_map['/dev/sde'] = BlockDeviceType(ephemeral_name="ephemeral3")
|
|
|
|
ami_id = m._conn.register_image(
|
|
name=ami_name,
|
|
description=description,
|
|
architecture="x86_64",
|
|
root_device_name="/dev/sda",
|
|
kernel_id=aki.id,
|
|
block_device_map=block_map)
|
|
|
|
print >> sys.stderr, "registered AMI {0}".format(ami_id)
|
|
|
|
print >> sys.stderr, "sleeping a bit..."
|
|
time.sleep(30)
|
|
|
|
print >> sys.stderr, "setting image name..."
|
|
m._conn.create_tags([ami_id], {'Name': ami_name})
|
|
|
|
print >> sys.stderr, "making image public..."
|
|
image = m._conn.get_all_images(image_ids=[ami_id])[0]
|
|
image.set_launch_permissions(user_ids=[], group_names=["all"])
|
|
|
|
# Do a test deployment to make sure that the AMI works.
|
|
f = open("ebs-test.nix", "w")
|
|
f.write(
|
|
'''
|
|
{{
|
|
network.description = "NixOS EBS test";
|
|
|
|
resources.ec2KeyPairs.keypair.accessKeyId = "logicblox-dev";
|
|
resources.ec2KeyPairs.keypair.region = "{0}";
|
|
|
|
machine = {{ config, pkgs, resources, ... }}: {{
|
|
deployment.targetEnv = "ec2";
|
|
deployment.ec2.accessKeyId = "logicblox-dev";
|
|
deployment.ec2.region = "{0}";
|
|
deployment.ec2.instanceType = "{2}";
|
|
deployment.ec2.keyPair = resources.ec2KeyPairs.keypair.name;
|
|
deployment.ec2.securityGroups = [ "admin" ];
|
|
deployment.ec2.ami = "{1}";
|
|
}};
|
|
}}
|
|
'''.format(args.region, ami_id, instance_type))
|
|
f.close()
|
|
|
|
test_depl = db.create_deployment()
|
|
test_depl.auto_response = "y"
|
|
test_depl.name = "ebs-creator-test"
|
|
test_depl.nix_exprs = [os.path.abspath("./ebs-test.nix")]
|
|
test_depl.deploy(create_only=True)
|
|
test_depl.machines['machine'].run_command("nixos-version")
|
|
|
|
if args.hvm:
|
|
image_type = 'hvm'
|
|
else:
|
|
image_type = 'ebs'
|
|
|
|
# Log the AMI ID.
|
|
f = open("{0}.{1}.ami-id".format(args.region, image_type), "w")
|
|
f.write("{0}".format(ami_id))
|
|
f.close()
|
|
|
|
for dest in [ 'us-east-1', 'us-west-1', 'us-west-2', 'eu-west-1', 'ap-southeast-1', 'ap-southeast-2', 'ap-northeast-1', 'sa-east-1']:
|
|
if args.region != dest:
|
|
print >> sys.stderr, "copying image from region {0} to {1}".format(args.region, dest)
|
|
conn = boto.ec2.connect_to_region(dest)
|
|
copy_image = conn.copy_image(args.region, ami_id, ami_name, description=None, client_token=None)
|
|
|
|
# Log the AMI ID.
|
|
f = open("{0}.{1}.ami-id".format(dest, image_type), "w")
|
|
f.write("{0}".format(copy_image.image_id))
|
|
f.close()
|
|
|
|
|
|
if not args.keep:
|
|
test_depl.destroy_resources()
|
|
test_depl.delete()
|
|
|