├── .gitignore ├── plugins ├── admin-user-tasks │ ├── add-sudo-package │ └── create-user ├── lvm-root-volume-tasks │ ├── 72-unmount-boot │ ├── 73-deactivate-vg │ ├── 01-lvm-package │ ├── 52-replace-expand-volume │ ├── 31-fstab │ ├── 30-grub │ ├── 13-mount-volume │ ├── 12-partition-and-format-volume │ ├── files │ │ ├── expand-lvm-volume │ │ └── 40_custom │ └── 95-register-ami ├── unattended-upgrades-tasks │ ├── add-unattended-upgrades-package │ └── create-02periodic ├── publish-ami ├── publish-ami-tasks │ └── grant-launch-permission ├── standard-packages ├── publish-snapshot ├── remount ├── build-metadata ├── publish-snapshot-tasks │ ├── grant-create-volume-permission │ └── euca-modify-snapshot-attribute ├── standard-packages-tasks │ └── add-standard-packages ├── admin-user ├── build-metadata-tasks │ └── create-build-metadata ├── unattended-upgrades ├── remount-tasks │ └── remount-volume ├── lvm-root-volume ├── README.md └── HOWTO.md ├── tasks ├── ec2 │ ├── 82-delete-volume │ ├── 39-dhcpcd-conf │ ├── 73-detach-volume │ ├── wheezy │ │ └── 01-packages-kernels │ ├── squeeze │ │ └── 01-packages-kernels │ ├── 50-ec2-scripts │ ├── 10-create-volume │ ├── 31-fstab │ ├── 80-ebs-snapshot │ ├── 01-packages-ec2 │ ├── 30-grub │ ├── 13-mount-volume │ ├── 06-patch-boto │ ├── 11-attach-volume │ ├── 05-install-euca2ools │ ├── 07-host-information │ └── 95-register-ami ├── gce │ ├── 12-label-volume │ ├── 01-packages-gce │ ├── 80-save-image │ ├── 40-get-release-info │ ├── 85-delete-workspace │ ├── 10-create-volume │ ├── 75-compress-image │ ├── 25-install-gce-debs │ ├── 25-install-backports-packages │ ├── 39-set-hostname │ ├── 32-install-kernel │ ├── 37-fix-ntp │ ├── 31-fstab │ ├── 73-delete-loopback │ ├── 70-finalize-grub │ ├── 35-disable-ipv6 │ ├── 21-apt-sources-goog │ ├── 15-prepare-backport-sources │ ├── 26-remove-goog-apt-source │ ├── 15-prefer-backports-kernel-and-ssh │ ├── 11-create-loopback │ ├── 13-mount-volume │ ├── 30-grub │ ├── 25-install-cloudsdk │ ├── 09-create-workspace │ └── 95-register-image ├── wheezy │ ├── 50-add-init-scripts │ ├── 39-networking-interfaces │ └── 62-delete-host-keys ├── squeeze │ ├── 50-add-init-scripts │ ├── 51-init-scripts-hwclockfirst │ ├── 39-networking-interfaces │ └── 62-delete-host-keys ├── 39-networking ├── 32-blacklist ├── 04-install-host-packages ├── 72-unmount-volume ├── 61-apt-cleanup ├── 12-format-volume ├── 51-init-scripts ├── 20-locales ├── 38-security ├── 03-build-info ├── 21-apt-sources ├── 33-disable-gettys ├── 22-apt-upgrade ├── 60-cleanup ├── 01-packages ├── 15-mount-specials ├── 71-unmount-specials └── 14-bootstrap ├── LICENSE ├── LICENSE.ec2ubuntu ├── init.d ├── expand-volume ├── squeeze │ └── generate-ssh-hostkeys ├── wheezy │ └── generate-ssh-hostkeys ├── ec2-get-credentials └── ec2-run-user-data ├── regression-checklist.md ├── grub.d └── 40_custom ├── README.md ├── utils ├── gce ├── ec2 └── CHANGELOG.md /.gitignore: -------------------------------------------------------------------------------- 1 | euca2ools-* 2 | bootstrap-[a-z0-9]*.tar 3 | .DS_Store 4 | 5 | -------------------------------------------------------------------------------- /plugins/admin-user-tasks/add-sudo-package: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | packages+=('sudo') 3 | -------------------------------------------------------------------------------- /plugins/lvm-root-volume-tasks/72-unmount-boot: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Unmount the boot partition 3 | umount $imagedir/boot 4 | -------------------------------------------------------------------------------- /plugins/unattended-upgrades-tasks/add-unattended-upgrades-package: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | packages+=('unattended-upgrades') 3 | -------------------------------------------------------------------------------- /plugins/lvm-root-volume-tasks/73-deactivate-vg: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Deactivate the VG before detaching 3 | 4 | vgchange -an vg_root -------------------------------------------------------------------------------- /plugins/lvm-root-volume-tasks/01-lvm-package: -------------------------------------------------------------------------------- 1 | packages+=('lvm2') 2 | packages+=('parted') 3 | 4 | host_packages+=('lvm2') 5 | 6 | -------------------------------------------------------------------------------- /plugins/publish-ami: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | insert_task_after $TASK_CREATE_AMI \ 3 | $plugindir/publish-ami-tasks/grant-launch-permission 4 | -------------------------------------------------------------------------------- /plugins/publish-ami-tasks/grant-launch-permission: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | euca-modify-image-attribute --launch-permission --add all "$ami_id" 3 | -------------------------------------------------------------------------------- /tasks/ec2/82-delete-volume: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Delete the EBS volume 3 | log "Deleting the volume" 4 | euca-delete-volume $volume_id 5 | -------------------------------------------------------------------------------- /plugins/standard-packages: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | insert_task_after $TASK_PACKAGES \ 3 | $plugindir/standard-packages-tasks/add-standard-packages 4 | -------------------------------------------------------------------------------- /plugins/publish-snapshot: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | insert_task_after $TASK_SNAPSHOT \ 3 | $plugindir/publish-snapshot-tasks/grant-create-volume-permission 4 | -------------------------------------------------------------------------------- /plugins/remount: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | remove_task $TASK_DELETE_VOLUME 3 | insert_task_after $TASK_CREATE_AMI \ 4 | $plugindir/remount-tasks/remount-volume 5 | -------------------------------------------------------------------------------- /tasks/gce/12-label-volume: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Label volume 3 | 4 | case $filesystem in 5 | # Add a fs label 6 | ext*) e2label $device_path / ;; 7 | esac 8 | -------------------------------------------------------------------------------- /plugins/build-metadata: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | true ${BUILD_METADATA:?} 3 | insert_task_after $TASK_CREATE_AMI \ 4 | $plugindir/build-metadata-tasks/create-build-metadata 5 | -------------------------------------------------------------------------------- /plugins/publish-snapshot-tasks/grant-create-volume-permission: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | $taskdir/euca-modify-snapshot-attribute --create-volume-permission --add all "$snapshot_id" 3 | -------------------------------------------------------------------------------- /tasks/ec2/39-dhcpcd-conf: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # let the DHCP client manage /etc/resolv.conf 4 | sed -i -e "s/^#*SET_DNS=.*/SET_DNS='yes'/" $imagedir/etc/default/dhcpcd 5 | -------------------------------------------------------------------------------- /tasks/ec2/73-detach-volume: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Detach the EBS volume 3 | euca-detach-volume $volume_id 4 | dotdot "euca-describe-volumes $volume_id | grep 'available'" 5 | -------------------------------------------------------------------------------- /tasks/gce/01-packages-gce: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | packages+=('python') 3 | packages+=('sudo') 4 | packages+=('ntp') 5 | packages+=('lsb-release') 6 | packages+=('acpi-support-base') 7 | -------------------------------------------------------------------------------- /tasks/wheezy/50-add-init-scripts: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Add standard startup scripts to the init_scripts list 3 | init_scripts+=("$scriptdir/init.d/wheezy/generate-ssh-hostkeys") 4 | -------------------------------------------------------------------------------- /tasks/gce/80-save-image: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Move the image file out of our workspace 3 | 4 | log "Moving $tarball_name to $originaldir" 5 | mv $workspace/$tarball_name $originaldir 6 | -------------------------------------------------------------------------------- /tasks/squeeze/50-add-init-scripts: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Add standard startup scripts to the init_scripts list 3 | init_scripts+=("$scriptdir/init.d/squeeze/generate-ssh-hostkeys") 4 | -------------------------------------------------------------------------------- /tasks/squeeze/51-init-scripts-hwclockfirst: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # squeeze has the hwclockfirst.sh script, which wheezy does not have 3 | chroot $imagedir insserv -r hwclockfirst.sh 4 | -------------------------------------------------------------------------------- /plugins/standard-packages-tasks/add-standard-packages: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | packages+=('lsb-release') 3 | packages+=('psmisc') 4 | packages+=('less') 5 | packages+=('vim') 6 | packages+=('emacs') 7 | -------------------------------------------------------------------------------- /tasks/39-networking: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Basic networking 3 | 4 | # Delete the hostname file and DNS info, we get it from the ec2 dhcp server 5 | rm -f $imagedir/etc/hostname $imagedir/etc/resolv.conf 6 | -------------------------------------------------------------------------------- /tasks/gce/40-get-release-info: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | lsb_release="$(chroot $imagedir lsb_release -r -s)" 4 | lsb_description="$(chroot $imagedir lsb_release -d -s)" 5 | major_version=${lsb_release%%.*} 6 | -------------------------------------------------------------------------------- /plugins/admin-user: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | insert_task_after $TASK_PACKAGES \ 3 | $plugindir/admin-user-tasks/add-sudo-package 4 | insert_task_after $TASK_INITSCRIPTS \ 5 | $plugindir/admin-user-tasks/create-user 6 | -------------------------------------------------------------------------------- /plugins/build-metadata-tasks/create-build-metadata: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cat < $BUILD_METADATA 3 | AMI_ID=$ami_id 4 | AMI_NAME=$ami_name 5 | SNAPSHOT_ID=$snapshot_id 6 | EOF 7 | log "AMI metadata recorded at $buildmetadata" 8 | -------------------------------------------------------------------------------- /tasks/32-blacklist: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Blacklist 3 | 4 | # Prevent pcspkr from loading because we don't need it 5 | cat >> $imagedir/etc/modprobe.d/blacklist.conf <> $imagedir/etc/network/interfaces <> $imagedir/etc/network/interfaces <> $imagedir/etc/hostname 8 | -------------------------------------------------------------------------------- /tasks/12-format-volume: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Format the volume 3 | 4 | log "Formatting the device $device_path with $filesystem" 5 | mkfs.$filesystem $device_path 6 | 7 | case $filesystem in 8 | # Disable time based fs checks 9 | ext*) tune2fs -i 0 $device_path ;; 10 | # Add some tools for xfs, ext tools are built-in 11 | xfs) packages+=('xfsprogs') ;; 12 | esac 13 | -------------------------------------------------------------------------------- /tasks/gce/32-install-kernel: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Google Compute Engine doesn't use Xen, so all working versions of Debian use 3 | # the 'linux-image-$flavor' package. 4 | [ $arch = 'amd64' ] && kernel_package='linux-image-amd64' 5 | [ $arch = 'i386' ] && kernel_package='linux-image-686' 6 | 7 | chroot $imagedir apt-get -q -y --no-install-recommends ${kernel_install_opts} install ${kernel_package} 8 | -------------------------------------------------------------------------------- /tasks/wheezy/62-delete-host-keys: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Remove the ssh host keys, they need to be shredded 3 | shred --remove \ 4 | $imagedir/etc/ssh/ssh_host_dsa_key \ 5 | $imagedir/etc/ssh/ssh_host_dsa_key.pub \ 6 | $imagedir/etc/ssh/ssh_host_rsa_key \ 7 | $imagedir/etc/ssh/ssh_host_rsa_key.pub \ 8 | $imagedir/etc/ssh/ssh_host_ecdsa_key \ 9 | $imagedir/etc/ssh/ssh_host_ecdsa_key.pub 10 | -------------------------------------------------------------------------------- /tasks/51-init-scripts: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Install startup scripts. 3 | for script in ${init_scripts[*]}; do 4 | scriptname=`basename $script` 5 | cp "$script" "$imagedir/etc/init.d/$scriptname" 6 | chmod 755 "$imagedir/etc/init.d/$scriptname" 7 | chroot $imagedir insserv -d "$scriptname" 8 | done 9 | 10 | # Inside Xen, CMOS clock is irrelevant, so save seconds at boot 11 | chroot $imagedir insserv -r hwclock.sh 12 | -------------------------------------------------------------------------------- /tasks/gce/37-fix-ntp: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Change NTP to use server located on the host machine instead of Debian pool 4 | 5 | configuration_file=etc/ntp.conf 6 | 7 | sed --in-place '/^server\s\+/d' $imagedir/$configuration_file 8 | echo "# Use server located on the host machine to avoid network traffic" >>$imagedir/$configuration_file 9 | echo "server metadata.google.internal iburst" >>$imagedir/$configuration_file 10 | 11 | -------------------------------------------------------------------------------- /tasks/20-locales: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Set locale 3 | # Uncomment the chosen locale 4 | sed -i "s/# $locale.$charmap $charmap/$locale.$charmap $charmap/" $imagedir/etc/locale.gen 5 | # Generate the locale 6 | chroot $imagedir dpkg-reconfigure --priority=critical locales 7 | 8 | # Set timezone 9 | cat > $imagedir/etc/timezone <> $imagedir/etc/fstab <> $imagedir/etc/fstab <> $imagedir/etc/ssh/sshd_config <> $imagedir/etc/sysctl.d/70-disable-ipv6.conf < $imagedir/etc/apt/sources.list.d/goog.list < $imagedir/etc/apt/sources.list 5 | 6 | for apt_mirror in $apt_mirrors; do 7 | cat >> $imagedir/etc/apt/sources.list <> $imagedir/etc/apt/sources.list <> $imagedir/etc/apt/sources.list.d/backports.list <> $imagedir/etc/apt/preferences.d/backports.pref < 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /tasks/ec2/13-mount-volume: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Mount the EBS volume 3 | imagedir=$debootstrap_dir/${volume_id} 4 | 5 | # Fail if the imagedir exists, it should be quite unique. 6 | # This guarantees that we later on can delete it without having to check for anything. 7 | if [ -d "$imagedir" ]; then 8 | die "The mount location $imagedir already exists." 9 | fi 10 | # Create the dir we are going to mount the volume onto 11 | mkdir -p $imagedir 12 | 13 | [ -n "`mount | grep "$imagedir"`" ] && die "Something is already mounted on $imagedir" 14 | 15 | mount $device_path $imagedir 16 | log "The volume is mounted at ${imagedir}." 17 | -------------------------------------------------------------------------------- /plugins/lvm-root-volume-tasks/31-fstab: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Create the fstab 3 | 4 | # Add some mount options depending on the filesystem 5 | case $filesystem in 6 | ext2) mountoptions+=',barrier=0' ;; 7 | ext3) mountoptions+=',barrier=0' ;; 8 | ext4) mountoptions+=',barrier=0' ;; 9 | xfs) mountoptions+=',nobarrier' ;; 10 | *) ;; 11 | esac 12 | 13 | cat >> $imagedir/etc/fstab < $imagedir/usr/sbin/policy-rc.d << EOF 6 | #!/bin/sh 7 | exit 101 8 | EOF 9 | chmod 755 $imagedir/usr/sbin/policy-rc.d 10 | 11 | # Update the package cache 12 | chroot $imagedir apt-get update | spin 13 | [ $PIPESTATUS == 0 ] || die "apt-get update failed!" 14 | 15 | # Fix broken dependencies, if any. 16 | chroot $imagedir apt-get -f install -q -y 17 | 18 | # Upgrade existing packages 19 | chroot $imagedir apt-get -q -y upgrade | spin 20 | [ $PIPESTATUS == 0 ] || die "apt-get upgrade failed!" 21 | -------------------------------------------------------------------------------- /LICENSE.ec2ubuntu: -------------------------------------------------------------------------------- 1 | Copyright 2008-2011 Eric Hammond 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /tasks/ec2/06-patch-boto: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # boto has bug when registering images, we fix it with this 3 | function apply_boto_patch { 4 | local boto_ec2_dir='/usr/share/pyshared/boto/ec2' 5 | local patch_url='https://bugzilla.redhat.com/attachment.cgi?id=455857' 6 | if [ -r "$boto_ec2_dir/blockdevicemapping.py" ]; then 7 | local result=$( 8 | grep -q "pre = '%sBlockDeviceMapping.%d' % (pre, i)" $boto_ec2_dir/blockdevicemapping.py 9 | echo $?) 10 | if [ $result -eq 0 ]; then 11 | wget -qO - $patch_url | patch -sfr - -d $boto_ec2_dir 12 | [ $PIPESTATUS == 0 ] || die "Unable to patch boto." 13 | fi 14 | fi 15 | } 16 | apply_boto_patch 17 | -------------------------------------------------------------------------------- /plugins/lvm-root-volume-tasks/30-grub: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Disable normal grub 2 cfg files 3 | chmod -x $imagedir/etc/grub.d/* 4 | 5 | # Install the 40_custom file, which generates a nice menu.lst 6 | script='40_custom' 7 | cp "$taskdir/files/$script" "$imagedir/etc/grub.d/$script" 8 | chmod 755 "$imagedir/etc/grub.d/$script" 9 | 10 | sed -i "s/^GRUB_TIMEOUT=1/GRUB_TIMEOUT=0\nGRUB_HIDDEN_TIMEOUT=true/" $imagedir/etc/default/grub 11 | 12 | # Update grub.cfg using the script 13 | chroot $imagedir update-grub 14 | 15 | # Alias grub.cfg as menu.lst 16 | chroot $imagedir ln -s /boot/grub/grub.cfg /boot/grub/menu.lst 17 | 18 | chroot $imagedir ln -s . /boot/boot 19 | -------------------------------------------------------------------------------- /tasks/60-cleanup: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Clean up the image 3 | 4 | # We do the same to the bash history, there shouldn't be anything sensitive in there, 5 | # it's just in case plugins need to execute commands that have credentials in their parameters. 6 | # In fact: The standard bootstrapping process does not even create it. 7 | if [ -f $imagedir/root/.bash_history ]; then 8 | shred --remove $imagedir/root/.bash_history 9 | fi 10 | 11 | # Clear the motd head, it contains the hostname of the bootstrapper 12 | > /var/run/motd 13 | 14 | # Remove log files, temporary files and bash history 15 | rm -rf \ 16 | $imagedir/var/log/{bootstrap,dpkg}.log \ 17 | $imagedir/tmp/* 18 | -------------------------------------------------------------------------------- /tasks/ec2/11-attach-volume: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Attach the EBS volume 3 | 4 | # Get a random device letter, we will hang forever if we try to attach a volume to an already mapped device. 5 | for device_letter in {f..z}; do 6 | device_path="/dev/xvd$device_letter" 7 | [ ! -b $device_path ] && break 8 | done 9 | [ -b $device_path ] && die "No free device letters found (tried sdf to sdz)!" 10 | 11 | # Wait until the volume is available 12 | dotdot "euca-describe-volumes $volume_id | grep available" 13 | euca-attach-volume --instance "$instance_id" --device "/dev/sd$device_letter" "$volume_id" 14 | # Wait until the volume is attached 15 | dotdot "test -b $device_path && echo attached" 16 | -------------------------------------------------------------------------------- /plugins/admin-user-tasks/create-user: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | admin_username='admin' 3 | 4 | # Create the user 5 | chroot $imagedir useradd --create-home --shell /bin/bash $admin_username 6 | 7 | # Allow ec2-user to use sudo without a password 8 | cat > $imagedir/etc/sudoers.d/99_admin < $imagedir/etc/apt/apt.conf.d/02periodic 3 | // Enable the update/upgrade script (0=disable) 4 | APT::Periodic::Enable "1"; 5 | 6 | // Do "apt-get update" automatically every n-days (0=disable) 7 | APT::Periodic::Update-Package-Lists "1"; 8 | 9 | // Do "apt-get upgrade --download-only" every n-days (0=disable) 10 | APT::Periodic::Download-Upgradeable-Packages "1"; 11 | 12 | // Run the "unattended-upgrade" security upgrade script 13 | // every n-days (0=disabled) 14 | // Requires the package "unattended-upgrades" and will write 15 | // a log in /var/log/unattended-upgrades 16 | APT::Periodic::Unattended-Upgrade "1"; 17 | 18 | EOF 19 | -------------------------------------------------------------------------------- /tasks/01-packages: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Add some basic packages we are going to need 3 | packages+=('udev') 4 | packages+=('openssh-server') 5 | # We could bootstrap without locales, but things just suck without them, 6 | # error messages etc. 7 | packages+=('locales') 8 | # Needed for the init scripts 9 | packages+=('file') 10 | 11 | packages+=('grub-pc') 12 | 13 | # We will need to format the volume, so we need xfsprogs 14 | [ $filesystem = 'xfs' ] && host_packages+=('xfsprogs') 15 | # In order to make sure a volume is not busy, before unmounting we need lsof 16 | host_packages+=('lsof') 17 | host_packages+=('debootstrap') 18 | 19 | # Force noninteractive mode for apt-get 20 | export DEBIAN_FRONTEND='noninteractive' 21 | -------------------------------------------------------------------------------- /plugins/lvm-root-volume-tasks/13-mount-volume: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Mount the EBS volume 3 | imagedir=$debootstrap_dir/${volume_id} 4 | 5 | # Fail if the imagedir exists, it should be quite unique. 6 | # This guarantees that we later on can delete it without having to check for anything. 7 | if [ -d "$imagedir" ]; then 8 | die "The mount location $imagedir already exists." 9 | fi 10 | # Create the dir we are going to mount the volume onto 11 | mkdir -p $imagedir 12 | 13 | [ -n "`mount | grep "$imagedir"`" ] && die "Something is already mounted on $imagedir" 14 | 15 | mount $root_device_path $imagedir 16 | mkdir -p $imagedir/boot 17 | mount $boot_device_path $imagedir/boot 18 | 19 | log "The volume is mounted at ${imagedir}." 20 | -------------------------------------------------------------------------------- /tasks/15-mount-specials: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Mount special devices 3 | 4 | # Unmount if the bootstrapping fails, but keep the trap used by Google Compute 5 | # Engine builds. 6 | trap "printf 'Bootstrapping failed!\n' 7 | cd $originaldir 8 | chroot $imagedir umount /dev/pts 9 | chroot $imagedir umount /sys 10 | chroot $imagedir umount /proc 11 | umount $imagedir/dev 12 | 13 | declare -F cleanup_loopback_mount >/dev/null 2>&1 && cleanup_loopback_mount 14 | " ERR EXIT 15 | 16 | # Mount all the different special devices, other installers depend on their existence 17 | mount --bind /dev $imagedir/dev 18 | chroot $imagedir mount -t proc none /proc 19 | chroot $imagedir mount -t sysfs none /sys 20 | chroot $imagedir mount -t devpts none /dev/pts 21 | -------------------------------------------------------------------------------- /init.d/expand-volume: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ### BEGIN INIT INFO 3 | # Provides: expand-volume 4 | # Required-Start: 5 | # Required-Stop: 6 | # Should-Start: 7 | # Should-Stop: 8 | # Default-Start: 2 3 4 5 9 | # Default-Stop: 10 | # Description: Expand the filesystem of the mounted root volume to its maximum possible size 11 | ### END INIT INFO 12 | 13 | prog=$(basename $0) 14 | logger="logger -t $prog" 15 | 16 | device_path="/dev/xvda1" 17 | 18 | filesystem=`blkid | grep $device_path | sed 's#\(.*\):.*TYPE="\(.*\)".*#\2#'` 19 | 20 | case $filesystem in 21 | xfs) xfs_growfs / ;; 22 | ext2) resize2fs $device_path ;; 23 | ext3) resize2fs $device_path ;; 24 | ext4) resize2fs $device_path ;; 25 | *) $logger "The filesystem $filesystem was not recognized. Unable to expand size." ;; 26 | esac 27 | -------------------------------------------------------------------------------- /tasks/71-unmount-specials: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Unmount special devices 3 | 4 | # We unmount from inside the image, otherwise the system won't boot 5 | chroot $imagedir umount /dev/pts 6 | chroot $imagedir umount /sys 7 | chroot $imagedir umount /proc 8 | umount $imagedir/dev 9 | 10 | # No need to umount on errors any longer, but keep the trap used by 11 | # Google Compute Engine builds if present. 12 | trap " 13 | declare -F cleanup_loopback_mount >/dev/null 2>&1 && cleanup_loopback_mount 14 | " ERR EXIT 15 | 16 | # Unmount the entire volume now 17 | 18 | # cd out of the volume, otherwise we step on our own feet 19 | cd $scriptdir 20 | 21 | # Wait for the volume to idle, maybe the user is currently in it 22 | while [ -n "`lsof $imagedir`" ]; do 23 | log "Waiting for volume to idle, before unmounting." 24 | sleep 5 25 | done 26 | -------------------------------------------------------------------------------- /tasks/gce/30-grub: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Prepare GRUB to install itself correctly. 3 | 4 | # Set up a device map for GRUB to use while loopback-mounted: 5 | echo "(hd0) ${base_device_path}" > $imagedir/boot/grub/device.map 6 | echo "(hd0,1) ${device_path}" >> $imagedir/boot/grub/device.map 7 | 8 | # Configure GRUB timeouts for fast booting: 9 | sed -i -e '/GRUB_TIMEOUT=/s/^.*/GRUB_TIMEOUT=0/' $imagedir/etc/default/grub 10 | 11 | # Direct GRUB and Linux console output to ttyS0 to facilitate gcutil 12 | # getserialportoutput. 13 | # 14 | # The backreference on the next line avoids spurious spaces in the resulting 15 | # kernel command line. 16 | sed -i -e '/GRUB_CMDLINE_LINUX=/s/\( \)\?"$/\1console=ttyS0,38400n8"/' \ 17 | $imagedir/etc/default/grub 18 | sed -i -e '/GRUB_TERMINAL=/s/^.*/GRUB_TERMINAL="serial --unit=0 --speed=38400 --word=8 --parity=no --stop=1"' \ 19 | $imagedir/etc/default/grub 20 | -------------------------------------------------------------------------------- /tasks/ec2/05-install-euca2ools: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # We use euca2ools to communicate with AWS 4 | # The package in squeeze is v 1.2, which does not support EBS images 5 | # So we install 1.3 from source instead 6 | if [ ! -d $scriptdir/euca2ools ]; then 7 | wget -qO $scriptdir/euca2ools-1.3.2.tar.gz https://github.com/eucalyptus/euca2ools/archive/1.3.2.tar.gz 8 | tar zxf $scriptdir/euca2ools-1.3.2.tar.gz 9 | fi 10 | 11 | function install_euca2ools { 12 | # We want to fail if make fails, so don't start a subshell with () 13 | # Remember the old dir 14 | local orig_pwd=$(pwd) 15 | cd euca2ools-1.3.2 16 | make | spin 17 | [ $PIPESTATUS == 0 ] || die "Bootstrapping failed!" 18 | cd $orig_pwd 19 | } 20 | 21 | # make the euca2ools if they are not installed or the version is wrong 22 | if ! command -v euca-version > /dev/null 2>&1; then 23 | install_euca2ools 24 | elif [[ ! "`euca-version`" =~ euca2ools\ 1.3.* ]]; then 25 | install_euca2ools 26 | fi 27 | -------------------------------------------------------------------------------- /tasks/gce/25-install-cloudsdk: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Fetch Cloud SDK's sha1sum file to identify the latest version of Cloud SDK, 4 | # then download that version. 5 | cloudsdk_url_prefix="https://dl.google.com/dl/cloudsdk/release" 6 | cloudsdk_regex='^packages\/google-cloud-sdk-coretools-linux-[0-9]*\.tar\.gz$' 7 | cloudsdk_url_suffix="$(wget -O- "${cloudsdk_url_prefix}/sha1.txt" \ 8 | | awk "\$2 ~ /${cloudsdk_regex}/ { print \$2 }")" 9 | cloudsdk_url="${cloudsdk_url_prefix}/${cloudsdk_url_suffix}" 10 | wget -P $imagedir/tmp/ "${cloudsdk_url}" 11 | 12 | mkdir -p $imagedir/usr/local/share/google 13 | tar xf $imagedir/tmp/google-cloud-sdk-coretools-linux-*.tar.gz \ 14 | -C $imagedir/usr/local/share/google --no-same-owner 15 | ln -s ../share/google/google-cloud-sdk/bin/gsutil $imagedir/usr/local/bin/gsutil 16 | ln -s ../share/google/google-cloud-sdk/bin/gcutil $imagedir/usr/local/bin/gcutil 17 | ln -s ../share/google/google-cloud-sdk/bin/gcloud $imagedir/usr/local/bin/gcloud 18 | -------------------------------------------------------------------------------- /plugins/lvm-root-volume-tasks/12-partition-and-format-volume: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Format the volume with a boot partition then the rest LVM 4 | sfdisk $device_path << EOF 5 | ,20,L 6 | ,,8e 7 | EOF 8 | 9 | boot_device_path=${device_path}1 10 | lvm_device_path=${device_path}2 11 | 12 | log "Formatting the boot device $boot_device_path with ext2" 13 | mkfs.ext2 $boot_device_path 14 | 15 | log "Creating LVM Volumes on $lvm_device_path" 16 | pvcreate $lvm_device_path 17 | vgcreate vg_root $lvm_device_path 18 | 19 | log "Creating swap" 20 | lvcreate -nlv_swap -L1G vg_root 21 | mkswap /dev/mapper/vg_root-lv_swap 22 | 23 | lvcreate -nlv_root -l100%FREE vg_root 24 | root_device_path=/dev/mapper/vg_root-lv_root 25 | log "Formatting the device $root_device_path with $filesystem" 26 | mkfs.$filesystem $root_device_path 27 | 28 | case $filesystem in 29 | # Disable time based fs checks 30 | ext*) tune2fs -i 0 $root_device_path ;; 31 | # Add some tools for xfs, ext tools are built-in 32 | xfs) packages+=('xfsprogs') ;; 33 | esac 34 | -------------------------------------------------------------------------------- /init.d/squeeze/generate-ssh-hostkeys: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ### BEGIN INIT INFO 3 | # Provides: generate-ssh-hostkeys 4 | # Required-Start: $local_fs 5 | # Required-Stop: 6 | # Should-Start: 7 | # Should-Stop: 8 | # Default-Start: S 9 | # Default-Stop: 10 | # Description: Generate ssh host keys if they do not exist 11 | ### END INIT INFO 12 | 13 | prog=$(basename $0) 14 | logger="logger -t $prog" 15 | 16 | rsa_key="/etc/ssh/ssh_host_rsa_key" 17 | dsa_key="/etc/ssh/ssh_host_dsa_key" 18 | 19 | # Exit if the hostkeys already exist 20 | if [ -f $rsa_key -a -f $dsa_key ]; then 21 | exit 22 | fi 23 | 24 | # Generate the ssh host keys 25 | [ -f $rsa_key ] || ssh-keygen -f $rsa_key -t rsa -C 'host' -N '' 26 | [ -f $dsa_key ] || ssh-keygen -f $dsa_key -t dsa -C 'host' -N '' 27 | 28 | # Output the public keys to the console 29 | # This allows user to get host keys securely through console log 30 | echo "-----BEGIN SSH HOST KEY FINGERPRINTS-----" | $logger 31 | ssh-keygen -l -f $rsa_key.pub | $logger 32 | ssh-keygen -l -f $dsa_key.pub | $logger 33 | echo "------END SSH HOST KEY FINGERPRINTS------" | $logger 34 | -------------------------------------------------------------------------------- /tasks/gce/09-create-workspace: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Prepare safe workspace for GCE files 3 | 4 | # Ensure our temporary loopback device and mount get cleaned up no matter how we 5 | # exit, even if it's before the usual cleanup steps. 6 | function cleanup_loopback_mount () { 7 | # Disable exit-on-error 8 | set +e 9 | 10 | if [ -n "$imagedir" ]; then 11 | log "Unmounting $imagedir ahead of unclean exit." 12 | umount $imagedir 13 | fi 14 | 15 | if [ -n "$device_path" ]; then 16 | log "Deleting partition loopback device $device_path ahead of unclean exit." 17 | losetup -d $device_path 18 | fi 19 | 20 | if [ -n "$base_device_path" ]; then 21 | log "Deleting whole-disk loopback device $base_device_path ahead of unclean exit." 22 | losetup -d $base_device_path 23 | fi 24 | 25 | if [ -d "$workspace" ]; then 26 | log "$workspace still exists. Leaving alone for potential debugging." 27 | fi 28 | 29 | # Enable exit-on-error 30 | set -e 31 | } 32 | trap cleanup_loopback_mount ERR EXIT 33 | 34 | workspace="$(mktemp -d -t build-debian-cloud.XXXXXXXXXX)" 35 | tarballdir=$workspace 36 | -------------------------------------------------------------------------------- /plugins/remount-tasks/remount-volume: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Mostly copied from the create-volume task 3 | 4 | # Get a random device letter, we will hang forever if we try to attach a volume to an already mapped device. 5 | for device_letter in {f..z}; do 6 | device_path="/dev/xvd$device_letter" 7 | [ ! -b $device_path ] && break 8 | done 9 | [ -b $device_path ] && die "No free device letters found (tried sdf to sdz)!" 10 | 11 | logn "Reattaching the bootstrapped volume" 12 | euca-attach-volume --instance "$instance_id" --device "/dev/sd$device_letter" "$volume_id" 13 | # Wait until the volume is attached 14 | dotdot "test -b $device_path && echo attached" 15 | 16 | # Recreate the imagedir, it was delete in the unmount task 17 | log "Creating mount location ${imagedir}" 18 | mkdir -p $imagedir 19 | 20 | mount $device_path $imagedir 21 | log "The volume has been mounted at $imagedir" 22 | 23 | log "Run the following commands to unmount and remove the volume:" 24 | logplain \ 25 | "export EC2_URL=https://ec2.$region.amazonaws.com" \ 26 | "umount $imagedir" \ 27 | "rm -rf $imagedir" \ 28 | "euca-detach-volume $volume_id" \ 29 | "euca-delete-volume $volume_id" 30 | -------------------------------------------------------------------------------- /tasks/gce/95-register-image: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ -z "${gcs_dest}" ]; then 4 | log "No GCS destination URL prefix defined. Not uploading the image." 5 | exit 0 6 | fi 7 | 8 | # If someone puts a single slash at the end of the destination URL prefix, do 9 | # what they mean and don't gratuitously use a double slash. 10 | gcs_dest=${gcs_dest%/} 11 | 12 | log "Uploading image to \"${gcs_dest}/${tarball_name}\"" 13 | gsutil cp "${originaldir}/${tarball_name}" "${gcs_dest}/${tarball_name}" 14 | 15 | if [ -z "${gce_project}" ]; then 16 | log "No GCE destination project defined. Not adding the image." 17 | exit 0 18 | fi 19 | 20 | # GCE image name is the tarball basename minus minor and patch versions 21 | image_name="$distribution-$major_version-$codename-$image_name_suffix" 22 | 23 | # Only set the description if it's not set yet 24 | if ! [[ -v description ]]; then 25 | description="${lsb_description} built on ${build_date}" 26 | fi 27 | 28 | log "Adding image \"${image_name}\" to GCE project \"${gce_project}\"" 29 | gcutil --project="${gce_project}" addimage "${image_name}" \ 30 | "${gcs_dest}/${tarball_name}" --description="${description}" \ 31 | --preferred_kernel="${gce_kernel}" 32 | -------------------------------------------------------------------------------- /plugins/lvm-root-volume: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # lvm-root-volume 4 | # Put the root volume on an LVM partition 5 | 6 | if [ $volume_size -lt 2 ] 7 | then 8 | die "Disk must be at least 2 GB" 9 | fi 10 | 11 | insert_task_after $TASK_PACKAGES $plugindir/lvm-root-volume-tasks/01-lvm-package 12 | 13 | remove_task "12-format-volume" 14 | insert_task_after "11-attach-volume" "$plugindir/lvm-root-volume-tasks/12-partition-and-format-volume" 15 | remove_task $TASK_MOUNT_VOLUME 16 | insert_task_before $TASK_BOOTSTRAP "$plugindir/lvm-root-volume-tasks/13-mount-volume" 17 | 18 | 19 | remove_task "30-grub" 20 | insert_task_after "22-apt-upgrade" "$plugindir/lvm-root-volume-tasks/30-grub" 21 | remove_task "31-fstab" 22 | insert_task_after "30-grub" "$plugindir/lvm-root-volume-tasks/31-fstab" 23 | 24 | insert_task_after "51-init-scripts" "$plugindir/lvm-root-volume-tasks/52-replace-expand-volume" 25 | 26 | insert_task_before $TASK_UNMOUNT_VOLUME "$plugindir/lvm-root-volume-tasks/72-unmount-boot" 27 | insert_task_before "73-detach-volume" "$plugindir/lvm-root-volume-tasks/73-deactivate-vg" 28 | remove_task $TASK_CREATE_AMI 29 | insert_task_after $TASK_DELETE_VOLUME "$plugindir/lvm-root-volume-tasks/95-register-ami" 30 | -------------------------------------------------------------------------------- /tasks/ec2/07-host-information: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Retreive information about the host system 3 | instance_information=`wget -qO - http://169.254.169.254/latest/dynamic/instance-identity/document` 4 | # We need the region, for the apt sources and the availability zone for the EBS volume 5 | instance_id=`printf -- "$instance_information" | grep instanceId | awk -F\" '{print $4}'` 6 | region=`printf -- "$instance_information" | grep region | awk -F\" '{print $4}'` 7 | availability_zone=`printf -- "$instance_information" | grep availabilityZone | awk -F\" '{print $4}'` 8 | 9 | if [ -z "$instance_id" ]; then 10 | die \ 11 | "Unable to fetch the instance id of this machine." \ 12 | "This script must be running on ec2 in order to mount EBS volumes." 13 | fi 14 | [ -z "$region" ] && die "Unable to fetch the region of this machine." 15 | [ -z "$availability_zone" ] && die "Unable to fetch the availability zone of this machine." 16 | 17 | # Set the ec2_url to the same region as this machine, anything else wouldn't make sense 18 | export EC2_URL="https://ec2.$region.amazonaws.com" 19 | 20 | # Check if we can handle this region, there are hardcoded AKIs later on. 21 | if ! $(contains $region known_regions[@]); then 22 | die "The region $region is unkown." 23 | fi 24 | -------------------------------------------------------------------------------- /init.d/wheezy/generate-ssh-hostkeys: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ### BEGIN INIT INFO 3 | # Provides: generate-ssh-hostkeys 4 | # Required-Start: $local_fs 5 | # Required-Stop: 6 | # Should-Start: 7 | # Should-Stop: 8 | # Default-Start: S 9 | # Default-Stop: 10 | # Description: Generate ssh host keys if they do not exist 11 | ### END INIT INFO 12 | 13 | prog=$(basename $0) 14 | logger="logger -t $prog" 15 | 16 | rsa_key="/etc/ssh/ssh_host_rsa_key" 17 | dsa_key="/etc/ssh/ssh_host_dsa_key" 18 | ecdsa_key="/etc/ssh/ssh_host_ecdsa_key" 19 | 20 | # Exit if the hostkeys already exist 21 | if [ -f $rsa_key -a -f $dsa_key -a -f $ecdsa_key ]; then 22 | exit 23 | fi 24 | 25 | # Generate the ssh host keys 26 | [ -f $rsa_key ] || ssh-keygen -f $rsa_key -t rsa -C 'host' -N '' 27 | [ -f $dsa_key ] || ssh-keygen -f $dsa_key -t dsa -C 'host' -N '' 28 | [ -f $ecdsa_key ] || ssh-keygen -f $ecdsa_key -t ecdsa -C 'host' -N '' 29 | 30 | # Output the public keys to the console 31 | # This allows user to get host keys securely through console log 32 | echo "-----BEGIN SSH HOST KEY FINGERPRINTS-----" | $logger 33 | ssh-keygen -l -f $rsa_key.pub | $logger 34 | ssh-keygen -l -f $dsa_key.pub | $logger 35 | ssh-keygen -l -f $ecdsa_key.pub | $logger 36 | echo "------END SSH HOST KEY FINGERPRINTS------" | $logger 37 | -------------------------------------------------------------------------------- /regression-checklist.md: -------------------------------------------------------------------------------- 1 | # Regression checklist # 2 | This is a checklist, to keep a record of subtle bugs in the AMI that can be hard to spot. 3 | Before publishing any AMI, this list should be consulted first. 4 | 5 | *Note that the bootstrapper does all the listed things for you. However, regressions are always a possibility.* 6 | 7 | * EBS root volume, should be set to "Delete on Termination" 8 | * The system log on instance startup should contain no error other than "hostname: the specified hostname is invalid" 9 | * The AWS credentials should not be obtainable from the root volume (see below for instructions) 10 | * The ssh hostkeys of the bootstrapping machine are copied into the image by debootstrap 11 | to remove them properly they have to be shredded with `shred --remove FILENAME` 12 | * There should be no `.*_history` files in any home directories 13 | * The default locale should be generated 14 | * A timezone should be set 15 | * The aptitude sources list should at least contain a security update mirror and a main repository mirror 16 | * There should be no broken dependencies or un-upgraded packages 17 | * eth0 should be configured 18 | * The aptitude package cache should be deleted 19 | * The motd contains a reference to the host (debootstrap put it there), that should be removed 20 | 21 | 22 | 23 | To check a volume for any sensitive data run the following commands: 24 | 25 | ``` 26 | apt-get update 27 | apt-get install binutils 28 | strings /dev/xvda1 | grep -A10 -B10 'AWS_|EC2_' 29 | ``` 30 | -------------------------------------------------------------------------------- /tasks/14-bootstrap: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Bootstrap 3 | 4 | # Concatenate the packages 5 | SAVE_IFS=$IFS 6 | IFS="," 7 | include="${packages[*]}" 8 | exclude="${exclude_packages[*]}" 9 | IFS=$SAVE_IFS 10 | 11 | # For bootstrapping purposes, we just use the first-listed mirror even if 12 | # multiple are specified. 13 | bootstrap_mirror=${apt_mirrors%% *} 14 | 15 | bootstrap_args="--arch $arch --include=$include --exclude=$exclude 16 | $codename $imagedir $bootstrap_mirror" 17 | 18 | args_hash=`printf -- "$arch $include -- $exclude $codename" | sha1sum` 19 | 20 | tarball="$tarballdir/bootstrap-${args_hash:0:7}.tar" 21 | 22 | include_param="" 23 | if [[ $include != "" ]]; then 24 | include_param="--include=$include" 25 | fi 26 | 27 | exclude_param="" 28 | if [[ $exclude != "" ]]; then 29 | exclude_param="--exclude=$exclude" 30 | fi 31 | 32 | # When testing, some bootstraps can fail, instead of re-downloading it all 33 | # we create a tarball containing all the necessary things for bootstrapping 34 | if [ ! -e "$tarball" ]; then 35 | log "Creating the bootstrap tarball" 36 | debootstrap --arch $arch --make-tarball=$tarball \ 37 | ${include_param} ${exclude_param} \ 38 | $codename $imagedir $bootstrap_mirror | spin 39 | [ $PIPESTATUS == 1 ] || die "Creating bootstrap tarball failed!" 40 | fi 41 | 42 | log "Bootstrapping" 43 | debootstrap --arch $arch --unpack-tarball=$tarball \ 44 | ${include_param} ${exclude_param} \ 45 | $codename $imagedir $bootstrap_mirror | spin 46 | [ $PIPESTATUS == 0 ] || die "Bootstrapping failed!" 47 | -------------------------------------------------------------------------------- /init.d/ec2-get-credentials: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ### BEGIN INIT INFO 3 | # Provides: ec2-get-credentials 4 | # Required-Start: $network 5 | # Required-Stop: 6 | # Should-Start: 7 | # Should-Stop: 8 | # Default-Start: 2 3 4 5 9 | # Default-Stop: 10 | # Description: Retrieve the ssh credentials and add to authorized_keys 11 | ### END INIT INFO 12 | # 13 | # ec2-get-credentials - Retrieve the ssh credentials and add to authorized_keys 14 | # 15 | # Based on /usr/local/sbin/ec2-get-credentials from Amazon's ami-20b65349 16 | # 17 | 18 | prog=$(basename $0) 19 | logger="logger -t $prog" 20 | 21 | public_key_url=http://169.254.169.254/1.0/meta-data/public-keys/0/openssh-key 22 | username='root' 23 | # A little bit of nastyness to get the homedir, when the username is a variable 24 | ssh_dir="`eval printf ~$username`/.ssh" 25 | authorized_keys="$ssh_dir/authorized_keys" 26 | 27 | # Try to get the ssh public key from instance data. 28 | public_key=`wget -qO - $public_key_url` 29 | if [ -n "$public_key" ]; then 30 | if [ ! -f $authorized_keys ]; then 31 | if [ ! -d $ssh_dir ]; then 32 | mkdir -m 700 $ssh_dir 33 | chown $username:$username $ssh_dir 34 | fi 35 | touch $authorized_keys 36 | chown $username:$username $authorized_keys 37 | fi 38 | 39 | if ! grep -s -q "$public_key" $authorized_keys; then 40 | printf "\n%s" -- "$public_key" >> $authorized_keys 41 | $logger "New ssh key added to $authorized_keys from $public_key_url" 42 | chmod 600 $authorized_keys 43 | chown $username:$username $authorized_keys 44 | fi 45 | fi 46 | -------------------------------------------------------------------------------- /init.d/ec2-run-user-data: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ### BEGIN INIT INFO 3 | # Provides: ec2-run-user-data 4 | # Required-Start: ec2-get-credentials 5 | # Required-Stop: 6 | # Should-Start: 7 | # Should-Stop: 8 | # Default-Start: 2 3 4 5 9 | # Default-Stop: 10 | # Description: Run instance user-data if it looks like a script. 11 | ### END INIT INFO 12 | # 13 | # Only retrieves and runs the user-data script once per instance. If 14 | # you want the user-data script to run again (e.g., on the next boot) 15 | # then readd this script with insserv: 16 | # insserv -d ec2-run-user-data 17 | # 18 | prog=$(basename $0) 19 | logger="logger -t $prog" 20 | instance_data_url="http://169.254.169.254/2008-02-01" 21 | 22 | 23 | # Retrieve the instance user-data and run it if it looks like a script 24 | user_data_file=$(tempfile --prefix ec2 --suffix .user-data --mode 700) 25 | $logger "Retrieving user-data" 26 | wget -qO $user_data_file $instance_data_url/user-data 2>&1 | $logger 27 | 28 | if [ $(file -b --mime-type $user_data_file) = 'application/x-gzip' ]; then 29 | $logger "Uncompressing gzip'd user-data" 30 | mv $user_data_file $user_data_file.gz 31 | gunzip $user_data_file.gz 32 | fi 33 | 34 | if [ ! -s $user_data_file ]; then 35 | $logger "No user-data available" 36 | elif head -1 $user_data_file | egrep -v '^#!'; then 37 | $logger "Skipping user-data as it does not begin with #!" 38 | else 39 | $logger "Running user-data" 40 | $user_data_file 2>&1 | logger -t "user-data" 41 | $logger "user-data exit code: $?" 42 | fi 43 | rm -f $user_data_file 44 | 45 | # Disable this script, it may only run once 46 | insserv -r $0 47 | -------------------------------------------------------------------------------- /plugins/lvm-root-volume-tasks/files/expand-lvm-volume: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ### BEGIN INIT INFO 3 | # Provides: expand-volume 4 | # Required-Start: 5 | # Required-Stop: 6 | # Should-Start: 7 | # Should-Stop: 8 | # Default-Start: 2 3 4 5 9 | # Default-Stop: 10 | # Description: Expand the filesystem of the mounted root volume to its maximum possible size 11 | ### END INIT INFO 12 | 13 | prog=$(basename $0) 14 | logger="logger -t $prog" 15 | 16 | device_path="/dev/xvda" 17 | new_part_number=3 18 | new_partition="${device_path}${new_part_number}" 19 | lvm_root_volume="/dev/mapper/vg_root-lv_root" 20 | 21 | # Check for existence of xvda3 - exit if it exists 22 | 23 | if fdisk -l ${device_path} | grep -q ${new_partition} 24 | then 25 | exit 26 | fi 27 | 28 | # Check for free space at end of disk - exit if no extra space 29 | if [ `parted ${device_path} "unit cyl print free" | tail -n2 | head -n1 | awk '{print $3}'` == "0cyl" ] 30 | then 31 | exit 32 | fi 33 | 34 | # Add new xvda3 with type 8e that fills up the disk 35 | ( echo n; echo p; echo ${new_part_number}; echo ; echo ; echo t; echo ${new_part_number}; echo 8e; echo w; ) | fdisk ${device_path} 36 | partprobe 37 | 38 | # Add the new partition to the VG, extend the LV 39 | pvcreate $new_partition 40 | vgextend vg_root $new_partition 41 | lvextend -l+100%FREE /dev/vg_root/lv_root 42 | 43 | # Resize the FS 44 | 45 | filesystem=`blkid | grep $lvm_root_volume | sed 's#\(.*\):.*TYPE="\(.*\)".*#\2#'` 46 | 47 | case $filesystem in 48 | xfs) xfs_growfs / ;; 49 | ext2) resize2fs $lvm_root_volume ;; 50 | ext3) resize2fs $lvm_root_volume ;; 51 | ext4) resize2fs $lvm_root_volume ;; 52 | *) $logger "The filesystem $filesystem was not recognized. Unable to expand size." ;; 53 | esac 54 | -------------------------------------------------------------------------------- /plugins/README.md: -------------------------------------------------------------------------------- 1 | ## Plugins ## 2 | In this folder you will find plugins made for the bootstrapper. 3 | You can run them via the `--plugin` option when bootstrapping: 4 | ``` 5 | ./build-debian-cloud ec2 --plugin plugins/admin-user 6 | ``` 7 | 8 | * `standard-packages` 9 | Adds some common packages to the AMI. 10 | * `unattended-upgrades` 11 | Enables unattended upgrades with aptitude. Your EC2 server will upgrade itself daily. 12 | * `build-metadata` 13 | Adds a build metadata output file to record the AMI and snapshot IDs for further scripting. 14 | * `admin-user` 15 | Creates a user named 'admin', gives it sudo rights and disables the root login. 16 | * `publish-ami` 17 | Grants launch permission of the new AMI to everybody. 18 | * `publish-snapshot` 19 | Grants everybody permission to create a volume from the snapshot for the AMI. 20 | * `remount` 21 | Remounts the bootstrapped volume. 22 | With this plugin you can inspect the results of the bootstrapping process without launching an instance. 23 | * `lvm-root-volume` 24 | Partitions the root volume with LVM. 25 | It creates 1 GB of swap then fills the rest. 26 | When booting it will create a 3rd partition if there is extra space, add it to the LVM VG and expand the volume. 27 | 28 | ## Other plugins ## 29 | The following is a list of external plugins you can use with build-debian-cloud. 30 | 31 | * [ec2-autohostname](https://github.com/secoya/ec2-autohostname) 32 | Create a Route 53 CNAME record via an instance tag when booting. 33 | 34 | * [ec2-minecraft](https://github.com/andsens/ec2-minecraft) 35 | Installs [Minecraft Server Manager](http://marcuswhybrow.net/minecraft-server-manager/) by Marcus Whybrow. 36 | 37 | * [debian-cloud-chef](https://github.com/tmatilai/debian-cloud-chef) 38 | Installs [Opscode Chef](http://www.opscode.com/chef/) client using the Omnibus installer. 39 | 40 | * [debian-cloud-rightscale](https://github.com/sitepoint/debian-cloud-rightscale) 41 | Installs [RightLink](http://support.rightscale.com/12-Guides/RightLink) by RightScale. 42 | 43 | If you have made a plugin for build-debian-cloud and would like to share it, 44 | send me a pull request or drop me an email. 45 | -------------------------------------------------------------------------------- /grub.d/40_custom: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # This file generates the old menu.lst configuration with grub2 4 | # It was copied from tomheadys github repo: 5 | # https://github.com/tomheady/ec2debian/blob/master/src/root/etc/grub.d/40_custom 6 | 7 | prefix=/usr 8 | exec_prefix=${prefix} 9 | bindir=${exec_prefix}/bin 10 | libdir=${exec_prefix}/lib 11 | . ${libdir}/grub/grub-mkconfig_lib 12 | 13 | export TEXTDOMAIN=grub 14 | export TEXTDOMAINDIR=${prefix}/share/locale 15 | 16 | GRUB_DEVICE=/dev/xvda1 17 | 18 | 19 | cat << EOF 20 | default ${GRUB_DEFAULT} 21 | timeout ${GRUB_TIMEOUT} 22 | EOF 23 | 24 | if ${GRUB_HIDDEN_TIMEOUT:-false}; then 25 | printf "hiddenmenu\n" 26 | fi 27 | 28 | linux_entry () 29 | { 30 | os="$1" 31 | version="$2" 32 | args="$4" 33 | 34 | title="$(gettext_quoted "%s, with Linux %s")" 35 | 36 | cat << EOF 37 | title ${version} 38 | root (hd0) 39 | kernel ${rel_dirname}/${basename} root=${GRUB_DEVICE} ro ${args} 40 | initrd ${rel_dirname}/${initrd} 41 | EOF 42 | } 43 | 44 | list=`for i in /boot/vmlinuz-* /boot/vmlinux-* /vmlinuz-* /vmlinux-* ; do 45 | if grub_file_is_not_garbage "$i" ; then echo -n "$i " ; fi 46 | done` 47 | prepare_boot_cache= 48 | 49 | while [ "x$list" != "x" ] ; do 50 | linux=`version_find_latest $list` 51 | basename=`basename $linux` 52 | dirname=`dirname $linux` 53 | rel_dirname=`make_system_path_relative_to_its_root $dirname` 54 | version=`echo $basename | sed -e "s,^[^0-9]*-,,g"` 55 | alt_version=`echo $version | sed -e "s,\.old$,,g"` 56 | linux_root_device_thisversion="${LINUX_ROOT_DEVICE}" 57 | 58 | initrd= 59 | for i in "initrd.img-${version}" "initrd-${version}.img" \ 60 | "initrd-${version}" "initramfs-${version}.img" \ 61 | "initrd.img-${alt_version}" "initrd-${alt_version}.img" \ 62 | "initrd-${alt_version}" "initramfs-${alt_version}.img"; do 63 | if test -e "${dirname}/${i}" ; then 64 | initrd="$i" 65 | break 66 | fi 67 | done 68 | 69 | initramfs= 70 | for i in "config-${version}" "config-${alt_version}"; do 71 | if test -e "${dirname}/${i}" ; then 72 | initramfs=`grep CONFIG_INITRAMFS_SOURCE= "${dirname}/${i}" | cut -f2 -d= | tr -d \"` 73 | break 74 | fi 75 | done 76 | 77 | linux_entry "${OS}" "${version}" \ 78 | "${GRUB_CMDLINE_LINUX} ${GRUB_CMDLINE_LINUX_DEFAULT}" 79 | 80 | list=`echo $list | tr ' ' '\n' | grep -vx $linux | tr '\n' ' '` 81 | done 82 | -------------------------------------------------------------------------------- /plugins/lvm-root-volume-tasks/files/40_custom: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # This file generates the old menu.lst configuration with grub2 4 | # It was copied from tomheadys github repo: 5 | # https://github.com/tomheady/ec2debian/blob/master/src/root/etc/grub.d/40_custom 6 | 7 | prefix=/usr 8 | exec_prefix=${prefix} 9 | bindir=${exec_prefix}/bin 10 | libdir=${exec_prefix}/lib 11 | . ${libdir}/grub/grub-mkconfig_lib 12 | 13 | export TEXTDOMAIN=grub 14 | export TEXTDOMAINDIR=${prefix}/share/locale 15 | 16 | GRUB_DEVICE=/dev/xvda1 17 | ROOT_DEVICE=/dev/mapper/vg_root-lv_root 18 | 19 | cat << EOF 20 | default ${GRUB_DEFAULT} 21 | timeout ${GRUB_TIMEOUT} 22 | EOF 23 | 24 | if ! ${GRUB_HIDDEN_TIMEOUT:-false}; then 25 | printf "hiddenmenu\n" 26 | fi 27 | 28 | linux_entry () 29 | { 30 | os="$1" 31 | version="$2" 32 | args="$4" 33 | 34 | title="$(gettext_quoted "%s, with Linux %s")" 35 | 36 | cat << EOF 37 | title ${version} 38 | root (hd0,0) 39 | kernel ${rel_dirname}/${basename} root=${ROOT_DEVICE} ro ${args} 40 | initrd ${rel_dirname}/${initrd} 41 | EOF 42 | } 43 | 44 | list=`for i in /boot/vmlinuz-* /boot/vmlinux-* /vmlinuz-* /vmlinux-* ; do 45 | if grub_file_is_not_garbage "$i" ; then echo -n "$i " ; fi 46 | done` 47 | prepare_boot_cache= 48 | 49 | while [ "x$list" != "x" ] ; do 50 | linux=`version_find_latest $list` 51 | basename=`basename $linux` 52 | dirname=`dirname $linux` 53 | rel_dirname=`make_system_path_relative_to_its_root $dirname` 54 | version=`echo $basename | sed -e "s,^[^0-9]*-,,g"` 55 | alt_version=`echo $version | sed -e "s,\.old$,,g"` 56 | linux_root_device_thisversion="${LINUX_ROOT_DEVICE}" 57 | 58 | initrd= 59 | for i in "initrd.img-${version}" "initrd-${version}.img" \ 60 | "initrd-${version}" "initramfs-${version}.img" \ 61 | "initrd.img-${alt_version}" "initrd-${alt_version}.img" \ 62 | "initrd-${alt_version}" "initramfs-${alt_version}.img"; do 63 | if test -e "${dirname}/${i}" ; then 64 | initrd="$i" 65 | break 66 | fi 67 | done 68 | 69 | initramfs= 70 | for i in "config-${version}" "config-${alt_version}"; do 71 | if test -e "${dirname}/${i}" ; then 72 | initramfs=`grep CONFIG_INITRAMFS_SOURCE= "${dirname}/${i}" | cut -f2 -d= | tr -d \"` 73 | break 74 | fi 75 | done 76 | 77 | linux_entry "${OS}" "${version}" \ 78 | "${GRUB_CMDLINE_LINUX} ${GRUB_CMDLINE_LINUX_DEFAULT}" 79 | 80 | list=`echo $list | tr ' ' '\n' | grep -vx $linux | tr '\n' ' '` 81 | done 82 | -------------------------------------------------------------------------------- /tasks/ec2/95-register-ami: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Register the snapshot as a new AMI 3 | 4 | # Figure out which pvGrub kernel ID we need 5 | case $region in 6 | us-east-1) 7 | [ $arch = 'amd64' ] && aki="aki-88aa75e1" 8 | [ $arch = 'i386' ] && aki="aki-b6aa75df" 9 | ;; 10 | us-west-1) 11 | [ $arch = 'amd64' ] && aki="aki-f77e26b2" 12 | [ $arch = 'i386' ] && aki="aki-f57e26b0" 13 | ;; 14 | us-west-2) 15 | [ $arch = 'amd64' ] && aki="aki-fc37bacc" 16 | [ $arch = 'i386' ] && aki="aki-fa37baca" 17 | ;; 18 | eu-west-1) 19 | [ $arch = 'amd64' ] && aki="aki-71665e05" 20 | [ $arch = 'i386' ] && aki="aki-75665e01" 21 | ;; 22 | ap-southeast-1) 23 | [ $arch = 'amd64' ] && aki="aki-fe1354ac" 24 | [ $arch = 'i386' ] && aki="aki-f81354aa" 25 | ;; 26 | ap-southeast-2) 27 | [ $arch = 'amd64' ] && aki="aki-31990e0b" 28 | [ $arch = 'i386' ] && aki="aki-33990e09" 29 | ;; 30 | ap-northeast-1) 31 | [ $arch = 'amd64' ] && aki="aki-44992845" 32 | [ $arch = 'i386' ] && aki="aki-42992843" 33 | ;; 34 | sa-east-1) 35 | [ $arch = 'amd64' ] && aki="aki-c48f51d9" 36 | [ $arch = 'i386' ] && aki="aki-ca8f51d7" 37 | ;; 38 | us-gov-west-1) 39 | [ $arch = 'amd64' ] && aki="aki-79a4c05a" 40 | [ $arch = 'i386' ] && aki="aki-7ba4c058" 41 | ;; 42 | *) die "Unrecognized region:" "$region" 43 | esac 44 | 45 | 46 | [ $arch = 'i386' ] && ami_arch='i386' 47 | [ $arch = 'amd64' ] && ami_arch='x86_64' 48 | 49 | # The AMI has to start with "debian", otherwise we won't get a nice icon 50 | # The ":N:true:standard" is necessary so that the root volume 51 | # will be deleted on termination of the instance (specifically the "true" part) 52 | ami_name="$distribution-$codename-$arch-$name_suffix" 53 | log "Registering an AMI with the snapshot '$snapshot_id'" 54 | ami_id=`euca-register \ 55 | --name "$ami_name" --description "$description" \ 56 | --architecture "$ami_arch" --kernel "$aki" \ 57 | --snapshot "$snapshot_id:$volume_size:true:standard" \ 58 | | awk '{print $2}'` 59 | 60 | # If the user has already created an unnamed AMI today, 61 | # this will fail, so give the AMI registration command to the user 62 | if [[ ! "$ami_id" =~ ^ami-[0-9a-z]{8}$ ]]; then 63 | die \ 64 | "Unable to register an AMI." \ 65 | "You can do it manually with:" \ 66 | "`which euca-register` \\\\" \ 67 | "--name '$ami_name' --description '$description' \\\\" \ 68 | "--architecture '$ami_arch' --kernel '$aki' \\\\" \ 69 | "--snapshot '$snapshot_id:$volume_size:true:standard'" 70 | fi 71 | log "Your AMI has been created with the ID '$ami_id'" 72 | -------------------------------------------------------------------------------- /plugins/lvm-root-volume-tasks/95-register-ami: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Register the snapshot as a new AMI 3 | 4 | # Figure out which pvGrub kernel ID we need - make sure to use the hd00 kernel since we have partitioned the root disk... 5 | case $region in 6 | us-east-1) 7 | [ $arch = 'amd64' ] && aki="aki-b4aa75dd" 8 | [ $arch = 'i386' ] && aki="aki-b2aa75db" 9 | ;; 10 | us-west-1) 11 | [ $arch = 'amd64' ] && aki="aki-eb7e26ae" 12 | [ $arch = 'i386' ] && aki="aki-e97e26ac" 13 | ;; 14 | us-west-2) 15 | [ $arch = 'amd64' ] && aki="aki-f837bac8" 16 | [ $arch = 'i386' ] && aki="aki-f637bac6" 17 | ;; 18 | eu-west-1) 19 | [ $arch = 'amd64' ] && aki="aki-8b655dff" 20 | [ $arch = 'i386' ] && aki="aki-89655dfd" 21 | ;; 22 | ap-southeast-1) 23 | [ $arch = 'amd64' ] && aki="aki-fa1354a8" 24 | [ $arch = 'i386' ] && aki="aki-f41354a6" 25 | ;; 26 | ap-southeast-2) 27 | [ $arch = 'amd64' ] && aki="aki-31990e0b" 28 | [ $arch = 'i386' ] && aki="aki-33990e09" 29 | ;; 30 | ap-northeast-1) 31 | [ $arch = 'amd64' ] && aki="aki-40992841" 32 | [ $arch = 'i386' ] && aki="aki-3e99283f" 33 | ;; 34 | sa-east-1) 35 | [ $arch = 'amd64' ] && aki="aki-c88f51d5" 36 | [ $arch = 'i386' ] && aki="aki-ce8f51d3" 37 | ;; 38 | us-gov-west-1) 39 | [ $arch = 'amd64' ] && aki="aki-75a4c056" 40 | [ $arch = 'i386' ] && aki="aki-77a4c054" 41 | ;; 42 | *) die "Unrecognized region:" "$region" 43 | esac 44 | 45 | 46 | [ $arch = 'i386' ] && ami_arch='i386' 47 | [ $arch = 'amd64' ] && ami_arch='x86_64' 48 | 49 | # The AMI has to start with "debian", otherwise we won't get a nice icon 50 | # The ":N:true:standard" is necessary so that the root volume 51 | # will be deleted on termination of the instance (specifically the "true" part) 52 | ami_name="$distribution-$codename-$arch-$name_suffix" 53 | log "Registering an AMI with the snapshot '$snapshot_id'" 54 | ami_id=`euca-register \ 55 | --name "$ami_name" --description "$description" \ 56 | --architecture "$ami_arch" --kernel "$aki" \ 57 | --snapshot "$snapshot_id:$volume_size:true:standard" \ 58 | --root-device-name /dev/sda \ 59 | | awk '{print $2}'` 60 | 61 | # If the user has already created an unnamed AMI today, 62 | # this will fail, so give the AMI registration command to the user 63 | if [[ ! "$ami_id" =~ ^ami-[0-9a-z]{8}$ ]]; then 64 | die \ 65 | "Unable to register an AMI." \ 66 | "You can do it manually with:" \ 67 | "`which euca-register` \\\\" \ 68 | "--name '$ami_name' --description '$description' \\\\" \ 69 | "--architecture '$ami_arch' --kernel '$aki' \\\\" \ 70 | "--snapshot '$snapshot_id:$volume_size:true:standard' \\\\" \ 71 | "--root-device-name /dev/sda" 72 | fi 73 | log "Your AMI has been created with the ID '$ami_id'" 74 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #*This project has been superseeded by [bootstrap-vz](https://github.com/andsens/bootstrap-vz) and is no longer maintained.*# 2 | 3 | ## Debian bootstrapping script for Amazon machine images and Google Compute Engine images ## 4 | 5 | This script bootstraps a vanilla Debian installation to create either 6 | an Amazon machine image or a Google Compute Engine image. 7 | The image contains no latent logfiles no .bash\_history or even the apt package cache. 8 | The machine configuration this script creates has been thoroughly tested. 9 | 10 | *This script has been tested on Debian squeeze and wheezy.* 11 | *To create an AMI this bootstrapper needs to be run on an Amazon EC2 instance.* 12 | 13 | ## Official AMIs provided by the Debian community and Amazon ## 14 | 15 | The Debian community and Amazon have together created AMIs using this bootstrapper 16 | and replicated them across AWS regions. The images have been tested for security and bugs. 17 | They are available on the [aws marketplace](https://aws.amazon.com/marketplace/pp/ref=csl_clsc_prd?sku=8fvdn95s5ev33cprr62nq3q7t). 18 | 19 | For each new official AMI a commit in this repository will be [tagged](https://github.com/andsens/build-debian-cloud/tags), 20 | marking the version the AMI was bootstrapped with. 21 | 22 | More information about these images and links to the gzipped volume images can be found on the 23 | [Debian wiki page](http://wiki.debian.org/Cloud/AmazonEC2Image/Squeeze) dedicated to those images. 24 | 25 | ## Usage ## 26 | 27 | The script is started with ``./build-debian-cloud``. 28 | You can choose to either bootstrap a Debian AMI (``./build-debian-cloud ec2``) 29 | or a Google Compute Engine image (``./build-debian-cloud gce``). 30 | Both modes have sensible defaults and can be configured with options and plugins. 31 | To see a list of options use ``--help``. 32 | When creating an AMI the script at least needs to know your AWS credentials. 33 | 34 | There are no interactive prompts, the bootstrapping can run entirely unattended 35 | from start till finish. 36 | 37 | Some plugins are included in the [plugins directory](https://github.com/andsens/build-debian-cloud/tree/master/plugins). 38 | A list of external plugins is also provided there. If none of those scratch 39 | your itch, you can of course [write your own plugin](https://github.com/andsens/build-debian-cloud/blob/master/plugins/HOWTO.md). 40 | 41 | ## Features ## 42 | 43 | ### AMI features ### 44 | 45 | * EBS booted 46 | * Base installation uses only 289MB 47 | * Base installation bootup time ~45s* (from AMI launch to SSH connectivity) 48 | * Support for both ext* and xfs 49 | * Uses standard Debian Xen kernel from apt 50 | * update-grub creates an actual menu.lst which pvGrub can read 51 | * ec2 system log is not cluttered by grub menu 52 | * ec2 startup scripts: 53 | * `ec2-get-credentials`: Copies the ec2 keypair to `~/.ssh/authorized_keys` 54 | * `ec2-run-user-data`: If the userdata starts with `#!` it will be executed 55 | * `generate-ssh-hostkeys`: Generates hostkeys for sshd on first boot 56 | * `expand-volume`: Expands the root partition to the volume size 57 | 58 | *\*The bootup time was measured with [this script](https://gist.github.com/3813743)* 59 | 60 | ### Bootstrapper (AMI) features ### 61 | 62 | * EBS volume is automatically created, mounted, formatted, unmounted, "snapshotted" and deleted 63 | * AMI is automatically registered with the right kernels for the current region of the host machine 64 | * Supports Debian squeeze and wheezy 65 | * Can create both 32-bit and 64-bit AMIs 66 | * Plugin system to keep the bootstrapping process automated 67 | * The process is divided into simple task based scripts 68 | * Uses only free software in accordance with the [Debian Social Contract](http://www.debian.org/social_contract). 69 | (In particular: The [EC2 API Tools](http://aws.amazon.com/developertools/351) 70 | have been replaced with [euca2ools](http://www.eucalyptus.com/download/euca2ools)) 71 | -------------------------------------------------------------------------------- /utils: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Define some colors 4 | txtdef="\e[0m" # revert to default color 5 | bldred="\e[1;31m" # bold red 6 | txtblu="\e[0;34m" # blue 7 | txtbld="\e[1m" # bold 8 | txtund="\e[4m" # underline 9 | 10 | logfile='' 11 | logcolor=$txtblu 12 | errfile='' 13 | errcolor=$bldred 14 | 15 | # Logging function without coloring 16 | function logplain { 17 | for line in "$@"; do 18 | out "$line\n" "$line\n" $logfile 19 | done 20 | } 21 | 22 | # Logging function, each arg is a line 23 | function log { 24 | for line in "$@"; do 25 | out "$logcolor$line$txtdef\n" "$line\n" $logfile 26 | done 27 | } 28 | 29 | # Log without the newline 30 | function logn { 31 | out "$logcolor$1$txtdef" "$1" $logfile 32 | } 33 | 34 | # Custom die function, output to stderr instead of stdout, each arg is a line 35 | function die { 36 | for line in "$@"; do 37 | out "$errcolor$line$txtdef\n" "$line\n" $errfile >&2 38 | done 39 | exit 1 40 | } 41 | 42 | # Output function, takes $msg, $filemsg and $file args in that order 43 | function out { 44 | printf -- "$1" 45 | if [ -n "$3" ]; then 46 | printf -- "$2" >>$3 47 | fi 48 | } 49 | 50 | # Wait for the execution of $cmd not to return an empty string 51 | function dotdot { 52 | local cmd=$1 53 | local status=`eval $cmd` 54 | local sleep=5 55 | [ ! -z "$2" ] && sleep=$2 56 | while [ -z "$status" ]; do 57 | logn '.' 58 | sleep $sleep 59 | # Don't error out if the command fails. 60 | status=`eval $cmd || true` 61 | done 62 | logn "\n" 63 | } 64 | 65 | # Turns once per newline that is piped into it 66 | function spin { 67 | local cursor='|' 68 | # Running `tput cols` in every iteration would cause significant slowdown. 69 | # Only do this once, the spinner is not a "need to have" anyways. 70 | local cols=$(( `tput cols` - 2 )) 71 | while read line; do 72 | printf -- "\r$logcolor$cursor$txtdef %-${cols}s" "${line:0:$cols}" 73 | case $cursor in 74 | '|') cursor='/' ;; 75 | '/') cursor='-' ;; 76 | '-') cursor='\\' ;; 77 | '\\') cursor='|' ;; 78 | esac 79 | done 80 | printf "\n" 81 | } 82 | 83 | function insert_task_before { 84 | if [ ! -e $2 ]; then 85 | die "Unable to add task `basename $2` to the tasklist." "$2 does not exist." 86 | fi 87 | if ! $(contains_basename $1 tasks[@]); then 88 | die "Unable to add `basename $2` to the tasklist." "Task $1 does not exist." 89 | fi 90 | local i=0 91 | for task in ${tasks[*]}; do 92 | if [ `basename $task` == "$1" ]; then 93 | log "Adding task `basename $2` before task `basename $task`" 94 | tasks[$i]=$2 95 | i=$[i+1] 96 | fi 97 | tasks[$i]=$task 98 | i=$[i+1] 99 | done 100 | } 101 | 102 | function insert_task_after { 103 | if [ ! -e $2 ]; then 104 | die "Unable to add task `basename $2` to the tasklist." "$2 does not exist." 105 | fi 106 | if ! $(contains_basename $1 tasks[@]); then 107 | die "Unable to add `basename $2` to the tasklist." "Task $1 does not exist." 108 | fi 109 | local i=0 110 | for task in ${tasks[*]}; do 111 | tasks[$i]=$task 112 | i=$[i+1] 113 | if [ `basename $task` == "$1" ]; then 114 | log "Adding task `basename $2` after task `basename $task`" 115 | tasks[$i]=$2 116 | i=$[i+1] 117 | fi 118 | done 119 | } 120 | 121 | function remove_task { 122 | if ! $(contains_basename $1 tasks[@]); then 123 | die "Unable to remove $1 from the tasklist." "The task does not exist." 124 | fi 125 | local i=0 126 | for task in ${tasks[*]}; do 127 | if [ `basename $task` == "$1" ]; then 128 | log "Removing task $1" 129 | else 130 | tasks[$i]=$task 131 | i=$[i+1] 132 | fi 133 | done 134 | unset tasks[${#tasks[@]}-1] 135 | } 136 | 137 | function contains { 138 | declare -a haystack=("${!2}") 139 | for needle in ${haystack[*]}; do 140 | [ "$needle" == "$1" ] && exit 0 141 | done 142 | exit 1 143 | } 144 | 145 | function contains_basename { 146 | declare -a haystack=("${!2}") 147 | for needle in ${haystack[*]}; do 148 | [ "`basename $needle`" == "$1" ] && exit 0 149 | done 150 | exit 1 151 | } 152 | -------------------------------------------------------------------------------- /plugins/HOWTO.md: -------------------------------------------------------------------------------- 1 | # Writing your own plugins # 2 | 3 | If you want to change the behavior of the bootstrapper you can either modify the tasks directly or write plugins. Writing plugins has several advantages. 4 | 5 | * You can easily include and exclude them with a command line option. 6 | * The code is held separate from the bare bones bootstrapping setup. 7 | * You don't need to fork this repo. 8 | * No need to merge updates into your own repo. 9 | * Easily share your plugins with others. 10 | 11 | All plugins specified when bootstrapping, will be sourced *before* any tasks are run. The plugins can modify the task list and add their own tasks. 12 | Tasks are simply paths to scripts. They will be sourced as well. 13 | I recommend namespacing the function and variable names in a task to avoid naming clashes. 14 | 15 | ## Adding tasks ## 16 | 17 | Adding tasks is quite easy. To have a custom task run before another, call `insert_task_before`. The first argument is one of the task variables [listed below](#task-variables). The second argument is the path to the task that is inserted. 18 | eg.: 19 | ``` 20 | insert_task_before $TASK_INITSCRIPTS "/$plugindir/add-puppet-init.sh" 21 | ``` 22 | 23 | To insert a task after any other task call `insert_task_after`. The arguments are the same. 24 | 25 | To remove a task, call `remove_task` with the basename of the script as an argument. 26 | ``` 27 | remove_task "40-networking" 28 | ``` 29 | 30 | ### Task variables ### 31 | 32 | The internal workings of the bootstrapper might change from time to time, this includes the filenames of the different tasks. There are however some fundamentals in the bootstrapping process that are not likely to ever change. You can hook your plugin up to those fundamentals, they are aliased in the form of variables that point to a task filename. 33 | 34 | * `TASK_PACKAGES`: Adds packages to the `packages` array (and `exclude_packages`) 35 | * `TASK_CREATE_VOLUME`: Creates the EBS volume 36 | * `TASK_MOUNT_VOLUME`: Mounts the EBS volume 37 | * `TASK_BOOTSTRAP`: Runs the bootstrapping process 38 | * `TASK_MOUNT_SPECIALS`: Mounts things like /dev/pts and /proc 39 | * `TASK_APT_SOURCES`: Sets the aptitude sources 40 | * `TASK_APT_UPGRADE`: Upgrades packages and fixes broken dependencies 41 | * `TASK_INITSCRIPTS`: Installs the init.d scripts 42 | * `TASK_UNMOUNT_SPECIALS`: Unmounts things like /dev/pts and /proc 43 | * `TASK_UNMOUNT_VOLUME`: Unmounts the EBS volume 44 | * `TASK_SNAPSHOT`: Creates a snapshot of the EBS volume 45 | * `TASK_DELETE_VOLUME`: Deletes the EBS volume 46 | * `TASK_CREATE_AMI`: Registers the snapshot as an AMI 47 | 48 | ### Internal variables ### 49 | 50 | If you want to install additional packages, simply append them to the `packages` variable. 51 | The `exclude_packages` excludes packages that would otherwise have been installed. 52 | 53 | If you need to install init.d scripts, simply add their path to the `init_scripts` variable and they will be automatically installed. 54 | 55 | You can append to an array in bash by doing this: 56 | ``` 57 | packages+=('vim') 58 | ``` 59 | 60 | __Other useful variables__: 61 | 62 | * `host_packages`: Packages to be installed on the host, works just like `packages`. 63 | * `scriptdir`: Holds the path to the bootstrapping script folder. 64 | * `imagedir`: The path to where the EBS volume is mounted. 65 | * `plugindir`: When adding tasks, this is the directory where the script is stored. This avoids some quirky bash magic. 66 | * `taskdir`: When a task is run, this variable points to the directory in which the task is located. 67 | * There are a lot of other variables, they are all declared on the first 50 lines in `ec2-debian-build-ami` 68 | 69 | ## Simple plugins ## 70 | If your plugin is really simple, you may not need to modify the task list. The `packages`, `exclude_packages` and `init_scripts` arrays are already declared when your plugin file is sourced. Removing nano and adding vim to the bootstrap process can be done with: 71 | ``` 72 | packages+=('vim') 73 | exclude_packages+=('nano') 74 | ``` 75 | 76 | ## Utility functions ## 77 | * `log`: Logs to the screen with blue text. Every parameter will be printed on a new line. 78 | * `die`: Kills the bootstrapping process with a message. Prints to stderr. 79 | * `spin`: Pipe into this function if you are running stuff that fills up the screen with verbose information. Every line of output will be printed on the same line in the console. 80 | -------------------------------------------------------------------------------- /gce: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Google cloud info 4 | gcs_dest= 5 | gce_project= 6 | 7 | # Image details 8 | image_name_suffix=v$(date +%Y%m%d) 9 | build_date=$(date +%Y-%m-%d) 10 | volume_size='10' 11 | apt_mirrors='http://gce_debian_mirror.storage.googleapis.com/ http://http.debian.net/debian' 12 | backports_mirrors='http://gce_debian_mirror.storage.googleapis.com/ http://ftp.debian.org/debian' 13 | use_backports_packages=false 14 | gce_kernel=projects/google/global/kernels/gce-v20130813 15 | 16 | # $description is set later if it has no value yet. This is 17 | # just for the help output. 18 | default_description="Debian GNU/Linux X.Y.Z (codename) built on YYYY-MM-DD" 19 | 20 | # List of options for gce subcommand 21 | help="build-debian-cloud gce 22 | This script creates a Debian Squeeze Amazon Machine Image. 23 | 24 | Options (defaults in ${txtbld}bold${txtdef}): 25 | 26 | ${txtund}Bootstrapping${txtdef} 27 | --arch ARCH [i386|amd64] Processor architecture of the image (${txtbld}${arch}${txtdef}) 28 | --codename C [squeeze|wheezy] Debian version codename to bootstrap (${txtbld}${codename}${txtdef}) 29 | --filesystem FS [ext2..4|xfs] Filesystem of the root volume (${txtbld}${filesystem}${txtdef}) 30 | --volume-size SIZE Default size of the root volume in GB (${txtbld}${volume_size}${txtdef}) 31 | --plugin FILE Path to plugin script. 32 | Can be specified more than once. 33 | 34 | --timezone ZONE Standard timezone (${txtbld}${timezone}${txtdef}) 35 | --locale LOCALE Standard locale (${txtbld}${locale}${txtdef}) 36 | --charmap CHARMAP Standard charmap (${txtbld}${charmap}${txtdef}) 37 | 38 | --name SUFFIX Image name suffix (${txtbld}${name_suffix}${txtdef}) 39 | --description DESC Description of the image (${txtbld}${default_description}${txtdef}) 40 | --apt-mirrors \"URL[ URL...]\" APT mirror URLs, space-separated (${txtbld}${apt_mirrors}${txtdef}) 41 | --backports-mirrors \"URL[ URL...]\" Backports APT mirror URLs, space-separated (${txtbld}${apt_mirrors}${txtdef}) 42 | --use-backports-packages [true|false] True if backports packages should be used (${txtbld}${apt_mirrors}${txtdef}) 43 | 44 | ${txtund}GCE${txtdef} 45 | --gcs-dest URL Google Cloud Storage image destination URL prefix (${txtbld}${gcs_dest}${txtdef}) 46 | --gce-project PROJECT Google Compute Engine image destination project (${txtbld}${gce_project}${txtdef}) 47 | --gce-kernel KERNEL Google Compute Engine image kernel (${txtbld}${gce_kernel}${txtdef}) 48 | 49 | ${txtund}Other options${txtdef} 50 | --debug Print debugging information 51 | --help Prints this help message 52 | " 53 | 54 | # Run through the parameters and save them to variables. 55 | while [ $# -gt 0 ]; do 56 | case $1 in 57 | --arch) arch=$2; shift 2 ;; 58 | --codename) codename=$2; shift 2 ;; 59 | --filesystem) filesystem=$2; shift 2 ;; 60 | --volume-size) volume_size=$2; shift 2 ;; 61 | --name) name_suffix=$2; shift 2 ;; 62 | --description) description=$2; shift 2 ;; 63 | --apt-mirrors) apt_mirrors=$2; shift 2 ;; 64 | --backports-mirrors) backports_mirrors=$2; shift 2 ;; 65 | --use-backports-packages) use_backports_packages=$2; shift 2 ;; 66 | --gcs-dest) gcs_dest=$2; shift 2 ;; 67 | --gce-project) gce_project=$2; shift 2 ;; 68 | --gce-kernel) gce_kernel=$2; shift 2 ;; 69 | --timezone) timezone=$2; shift 2 ;; 70 | --locale) locale=$2; shift 2 ;; 71 | --charmap) charmap=$2; shift 2 ;; 72 | --plugin) plugins+=("$2"); shift 2 ;; 73 | --debug) set -x; shift ;; 74 | -h|--help) printf -- "$help"; exit 0 ;; 75 | *) die "Unrecognized option: $1" \ 76 | "Type '$0 --help' to see a list of possible options"; ;; 77 | esac 78 | done 79 | 80 | 81 | # Specify points in the bootstrapping process, which the plugins can latch onto. 82 | # This way plugins don't need fixing if we rename files. 83 | TASK_PACKAGES="01-packages" 84 | TASK_CREATE_VOLUME="10-create-volume" 85 | TASK_MOUNT_VOLUME="13-mount-volume" 86 | TASK_BOOTSTRAP="14-bootstrap" 87 | TASK_MOUNT_SPECIALS="15-mount-specials" 88 | TASK_APT_SOURCES="21-apt-sources" 89 | TASK_APT_UPGRADE="22-apt-upgrade" 90 | TASK_UNMOUNT_SPECIALS="71-unmount-specials" 91 | TASK_UNMOUNT_VOLUME="72-unmount-volume" 92 | TASK_DELETE_VOLUME="73-delete-loopback" 93 | TASK_REGISTER_IMAGE="95-register-image" 94 | -------------------------------------------------------------------------------- /ec2: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # AMI details 4 | ami_id= 5 | name_suffix=$(date +%Y%m%d) 6 | description= 7 | snapshot_id= 8 | volume_id= 9 | volume_size='1' 10 | apt_mirrors='http://http.debian.net/debian' 11 | 12 | # EC2 known regions 13 | known_regions=('us-east-1') 14 | known_regions+=('us-west-1') 15 | known_regions+=('us-west-2') 16 | known_regions+=('eu-west-1') 17 | known_regions+=('ap-southeast-1') 18 | known_regions+=('ap-southeast-2') 19 | known_regions+=('ap-northeast-1') 20 | known_regions+=('sa-east-1') 21 | known_regions+=('us-gov-west-1') 22 | 23 | # EC2 host info 24 | instance_id= 25 | region= 26 | availability_zone= 27 | 28 | # Build-time info 29 | debootstrap_dir='/target' 30 | 31 | # List of options for ec2 subcommand 32 | help="build-debian-cloud ec2 33 | This subcommand creates a Debian Squeeze Amazon Machine Image. 34 | 35 | Options (defaults in ${txtbld}bold${txtdef}): 36 | 37 | ${txtund}Bootstrapping${txtdef} 38 | --arch ARCH [i386|amd64] Processor architecture of the image (${txtbld}${arch}${txtdef}) 39 | --codename C [squeeze|wheezy] Debian version codename to bootstrap (${txtbld}${codename}${txtdef}) 40 | --filesystem FS [ext2..4|xfs] Filesystem of the root volume (${txtbld}${filesystem}${txtdef}) 41 | --volume-size SIZE Default size of the root volume in GB (${txtbld}${volume_size}${txtdef}) 42 | --plugin FILE Path to plugin script. 43 | Can be specified more than once. 44 | 45 | --timezone ZONE Standard timezone (${txtbld}${timezone}${txtdef}) 46 | --locale LOCALE Standard locale (${txtbld}${locale}${txtdef}) 47 | --charmap CHARMAP Standard charmap (${txtbld}${charmap}${txtdef}) 48 | 49 | --debootstrap-dir DIR Debootstrap directory (${txtbld}${debootstrap_dir}${txtdef}) 50 | The created volume will be mounted 51 | at \$debootstrap_dir/\$volume_id. 52 | 53 | --name SUFFIX AMI name suffix (${txtbld}${name_suffix}${txtdef}) 54 | --description DESC Description of the AMI 55 | --apt-mirrors \"URL[ URL...]\" APT mirror URLs, space-separated (${txtbld}${apt_mirrors}${txtdef}) 56 | 57 | ${txtund}AWS${txtdef} 58 | --access-key ID AWS Access Key (${txtbld}\$EC2_ACCESS_KEY${txtdef}) 59 | --secret-key KEY AWS Secret Key (${txtbld}\$EC2_SECRET_KEY${txtdef}) 60 | 61 | ${txtund}Other options${txtdef} 62 | --debug Print debugging information 63 | --help Prints this help message 64 | " 65 | 66 | # Run through the parameters and save them to variables. 67 | while [ $# -gt 0 ]; do 68 | case $1 in 69 | --arch) arch=$2; shift 2 ;; 70 | --codename) codename=$2; shift 2 ;; 71 | --filesystem) filesystem=$2; shift 2 ;; 72 | --volume-size) volume_size=$2; shift 2 ;; 73 | --name) name_suffix=$2; shift 2 ;; 74 | --description) description=$2; shift 2 ;; 75 | --apt-mirrors) apt_mirrors=$2; shift 2 ;; 76 | --access-key) EC2_ACCESS_KEY=$2; shift 2 ;; 77 | --secret-key) EC2_SECRET_KEY=$2; shift 2 ;; 78 | --timezone) timezone=$2; shift 2 ;; 79 | --locale) locale=$2; shift 2 ;; 80 | --charmap) charmap=$2; shift 2 ;; 81 | --debootstrap-dir) debootstrap_dir=$2; shift 2 ;; 82 | --plugin) plugins+=("$2"); shift 2 ;; 83 | --debug) set -x; shift ;; 84 | -h|--help) printf -- "$help"; exit 0 ;; 85 | *) die "Unrecognized option: $1" \ 86 | "Type '$0 --help' to see a list of possible options"; ;; 87 | esac 88 | done 89 | 90 | 91 | # Required and default parameters 92 | true ${EC2_ACCESS_KEY:?} ${EC2_SECRET_KEY:?} 93 | 94 | # Export the AWS credentials, so that we can launch ec2-tools without specifying them 95 | export EC2_ACCESS_KEY EC2_SECRET_KEY 96 | 97 | # Specify points in the bootstrapping process, which the plugins can latch onto. 98 | # This way plugins don't need fixing if we rename files. 99 | TASK_PACKAGES="01-packages" 100 | TASK_CREATE_VOLUME="10-create-volume" 101 | TASK_MOUNT_VOLUME="13-mount-volume" 102 | TASK_MOUNT_SPECIALS="15-mount-specials" 103 | TASK_BOOTSTRAP="14-bootstrap" 104 | TASK_APT_SOURCES="21-apt-sources" 105 | TASK_APT_UPGRADE="22-apt-upgrade" 106 | TASK_INITSCRIPTS="51-init-scripts" 107 | TASK_UNMOUNT_SPECIALS="71-unmount-specials" 108 | TASK_UNMOUNT_VOLUME="72-unmount-volume" 109 | TASK_SNAPSHOT="80-ebs-snapshot" 110 | TASK_DELETE_VOLUME="82-delete-volume" 111 | TASK_CREATE_AMI="95-register-ami" 112 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### 2013-01-23 Anders Ingemann ### 2 | 3 | #### Major changes: #### 4 | * The root volume is once again set to be deleted upon termination of the instance (bug introduced in 5f04e809d7, see issue #42) 5 | * New plugin to publish an AMI 6 | * New plugin to publish a Snapshot 7 | 8 | ### 2013-01-21 Anders Ingemann ### 9 | 10 | #### Major changes: #### 11 | * Added support for wheezy 12 | * Implemented support for additional tasks based on the debian version codename 13 | 14 | #### Minor changes: #### 15 | * List operations have been refactored (tasks, known_regions) 16 | * Prevent two tasks from having the same basename 17 | * Fixed a bug where the help message for registering an AMI was not displayed correctly 18 | 19 | ### 2013-01-17 Anders Ingemann ### 20 | 21 | #### Major changes: #### 22 | * Fixed a bug that prevented boto from being patched that was introduced when switching to wget in 0d96ebc83e 23 | 24 | #### Minor changes: #### 25 | * Check `$PIPESTATUS` to make sure the boto patch was successful 26 | * Use sudoers.d in `admin-user` plugin (by @tmatilai) 27 | * Install `grub-pc` instead of the `grub` dummypackage (by @tmatilai) 28 | * Verify the AMI ID with a regex 29 | 30 | ### 2012-11-19 Anders Ingemann ### 31 | 32 | #### Major changes: #### 33 | * ext4 is now the default filesystem 34 | 35 | #### Minor changes: #### 36 | * `wget` is installed per default on debian, use that instead 37 | * Alternative `-h` help param for packaging 38 | * Fallback to `/usr/share/ec2debian-build-ami` when tasks dir is not found 39 | * Disable the grub boot menu and respect some config vars in /etc/default/grub 40 | 41 | #### Bugfixes: #### 42 | * Fixed an issue where the bootstrapper would halt if a task ended in an if statement that failed 43 | * The mountpoint was announced twice 44 | 45 | ### 2012-11-18 Anders Ingemann ### 46 | 47 | #### Major changes: #### 48 | * Add `change-root-uuid` script 49 | (see [this issue for more info](https://github.com/andsens/build-debian-cloud/issues/40)) 50 | * Remove contrib and non-free from apt/sources.list 51 | (thanks to zack for finding this and James for the patch) 52 | 53 | #### Bugfixes: #### 54 | * Autoapply patch to boto 55 | (thanks to James for the patch) 56 | * Fix bug in `generate-ssh-hostkeys` 57 | 58 | ### 2012-11-15 Anders Ingemann ### 59 | 60 | #### Major changes: #### 61 | * Don't attach ephemeral volumes, they can be added in the AWS console now 62 | 63 | #### Minor changes: #### 64 | * Remove hardcoded location specific apt mirror and use geo redirector instead 65 | * Fail if euca2ools make fails 66 | * Only install euca2ools if they are not installed already or have the wrong version 67 | 68 | ### 2012-11-12 Anders Ingemann ### 69 | 70 | #### Major changes: #### 71 | * Replace ec2-a[mp]i-tools with euca2ools 72 | 73 | #### Minor changes: #### 74 | * Added regions: ap-southeast-2 (Australia) 75 | * Changed the way `11-attach-volume` finds a free device letter 76 | 77 | ### 2012-11-10 Anders Ingemann ### 78 | 79 | #### Major changes: #### 80 | * Plugin removed: "ec2-run-user-data" is back in the standard bootstrapping process and has been simplified 81 | * New init.d script "expand-volume": Try to grow the root volume size when booting 82 | 83 | #### Minor changes: #### 84 | * Rename "ec2-ssh-host-keys-gen" to "generate-ssh-hostkeys" and refactor 85 | * Plugin modified: "ec2-user" renamed to "admin-user". Login name is now "admin" 86 | * Fix help message on AMI registration failure 87 | 88 | ### 2012-11-06 Anders Ingemann ### 89 | 90 | #### Major changes: #### 91 | * New plugin: "ec2-user". It creates a normal unix user. root ssh login will be disabled, with this plugin. Fixes #31 92 | * New plugin: "remount". It remounts the bootstrapped volume 93 | * Rename task variables to fit common scheme 94 | * Split up tasks, to get better granularity 95 | * Refactor of ec2-get-credentials 96 | * Update PV-Grub AKI IDs, reorder some regions and add us-gov-west-1 97 | 98 | #### Minor changes: #### 99 | * Add Apache Software License 2.0 Include the license for ec2ubuntu, the original script 100 | * Create /etc/timezone with cat 101 | * Append to /etc/network/interfaces, don't overwrite 102 | * The /tmp dir isn't missing, don't create it 103 | * Append to fstab, don't overwrite 104 | * Make `cat` commands more readable 105 | * Clean up hostkeys. Simplifies ec2-ssh-host-key-gen a LOT. 106 | * Don't delete root password, it's not set to begin with. 107 | * Use printf instead of echo 108 | * When bootstrapping fails jump to $originaldir instead of $scriptdir 109 | * Don't overwrite /etc/hosts, the original works just fine. 110 | * Log when a task is removed 111 | * Introduce logplain, logging without color 112 | * Move networking into 30s group 113 | * Format standard-packages the same way as the other plugins. 114 | * Fixed bug in task functions. No error was displayed when removing or adding a non-existent task. 115 | * Minor fixes to utils 116 | -------------------------------------------------------------------------------- /plugins/publish-snapshot-tasks/euca-modify-snapshot-attribute: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | # Software License Agreement (BSD License) 5 | # 6 | # Copyright (c) 2009, Eucalyptus Systems, Inc. 7 | # All rights reserved. 8 | # 9 | # Redistribution and use of this software in source and binary forms, with or 10 | # without modification, are permitted provided that the following conditions 11 | # are met: 12 | # 13 | # Redistributions of source code must retain the above 14 | # copyright notice, this list of conditions and the 15 | # following disclaimer. 16 | # 17 | # Redistributions in binary form must reproduce the above 18 | # copyright notice, this list of conditions and the 19 | # following disclaimer in the documentation and/or other 20 | # materials provided with the distribution. 21 | # 22 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 23 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 | # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 26 | # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 27 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 28 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 29 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 30 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 | # POSSIBILITY OF SUCH DAMAGE. 33 | # 34 | # Author: Neil Soman neil@eucalyptus.com 35 | 36 | import getopt 37 | import sys 38 | import os 39 | from euca2ools import Euca2ool, SnapshotValidationError, Util, \ 40 | ConnectionFailed 41 | 42 | usage_string = \ 43 | """ 44 | Modify attributes for a snapshot. 45 | 46 | euca-modify-snapshot-attribute [-c, --create-volume-permission] 47 | [-a, --add entity ] [-r, --remove entity] 48 | [-h, --help] [--version] [--debug] snapshot_id 49 | 50 | REQUIRED PARAMETERS 51 | 52 | snapshot_id Unique identifier for the snapshot that you want to modify the attributes for. 53 | 54 | OPTIONAL PARAMETERS 55 | 56 | -c, --create-volume-permission Create volume permissions. 57 | 58 | -a, --add Entity (typically, user id) to add. 59 | 60 | -r, --remove Entity (typically, user id) to remove. 61 | """ 62 | 63 | 64 | def usage(status=1): 65 | print usage_string 66 | Util().usage(compat=True) 67 | sys.exit(status) 68 | 69 | 70 | def version(): 71 | print Util().version() 72 | sys.exit() 73 | 74 | 75 | def main(): 76 | euca = None 77 | try: 78 | euca = Euca2ool('c:a:r:', ['create-volume-permission', 'add=', 'remove='], compat=True) 79 | except Exception, e: 80 | print e 81 | usage() 82 | 83 | snapshot_id = None 84 | entity = None 85 | snapshot_attribute = None 86 | operation_type = None 87 | user_ids = None 88 | groups = None 89 | 90 | for (name, value) in euca.opts: 91 | if name in ('-l', '--create-volume-permission'): 92 | if not snapshot_attribute: 93 | snapshot_attribute = 'createVolumePermission' 94 | elif name in ('-a', '--add'): 95 | entity = value 96 | operation_type = 'add' 97 | elif name in ('-r', '--remove'): 98 | entity = value 99 | operation_type = 'remove' 100 | elif name in ('-h', '--help'): 101 | usage(0) 102 | elif name == '--version': 103 | version() 104 | 105 | for arg in euca.args: 106 | snapshot_id = arg 107 | break 108 | 109 | if snapshot_attribute == 'createVolumePermission': 110 | if entity == 'all': 111 | groups = [entity] 112 | else: 113 | user_ids = [entity] 114 | 115 | if snapshot_id and snapshot_attribute: 116 | try: 117 | euca.validate_snapshot_id(snapshot_id) 118 | except SnapshotValidationError: 119 | print 'Invalid snapshot id' 120 | sys.exit(1) 121 | 122 | try: 123 | euca_conn = euca.make_connection() 124 | except ConnectionFailed, e: 125 | print e.message 126 | sys.exit(1) 127 | try: 128 | return_code = euca_conn.modify_snapshot_attribute( 129 | snapshot_id=snapshot_id, 130 | attribute=snapshot_attribute, 131 | operation=operation_type, 132 | user_ids=user_ids, 133 | groups=groups, 134 | ) 135 | except Exception, ex: 136 | euca.display_error_and_exit('%s' % ex) 137 | 138 | if return_code: 139 | print 'SNAPSHOT\t%s' % snapshot_id 140 | else: 141 | if not snapshot_id: 142 | print 'snapshot_id must be specified' 143 | if not snapshot_attribute: 144 | print '-c must be specified' 145 | usage() 146 | 147 | 148 | if __name__ == '__main__': 149 | main() 150 | --------------------------------------------------------------------------------