├── .gitignore ├── .gitmodules ├── LICENSE.md ├── README.md ├── Vagrantfile ├── config └── ansible.hosts ├── packer.json ├── preseeds └── preseed.cfg ├── requirements.txt └── scripts ├── clean.sh └── minimize.sh /.gitignore: -------------------------------------------------------------------------------- 1 | /builds 2 | /packer_cache 3 | /.vagrant 4 | /output-virtualbox-iso 5 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "ansible"] 2 | path = ansible 3 | url = https://github.com/tootsuite/mastodon-ansible 4 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ===================== 3 | 4 | Copyright © 2017-2018 Moritz Heiber 5 | 6 | Permission is hereby granted, free of charge, to any person 7 | obtaining a copy of this software and associated documentation 8 | files (the “Software”), to deal in the Software without 9 | restriction, including without limitation the rights to use, 10 | copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the 12 | Software is furnished to do so, subject to the following 13 | conditions: 14 | 15 | The above copyright notice and this permission notice shall be 16 | included in all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | OTHER DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # A VirtualBox image for Mastodon development 2 | 3 | This VirtualBox base image contains all the required packages and configuration for running/instantiating a Mastodon instance for development. It is build using [Hashicorp's Packer](https://packer.io), the provisioning is done with Ansible through a dedicated git submodule called [mastodon-ansible](https://github.com/moritzheiber/mastodon-ansible). The tests are using [ServerSpec](https://serverspec.org). It's made available through [Hashicorp's Vagrant Cloud](https://app.vagrantup.com) at [`mastodon/ubuntu-xenial64`](https://app.vagrantup.com/mastodon/boxes/ubuntu-xenial64). 4 | 5 | _Note: Some of the content of the scripts in `scripts/` is borrowed from the [Bento](https://github.com/chef/bento) project._ 6 | 7 | ## Prerequisites (for building on your own) 8 | 9 | - VirtualBox >= 5.1.x 10 | - Packer >= 1.0.0 11 | - Python >= 2.x 12 | - pip/python-pip >= 8.x 13 | 14 | for testing purposes: 15 | 16 | - Vagrant >= 2.0.3 17 | 18 | ## Setup 19 | 20 | ```sh 21 | $ virtualenv env 22 | $ source env/bin/activate 23 | $ pip install -r requirements.txt 24 | ``` 25 | 26 | ## Building the box locally 27 | 28 | Just execute: 29 | 30 | ```sh 31 | $ packer build packer.json 32 | ``` 33 | 34 | _Note: It will take at least roughly 5 - 10 minutes for the ISO to get preseeded ("pre-provisioned") by the Debian installer, hence the rather long timeout/waiting period before packer actually starts provisioning. If you're unsure whether there is any progress change the values `headless` in the `packer.json` from `true` to `false` and re-run the process. This will give have VirtualBox show you the output of the console the ISO is running on._ 35 | 36 | This will preseed the Ubuntu ISO image for Ubuntu Xenial 64bit with a couple of sane defaults and packages. Afterwards, the Ansible provisioner is run using the playbooks from the [mastodon-ansible](https://github.com/moritzheiber/mastodon-ansible) repository. 37 | 38 | In the end you should have a box in `builds/` with all the required components installed you can run directly in Vagrant. 39 | 40 | ## Publishing to Vagrant Cloud 41 | 42 | _Note: You need a valid access token to submit new versions of the box to Vagrant Cloud. It will not work otherwise. There is a organization called `mastodon` which is responsible for the Vagrant boxes under the same namespace. If you think you can/want to contribute send me a message on @moritzheiber@mastodon.social and I'll add your to the organization._ 43 | 44 | If you have a valid `VAGRANT_CLOUD_TOKEN` you can use the `vagrant-cloud` post-processor to upload a new version to Vagrant Cloud. It'll be done automatically if the token is set: 45 | 46 | ``` 47 | $ VAGRANT_CLOUD_TOKEN="my-token" packer build packer.json 48 | ``` 49 | 50 | ## Testing 51 | 52 | TODO 53 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | Vagrant.configure('2') do |config| 2 | config.vm.box = 'builds/virtualbox-mastodon-ubuntu1604.box' 3 | config.vm.box_check_update = true 4 | config.vm.network 'private_network', type: 'dhcp' 5 | end 6 | -------------------------------------------------------------------------------- /config/ansible.hosts: -------------------------------------------------------------------------------- 1 | localhost ansible_connection=local 2 | -------------------------------------------------------------------------------- /packer.json: -------------------------------------------------------------------------------- 1 | { 2 | "variables": { 3 | "user": "vagrant", 4 | "password": "vagrant" 5 | }, 6 | "provisioners": [ 7 | { 8 | "type": "shell", 9 | "inline": [ 10 | "sudo apt-get update -qq", 11 | "sudo apt-get install -y git-core python-pip python-apt libffi-dev libssl-dev build-essential python-virtualenv python-setuptools", 12 | "mkdir -p /tmp/mastodon", 13 | "git clone https://github.com/tootsuite/mastodon-ansible /tmp/mastodon/ansible" 14 | ] 15 | }, 16 | { "type": "shell", 17 | "inline": [ 18 | "install -m0700 -o {{user `user`}} -g {{user `user`}} -d /home/{{user `user`}}/.ssh", 19 | "curl -SsL https://raw.githubusercontent.com/mitchellh/vagrant/master/keys/vagrant.pub -o /home/{{user `user`}}/.ssh/authorized_keys" 20 | ] 21 | }, 22 | { "type": "shell", 23 | "execute_command": "{{ .Vars }} sudo -E -S sh '{{ .Path }}'", 24 | "inline": [ 25 | "mount -o loop ${HOME}/VBoxGuestAdditions.iso /mnt", 26 | "/mnt/VBoxLinuxAdditions.run install", 27 | "umount /mnt", 28 | "rm ${HOME}/VBoxGuestAdditions.iso" 29 | ] 30 | }, 31 | { 32 | "type": "shell", 33 | "inline": [ 34 | "virtualenv venv", 35 | ". venv/bin/activate", 36 | "pip install --upgrade ansible==2.2.2.0" 37 | ] 38 | }, 39 | { 40 | "type": "file", 41 | "source": "config/ansible.hosts", 42 | "destination": "/tmp/mastodon/ansible.hosts" 43 | }, 44 | { 45 | "type": "shell", 46 | "environment_vars": ["ANSIBLE_REMOTE_TEMP=\"/tmp/mastodon/.ansible-tmp\"", "ANSIBLE_SSH_PIPELINING=True" ], 47 | "inline": [ 48 | ". venv/bin/activate", 49 | "ansible-playbook -i /tmp/mastodon/ansible.hosts -v -b --extra-vars mastodon_db_password=CHANGEME /tmp/mastodon/ansible/bare/playbook.yml" 50 | ] 51 | }, 52 | { 53 | "type": "shell", 54 | "execute_command": "{{ .Vars }} sudo -E -S sh '{{ .Path }}'", 55 | "scripts": ["scripts/clean.sh", "scripts/minimize.sh"] 56 | } 57 | ], 58 | "builders": [ 59 | { 60 | "type": "virtualbox-iso", 61 | "boot_command": [ 62 | "", 63 | "", 64 | "", 65 | "", 66 | "/install/vmlinuz ", 67 | " auto ", 68 | " console-setup/ask_detect=false ", 69 | " console-setup/layoutcode=us ", 70 | " console-setup/modelcode=pc105 ", 71 | " debconf/frontend=noninteractive ", 72 | " debian-installer=en_US.UTF-8 ", 73 | " fb=false ", 74 | " initrd=/install/initrd.gz ", 75 | " kbd-chooser/method=us ", 76 | " keyboard-configuration/layout=USA ", 77 | " keyboard-configuration/variant=USA ", 78 | " locale=en_US.UTF-8 ", 79 | " netcfg/get_domain=vm ", 80 | " netcfg/get_hostname=vagrant ", 81 | " grub-installer/bootdev=/dev/sda ", 82 | " noapic ", 83 | " preseed/url=http://{{ .HTTPIP }}:{{ .HTTPPort }}/preseed.cfg", 84 | " -- ", 85 | "" 86 | ], 87 | "boot_wait": "10s", 88 | "disk_size": 10000, 89 | "guest_os_type": "Ubuntu_64", 90 | "headless": true, 91 | "http_directory": "preseeds", 92 | "iso_urls": "http://releases.ubuntu.com/16.04/ubuntu-16.04.4-server-amd64.iso", 93 | "iso_checksum_type": "sha256", 94 | "iso_checksum": "0a03608988cfd2e50567990dc8be96fb3c501e198e2e6efcb846d89efc7b89f2", 95 | "ssh_username": "{{user `user`}}", 96 | "ssh_password": "{{user `password`}}", 97 | "ssh_wait_timeout": "20m", 98 | "shutdown_command": "echo 'vagrant'|sudo -S shutdown -P now", 99 | "vm_name": "packer-ubuntu-16.04-amd64", 100 | "vboxmanage": [ 101 | [ 102 | "modifyvm", 103 | "{{.Name}}", 104 | "--memory", 105 | "1024" 106 | ], 107 | [ 108 | "modifyvm", 109 | "{{.Name}}", 110 | "--cpus", 111 | "1" 112 | ] 113 | ] 114 | } 115 | ], 116 | "post-processors": [ 117 | [ 118 | { 119 | "output": "builds/{{.Provider}}-mastodon-ubuntu1604.box", 120 | "type": "vagrant" 121 | }, 122 | { 123 | "type": "vagrant-cloud", 124 | "box_tag": "mastodon/ubuntu-xenial64", 125 | "version": "0.5.{{timestamp}}" 126 | } 127 | ] 128 | ], 129 | "push": { 130 | "name": "mastodon/ubuntu-xenial64", 131 | "vcs": true 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /preseeds/preseed.cfg: -------------------------------------------------------------------------------- 1 | choose-mirror-bin mirror/http/proxy string 2 | d-i base-installer/kernel/override-image string linux-server 3 | d-i clock-setup/utc boolean true 4 | d-i clock-setup/utc-auto boolean true 5 | d-i finish-install/reboot_in_progress note 6 | d-i grub-installer/only_debian boolean true 7 | d-i grub-installer/with_other_os boolean true 8 | d-i partman-auto-lvm/guided_size string max 9 | d-i partman-auto/choose_recipe select atomic 10 | d-i partman-auto/method string lvm 11 | d-i partman-lvm/confirm boolean true 12 | d-i partman-lvm/confirm boolean true 13 | d-i partman-lvm/confirm_nooverwrite boolean true 14 | d-i partman-lvm/device_remove_lvm boolean true 15 | d-i partman/choose_partition select finish 16 | d-i partman/confirm boolean true 17 | d-i partman/confirm_nooverwrite boolean true 18 | d-i partman/confirm_write_new_label boolean true 19 | d-i pkgsel/include string openssh-server linux-source dkms nfs-common curl 20 | d-i pkgsel/install-language-support boolean false 21 | d-i pkgsel/update-policy select none 22 | d-i pkgsel/upgrade select full-upgrade 23 | d-i time/zone string UTC 24 | tasksel tasksel/first multiselect standard, ubuntu-server 25 | 26 | d-i console-setup/ask_detect boolean false 27 | d-i keyboard-configuration/layoutcode string us 28 | d-i keyboard-configuration/modelcode string pc105 29 | d-i debian-installer/locale string en_US.UTF-8 30 | 31 | # Set the root password 32 | d-i passwd/root-login boolean true 33 | d-i passwd/root-password password vagrant 34 | d-i passwd/root-password-again password vagrant 35 | 36 | # Setup the user account 37 | d-i passwd/user-fullname string vagrant 38 | d-i passwd/username string vagrant 39 | d-i passwd/user-password password vagrant 40 | d-i passwd/user-password-again password vagrant 41 | d-i user-setup/allow-password-weak boolean true 42 | d-i user-setup/encrypt-home boolean false 43 | d-i passwd/user-uid string 900 44 | d-i preseed/late_command string \ 45 | echo 'vagrant ALL=(ALL) NOPASSWD: ALL' > /target/etc/sudoers.d/vagrant ; \ 46 | in-target chmod 440 /etc/sudoers.d/vagrant ; 47 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | ansible==4.2.0 2 | -------------------------------------------------------------------------------- /scripts/clean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -eux 2 | 3 | # Delete all Linux headers 4 | dpkg --list \ 5 | | awk '{ print $2 }' \ 6 | | grep 'linux-headers' \ 7 | | xargs apt-get -y purge; 8 | 9 | # Remove specific Linux kernels, such as linux-image-3.11.0-15-generic but 10 | # keeps the current kernel and does not touch the virtual packages, 11 | # e.g. 'linux-image-generic', etc. 12 | dpkg --list \ 13 | | awk '{ print $2 }' \ 14 | | grep 'linux-image-.*-generic' \ 15 | | grep -v `uname -r` \ 16 | | xargs apt-get -y purge; 17 | 18 | # Delete Linux source 19 | dpkg --list \ 20 | | awk '{ print $2 }' \ 21 | | grep linux-source \ 22 | | xargs apt-get -y purge; 23 | 24 | # delete docs packages 25 | dpkg --list \ 26 | | awk '{ print $2 }' \ 27 | | grep -- '-doc$' \ 28 | | xargs apt-get -y purge; 29 | 30 | # Delete X11 libraries 31 | apt-get -y purge libx11-data xauth libxmuu1 libxcb1 libx11-6 libxext6; 32 | 33 | # Delete obsolete networking 34 | apt-get -y purge ppp pppconfig pppoeconf; 35 | 36 | # Delete oddities 37 | apt-get -y purge popularity-contest installation-report command-not-found command-not-found-data friendly-recovery; 38 | 39 | apt-get -y autoremove; 40 | apt-get -y clean; 41 | 42 | # Remove docs 43 | rm -rf /usr/share/doc/* 44 | 45 | # Remove caches 46 | find /var/cache -type f -exec rm -rf {} \; 47 | 48 | # delete any logs that have built up during the install 49 | find /var/log/ -name *.log -exec rm -f {} \; 50 | -------------------------------------------------------------------------------- /scripts/minimize.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -eux 2 | 3 | case "$PACKER_BUILDER_TYPE" in 4 | qemu) exit 0 ;; 5 | esac 6 | 7 | # Whiteout root 8 | count=$(df --sync -kP / | tail -n1 | awk -F ' ' '{print $4}') 9 | count=$(($count-1)) 10 | dd if=/dev/zero of=/tmp/whitespace bs=1M count=$count || echo "dd exit code $? is suppressed"; 11 | rm /tmp/whitespace 12 | 13 | # Whiteout /boot 14 | count=$(df --sync -kP /boot | tail -n1 | awk -F ' ' '{print $4}') 15 | count=$(($count-1)) 16 | dd if=/dev/zero of=/boot/whitespace bs=1M count=$count || echo "dd exit code $? is suppressed"; 17 | rm /boot/whitespace 18 | 19 | set +e 20 | swapuuid="`/sbin/blkid -o value -l -s UUID -t TYPE=swap`"; 21 | case "$?" in 22 | 2|0) ;; 23 | *) exit 1 ;; 24 | esac 25 | set -e 26 | 27 | if [ "x${swapuuid}" != "x" ]; then 28 | # Whiteout the swap partition to reduce box size 29 | # Swap is disabled till reboot 30 | swappart="`readlink -f /dev/disk/by-uuid/$swapuuid`"; 31 | /sbin/swapoff "$swappart"; 32 | dd if=/dev/zero of="$swappart" bs=1M || echo "dd exit code $? is suppressed"; 33 | /sbin/mkswap -U "$swapuuid" "$swappart"; 34 | fi 35 | 36 | sync; 37 | --------------------------------------------------------------------------------