Permalink
Find file
25c9f8b Jan 8, 2016
@purpleidea @wzzrd
300 lines (278 sloc) 10.8 KB
# Makefile for building Vagrant base image "boxes" for vagrant-libvirt
# Copyright (C) 2010-2013+ James Shubin
# Written by James Shubin <james@shubin.ca>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# NOTE: if you change any of the values in this file (such as SIZE or --install
# arguments) make won't notice the change, you'll have to manually clean first.
SHELL = /bin/bash
.PHONY: all xz index asc builder convert box local upload clean
.SILENT:
# virt-builder os-version
VERSION =
POSTFIX =
ifeq ($(POSTFIX), )
FULLNAME = $(VERSION)
else
FULLNAME = $(VERSION)-$(POSTFIX)
endif
ISO =
# if a slash exists, then assume an absolute path; TODO: split between abs/rel
ifeq (,$(findstring $(ISO), /))
FULLISO := $(shell echo `pwd`/iso/$(ISO))
# TODO: add xdg support
# FIXME: when iso in ~/.cache/ qemu shows a permission denied error...
#FULLISO := $(shell echo ~/.cache/vagrant-builder/iso/$(ISO))
endif
ifneq ($(ISO), )
# TODO: use a signed index instead (useful for automated environments?)
INDEX_PRESENT = $(OUTPUT)/index
BUILDER_FLAGS = --fingerprint '' --source $(OUTPUT)/index --no-check-signature --no-cache
else
INDEX_PRESENT =
BUILDER_FLAGS =
endif
BOX = $(FULLNAME).box
SIZE = 40
ifeq ($(SIZE), )
SIZE = 40
endif
# the user might want to generate a personal set of base images
OUTPUT := $(shell echo ~/tmp/builder/$(FULLNAME))
SERVER = 'user@host.example.org'
REMOTE_PATH = 'public_html/vagrant'
# list of keys (from keys/ folder) to install
KEYS =
# list of extra repos (from repos/ folder) to install
REPOS =
# list of extra packages to install
PACKAGES =
# list of docker images to pull in
DOCKER =
# $(VARIABLE:OLD_PREFIX%OLD_SUFFIX=NEW_PREFIX%NEW_SUFFIX)
# find out which key files actually exist
KEYS_PATTERN = $(KEYS:%=keys/RPM-GPG-KEY-%)
ifeq ($(KEYS_PATTERN), )
KEYS_PRESENT =
else
KEYS_PRESENT := $(shell ls $(KEYS_PATTERN) 2> /dev/null)
endif
# build command line args for virt-builder
KEYS_COMMAND = $(KEYS_PRESENT:%=--upload %:/etc/pki/rpm-gpg/)
# find out which repo files actually exist
REPOS_PATTERN = $(REPOS:%=repos/%.repo)
ifeq ($(REPOS_PATTERN), )
REPOS_PRESENT =
else
REPOS_PRESENT := $(shell ls $(REPOS_PATTERN) 2> /dev/null)
endif
# build command line args for virt-builder
REPOS_COMMAND = $(REPOS_PRESENT:%=--upload %:/etc/yum.repos.d/)
# needed for subscription-manager virt-builder install...
USERNAME := $(shell source ~/.vagrant-builder/auth.sh && echo "$$USERNAME")
PASSWORD := $(shell source ~/.vagrant-builder/auth.sh && echo "$$PASSWORD")
POOLID =
ifeq ($(POOLID), )
ATTACH = --auto
else
# POOLID can be a list of poolid's separated by spaces...
ATTACH = $(POOLID)
endif
# TODO: alternatively, we could simulate a repo download first, and then copy
# those repos in with the REPOS variable, but that's too complicated for now!
# this is a list of elements (space separated)
RHELREPOS =
ifeq ($(RHELREPOS), )
SUBSCRIPTION_COMMAND =
SUBSCRIPTION_UNREGISTER =
else
# FIXME: avoid passing in the password where it can be visible...
SUBSCRIPTION_COMMAND = --run-command "echo \"USERNAME='$(USERNAME)';PASSWORD='$(PASSWORD)';ATTACH='$(ATTACH)';REPOS='$(RHELREPOS)'\" > /root/subscribe.input.sh" --run files/subscribe.sh
SUBSCRIPTION_UNREGISTER = --run-command 'if (test "$(USERNAME)" != "" && test "$(PASSWORD)" != ""); then subscription-manager unregister; fi'
endif
# ensure this is run from a script file and not directly :)
ifeq ($(VERSION),)
$(info VERSION not set!)
all:
echo 'Run this Makefile using a script in ./versions/'
echo 'Example: ./versions/fedora-21.sh'
echo 'Note: doing this from this working directory is important!'
false # cause make to exit now :)
else
#
# aliases
#
all: box $(OUTPUT)/vagrant-box-add.sh $(OUTPUT)/vagrant-box-remove.sh
builder: $(OUTPUT)/builder.img
convert: $(OUTPUT)/box.img
box: $(OUTPUT)/$(BOX)
xz: $(OUTPUT)/$(VERSION).xz
index: $(OUTPUT)/index
asc: $(OUTPUT)/index.asc
local: $(OUTPUT)/SHA256SUMS.asc
endif
#
# clean
#
# delete created files
clean:
@echo Running clean...
# TODO: technically, the 'true' should check if all the files are rm-ed
rm $(OUTPUT)/{{builder,box}.img,metadata.json,$(BOX),vagrant-box-{add,remove}.sh,SHA256SUMS{,.asc}} || true
$(OUTPUT)/$(VERSION).xz: $(FULLISO)
sudo -v # auth
./scripts/virt-install.sh $(VERSION) $(FULLISO) $(OUTPUT)/$(VERSION) || (rm -f $(OUTPUT)/$(VERSION) && false)
virt-sysprep -a $(OUTPUT)/$(VERSION)
mv $(OUTPUT)/$(VERSION){,.tmp}
virt-sparsify $(OUTPUT)/$(VERSION){.tmp,} && rm -f $(OUTPUT)/$(VERSION).tmp
xz --best --block-size=16777216 $(OUTPUT)/$(VERSION) # produces .xz
#
# plain index
#
$(OUTPUT)/index: $(OUTPUT)/$(VERSION).xz
# TODO: get heredoc to work...
echo -n > $(OUTPUT)/index
echo '[$(VERSION)]' >> $(OUTPUT)/index
echo 'name=$(VERSION)' >> $(OUTPUT)/index
echo '#osinfo=$(VERSION)' >> $(OUTPUT)/index
echo 'arch=x86_64' >> $(OUTPUT)/index
echo 'file='`python -c "import os.path; print(os.path.relpath('$(OUTPUT)/$(VERSION).xz', '.'))"` >> $(OUTPUT)/index
echo 'checksum[sha512]='`sha512sum $(OUTPUT)/$(VERSION).xz | awk '{print $$1}'` >> $(OUTPUT)/index
echo 'format=qcow2' >> $(OUTPUT)/index
echo '# hardcoded to 6GiB' >> $(OUTPUT)/index
echo 'size=6442450944' >> $(OUTPUT)/index
echo 'compressed_size='`stat -c %s $(OUTPUT)/$(VERSION).xz` >> $(OUTPUT)/index
echo 'expand=/dev/sda3' >> $(OUTPUT)/index
echo 'notes=Made with vagrant-builder by @purpleidea' >> $(OUTPUT)/index
#echo 'hidden=true' >> $(OUTPUT)/index
#
# signed index
#
$(OUTPUT)/index.asc: $(OUTPUT)/index
gpg --clearsign --armor $(OUTPUT)/index
#
# virt-builder
#
# build image with virt-builder
# NOTE: some of this system prep is based on the vagrant-libvirt scripts
# TODO: install: ruby ruby-devel make gcc rubygems ?
# FIXME: if virt-builder fails during install, there is no unregister done!
$(OUTPUT)/builder.img: files/* $(REPOS_PRESENT) $(KEYS_PRESENT) $(INDEX_PRESENT)
@echo Running virt-builder...
[ -d $(OUTPUT) ] || mkdir -p $(OUTPUT)/ # ensure path is present first!
virt-builder $(BUILDER_FLAGS) $(VERSION) $(SUBSCRIPTION_COMMAND) $(REPOS_COMMAND) $(KEYS_COMMAND) \
--output $(OUTPUT)/builder.img \
--format qcow2 \
--size $(SIZE)G \
--install rsync,nfs-utils,sudo,openssh-server,openssh-clients,screen,tar \
--root-password file:files/password \
--run-command 'yum erase -y vim-minimal || true' \
--run-command 'yum install -y sudo vim-enhanced || true' \
--run-command 'yum install -y puppet || true' \
--upload files/docker.sh:/root/docker.sh \
--run-command 'yum install -y docker psmisc || true' \
--run-command 'systemctl enable docker || true' \
--run-command '/root/docker.sh pull $(DOCKER) && rm /root/docker.sh' \
--upload files/yum.sh:/root/yum.sh \
--run-command '/root/yum.sh install $(PACKAGES) && rm /root/yum.sh' \
--run-command 'yum update -y' \
--run files/user.sh \
--run files/ssh.sh \
--run files/network.sh \
--run files/cleanup.sh \
$(SUBSCRIPTION_UNREGISTER) \
--run-command 'touch /.autorelabel'
# boot machine once to run the selinux relabelling, see:
# https://www.redhat.com/archives/libguestfs/2014-January/msg00183.html
# https://github.com/libguestfs/libguestfs/commit/20a4bfde9628cfeb8bea441cab7dcc94843b34e3
qemu-system-x86_64 -machine accel=kvm:tcg -cpu host -m 512 -drive file=$(OUTPUT)/builder.img,format=qcow2,if=virtio -no-reboot -serial stdio -nographic -nodefaults || (rm $(OUTPUT)/builder.img; false)
reset # TODO: qemu-system-x86_64 borks the terminal :(
#
# convert
#
# workaround sparse qcow2 images bug
# thread: https://www.redhat.com/archives/libguestfs/2014-January/msg00008.html
$(OUTPUT)/box.img: $(OUTPUT)/builder.img
@echo Running convert...
qemu-img convert -O qcow2 $(OUTPUT)/builder.img $(OUTPUT)/box.img
#
# metadata.json
#
$(OUTPUT)/metadata.json:
@echo Running templater...
[ -d $(OUTPUT) ] || mkdir -p $(OUTPUT)/ # ensure path is present first!
echo '{"provider": "libvirt", "format": "qcow2", "virtual_size": $(SIZE)}' > $(OUTPUT)/metadata.json
echo '' >> $(OUTPUT)/metadata.json # newline
#
# tar
#
# create custom box
# format at: https://github.com/pradels/vagrant-libvirt/tree/master/example_box
$(OUTPUT)/$(BOX): Vagrantfile $(OUTPUT)/metadata.json $(OUTPUT)/box.img
@echo Running tar...
tar -cvzf $(OUTPUT)/$(BOX) ./Vagrantfile --directory=$(OUTPUT)/ ./metadata.json ./box.img
#
# vagrant box add
#
$(OUTPUT)/vagrant-box-add.sh:
@echo Running vagrant box add templater...
[ -d $(OUTPUT) ] || mkdir -p $(OUTPUT)/ # ensure path is present first!
echo '#!/bin/bash' > $(OUTPUT)/vagrant-box-add.sh
echo 'vagrant box add $(FULLNAME).box --name $(FULLNAME)' >> $(OUTPUT)/vagrant-box-add.sh
echo '' >> $(OUTPUT)/vagrant-box-add.sh # newline
chmod u+x $(OUTPUT)/vagrant-box-add.sh
#
# vagrant box remove
#
$(OUTPUT)/vagrant-box-remove.sh:
@echo Running vagrant box remove templater...
[ -d $(OUTPUT) ] || mkdir -p $(OUTPUT)/ # ensure path is present first!
echo '#!/bin/bash' > $(OUTPUT)/vagrant-box-remove.sh
echo 'vagrant box remove $(FULLNAME)' >> $(OUTPUT)/vagrant-box-remove.sh
echo '# the backing file has one of these two names...' >> $(OUTPUT)/vagrant-box-remove.sh
echo 'sudo rm -f /var/lib/libvirt/images/$(FULLNAME)_vagrant_box_image.img' >> $(OUTPUT)/vagrant-box-remove.sh
echo 'sudo rm -f /var/lib/libvirt/images/$(FULLNAME)_vagrant_box_image_0.img' >> $(OUTPUT)/vagrant-box-remove.sh
echo 'sudo systemctl reload libvirtd.service' >> $(OUTPUT)/vagrant-box-remove.sh
echo '' >> $(OUTPUT)/vagrant-box-remove.sh # newline
chmod u+x $(OUTPUT)/vagrant-box-remove.sh
#
# sha256sum
#
$(OUTPUT)/SHA256SUMS: $(OUTPUT)/$(BOX)
@echo Running sha256sum...
cd $(OUTPUT) && sha256sum $(BOX) > SHA256SUMS; cd -
#
# gpg
#
$(OUTPUT)/SHA256SUMS.asc: $(OUTPUT)/SHA256SUMS
@echo Running gpg...
# the --yes forces an overwrite of the SHA256SUMS.asc if necessary
gpg2 --yes --clearsign $(OUTPUT)/SHA256SUMS
#
# upload
#
# upload to public server
# NOTE: user downloads while file uploads are in progress don't cause problems!
upload: $(OUTPUT)/$(BOX) $(OUTPUT)/SHA256SUMS $(OUTPUT)/SHA256SUMS.asc
if [ "`cat $(OUTPUT)/SHA256SUMS`" != "`ssh $(SERVER) 'cd $(REMOTE_PATH)/$(FULLNAME)/ && sha256sum $(BOX)'`" ]; then \
echo Running upload...; \
scp -p $(OUTPUT)/{$(BOX),SHA256SUMS{,.asc}} $(SERVER):$(REMOTE_PATH)/$(FULLNAME)/; \
fi
# this method works too, but always hits the server on every make call
#upload:
#ifeq ($(shell cat $(OUTPUT)/SHA256SUMS), $(shell ssh $(SERVER) 'cd $(REMOTE_PATH)/ && sha256sum $(BOX)'))
# @echo true
#else
# @echo false
#endif