summaryrefslogblamecommitdiffstats
path: root/Makefile
blob: e54406773176a863a50f5e07ba5885c12a4abfab (plain) (tree)
1
2
3
4
5
6
7
8
9

                                                                  
                                         
                    
                                 
                                         
                                             
                                    
                                            

 
                                                                 
 

                                              
                                               


                                     
                                     
 

                                         
                                         
 

                                                 
















                                                                                     
 
 
                                                                
                                                                                            
 
                                                                        
                                                                                                     


                                                                                                      
                                                                                                                                                                 

                                                                                                                         

                                                                       
                                                                 


                                                                   
 


                              
 
                                                          
 
                                           
           
                




                                             
     
 
 
            
                                                 
     
 
 


                                                                               

                                               
 


                                                                             
                                                                                            




                                                                               




                                                                                                     



         

                                           
                                           
            

                                              


                                                

                                                     






                                                         
            
     
 
 
                                                                                              
     
 
 


                                                                            
               
                                                                  
                                                

                                         
                                          


                                                
                                                                 
                                                      
                      
                                                                   
 
 
                     




                                                                              
                                                                       

                                                          

                                                           
                              
                                                                                 
                                                              
                                                                           

                                                                                  
                                              


                                                        
                                             
                                                                                  
                                                                  
                                    
 
                       


                                 







                                                                


                                                                    


                                                                       
                                                           
                                              



                                                         
                                                                 
                                                           
                                                                  
                                    

                                                                 
                                                
 
 


                                                                        
                                                                           



                                           


                        

                                               


                           




                                                                                                                             
 






                                                                                               
 

                                           













                                                                             
 
 








                                           
                                                                          

 

                                           
                                                                
 
 

                                           
                                                           
 
 
     
                                                             
             
                                    
                                                                
             
                                             
                                                                
                                                                  
             
                                            
                                                       
             


                                                                    


                                            
                                      
                                                                                                                      











                                                                                                                    





                                       
# Simple Makefile to build base VM images using packer and ansible
# TODO
#   * support ssh as user instead of root
#   * testing target
PACKER                  ?= packer
ANSIBLE_DIR_CORE        ?= ansible-dracut
ANSIBLE_DIR_PROV        ?= ansible-roles-prov
HASHER                  ?= sha256sum
SHELL                   := /usr/bin/env bash


ARCH                    := $(shell uname -m | sed 's/i686/i386/')

BUILDER_QEMU_EXE        := qemu-system-$(ARCH)
BUILDER_QEMU_NAME       := qemu
BUILDER_QEMU_PROV       := $(BUILDER_QEMU_NAME)

BUILDER_VMWARE_EXE      := vmware
BUILDER_VMWARE_NAME     := vmware-iso
BUILDER_VMWARE_PROV     := vmware-vmx

BUILDER_VIRTUALBOX_EXE  := virtualbox
BUILDER_VIRTUALBOX_NAME := virtualbox-iso
BUILDER_VIRTUALBOX_PROV := virtualbox-ovf

BUILDERS                := QEMU VMWARE VIRTUALBOX


$(foreach cur, $(BUILDERS),\
    $(if $(shell command -v $(BUILDER_$(cur)_EXE)),\
        $(eval AVAILABLE_BUILDERS += $(cur))))


# If now BUILDER or PROVISIONER was specified on the command line, take the
# first available one; otherwise use that specified one.
BUILDER_SELECTED     := $(strip $(if $(filter $(BUILDER),$(AVAILABLE_BUILDERS)),\
                                    $(BUILDER),\
                                    $(firstword $(AVAILABLE_BUILDERS))))
PROVISIONER_SELECTED := $(strip $(if $(filter $(PROVISIONER),$(AVAILABLE_BUILDERS)),\
                                    $(PROVISIONER),\
                                    $(BUILDER_SELECTED)))

override BUILDER     = $(BUILDER_$(BUILDER_SELECTED)_NAME)
override PROVISIONER = $(BUILDER_$(PROVISIONER_SELECTED)_PROV)


# The packer templates, detected as *.json (excluding base.json)
TEMPLATES := $(basename $(filter-out base.json ansible-provisioner.json,$(wildcard *.json)))

# The provisioning flavors, detected as ansible-roles/setup-<flavor>.yml
FLAVORS := $(patsubst $(ANSIBLE_DIR_PROV)/setup-%.yml,%, $(wildcard $(ANSIBLE_DIR_PROV)/setup-*.yml))

BASETARGETS := $(foreach template, $(TEMPLATES), $(template)/base)
PROVTARGETS := $(foreach template, $(TEMPLATES), $(foreach flavor, $(FLAVORS), $(template)/$(flavor)))
REPROVTARGETS := $(foreach template, $(TEMPLATES), $(foreach flavor, $(FLAVORS), $(filter-out $(template)/$(flavor).latest,$(wildcard $(template)/$(flavor).*))))
COMPRESSPOSSIBLE := $(foreach template, $(TEMPLATES), $(foreach flavor, $(FLAVORS), $(wildcard $(template)/$(flavor).*)))
COMPRESSTARGETS := $(foreach possible, $(COMPRESSPOSSIBLE), $(possible)/compress)
BOOTTARGETS := $(foreach template, $(TEMPLATES), $(template)/base/boot)
BOOTTARGETS += $(foreach prov, $(PROVTARGETS), $(prov)/boot)
CURBOOTTARGETS := $(foreach prov, $(REPROVTARGETS), $(prov)/boot)
BASERMTARGETS := $(foreach target, $(BASETARGETS),  rm/$(target))
PROVRMTARGETS := $(foreach target, $(REPROVTARGETS),  rm/$(target))


ifndef ANSIBLE_PROV_EXTRA_ARGS
    ANSIBLE_PROV_EXTRA_ARGS :=
endif

#override ANSIBLE_PROV_EXTRA_ARGS += --scp-extra-args,"-O"

override PACKER_OPTS += -var-file=base.json
ifdef DEBUG
    VERBOSE := 1
    ifeq ($(DEBUG),STEP)
        override PACKER_OPTS += -debug
    else
        override PACKER_OPTS += -on-error=ask
    endif
endif


ifdef WINDOW
    override PACKER_OPTS += -var='headless=false'
endif


# We support parallel Provisioning packer builds.
# To ensure data consistency we save the used ansible roles before executing in
# its own environment, tagged by the current date & time.
TIMESTAMP := $(shell date "+%Y-%m-%d_%H-%M-%S")


# The ROOTPW is only needed for the base, boot and provisioning targets.
# In every other case it must not be checked as it should be possible to call
# help or cleanup without the need of defining the password.
PW_NEEDED := $(filter $(strip $(BASETARGETS) $(PROVTARGETS) $(BOOTTARGETS)),$(MAKECMDGOALS))
ifneq ($(PW_NEEDED),)
    ifeq ($(strip $(ROOTPW)),)
        $(error No root password is set, set it as ROOTPW in your environment.)
    else
        $(foreach cur,$(PW_NEEDED),\
            $(eval cur_dir := $(firstword $(subst /, ,$(cur)))/base)\
            $(if $(shell test -d "$(cur_dir)" && echo yes),\
                $(shell echo "$(ROOTPW)" | $(HASHER) --check --status "$(cur_dir)/rootpw.$(HASHER)")\
                $(if $(filter 1,$(.SHELLSTATUS)),\
                    $(error The wrong ROOTPW is set. Please correct it))))
    endif
endif


ifdef VERBOSE
    $(info root password:        $(ROOTPW))
    $(info hasher:               $(HASHER))
    $(info )
    $(info timestamp:            $(TIMESTAMP))
    $(info )
    $(info packer: executable:   $(PACKER))
    $(info packer: options:      $(PACKER_OPTS))
    $(info )
    $(info ansible: core:        $(ANSIBLE_DIR_CORE))
    $(info ansible: flavors:     $(ANSIBLE_DIR_PROV))
    $(info )
    $(info builder: available:   $(AVAILABLE_BUILDERS))
    $(info builder: chosen:      $(BUILDER))
    $(info )
    $(info targets: base:        $(strip $(BASETARGETS)))
    $(info targets: boot:        $(strip $(BOOTTARGETS)))
    $(info targets: provision:   $(strip $(PROVTARGETS)))
    $(info )
endif


.PHONY: help clean_except_last clean_bases clean_all clean_failed backinglock $(REPROVTARGETS)
help:


# Creating base images. When DRACUT_INIT is specified, the dracut module
# repository will be initially cloned and its required components (dnbd3,
# xmount, libxmount-qemu) will be pre-build to accelerate subsequent builds.
$(BASETARGETS):
	$(info ** Building template '$(@D)' using '$(BUILDER)' **)
	$(eval INIT_TAG := $(if $(DRACUT_INIT),\
	                       install,\
	                       untagged))
	$(PACKER) build -only=$(BUILDER) \
	    $(PACKER_OPTS) \
	    -var='vm_name=rootfs-image' \
	    -var='output_directory=$(@D)/base' \
	    -var='playbook=$(ANSIBLE_DIR_CORE)/slx-builder.yml' \
	    -var='extra_ansible_args=-t,$(INIT_TAG)' \
	    $(@D).json
	@echo "$(ROOTPW)" | $(HASHER) > $(@D)/base/rootpw.$(HASHER)


# Provisioning images
$(PROVTARGETS): $(foreach flav, $(FLAVORS), %/$(flav)): %/base

$(PROVTARGETS) $(REPROVTARGETS):
	$(eval FLAVOR := $(basename $(@F)))
	$(eval VERSION := $(if $(suffix $(@)), $(subst .,,$(suffix $(@))), 0))
	$(eval BASE_IMAGE := $(if $(wildcard $(@)/build/rootfs-image),\
	                         $(@)/build/rootfs-image,\
	                         $(@D)/base/rootfs-image))
	$(eval BUILD_DIR := $(@D)/$(FLAVOR).$(TIMESTAMP))
	$(info ** Provisioning '$(@D)' with '$(FLAVOR)' **)
	@mkdir -p $(BUILD_DIR)
	@cp -r `readlink -f $(ANSIBLE_DIR_PROV)` $(BUILD_DIR)/$(ANSIBLE_DIR_PROV)
	@ln -sfn $(FLAVOR).$(TIMESTAMP) $(@D)/$(FLAVOR).latest
	@ln -sfr $(BUILD_DIR) $(dir $(BASE_IMAGE))/$(TIMESTAMP).backinglock
	@-cp -r $@/$(ANSIBLE_DIR_PROV) $(BUILD_DIR)/$(ANSIBLE_DIR_PROV).$(VERSION)
	@-cp -r $@/$(ANSIBLE_DIR_PROV).* $(BUILD_DIR)
	$(PACKER) build -only=$(PROVISIONER) \
	    $(PACKER_OPTS) \
	    -var='vm_name=rootfs-image' \
	    -var='output_directory=$(BUILD_DIR)/build' \
	    -var='base_image=$(BASE_IMAGE)' \
	    -var='playbook=$(BUILD_DIR)/$(ANSIBLE_DIR_PROV)/setup-$(FLAVOR).yml' \
	    -var='extra_ansible_args=$(ANSIBLE_PROV_EXTRA_ARGS)' \
	    ansible-provisioner.json

# Generating boot files
$(BOOTTARGETS): %/boot: %

$(BOOTTARGETS) $(CURBOOTTARGETS):
	$(eval BASE_DIR := $(if $(filter base,$(notdir $(@D))),\
	                       ,\
	                       $(if $(wildcard $(@D)/build/.),\
	                           $(@D),\
	                           $(@D).$(TIMESTAMP))))
	$(eval BUILD_DIR := $(if $(BASE_DIR),\
	                        $(BASE_DIR)/build,\
	                        $(@D)))
	$(eval BUILD_DIR := $(if $(filter $(@), $(CURBOOTTARGETS)),\
				$(BASE_DIR)/build,\
				$(BUILD_DIR)))
	$(eval ANSIBLE_DIR_CUR := $(if $(BASE_DIR),\
	                              $(BASE_DIR)/$(ANSIBLE_DIR_PROV),\
	                              $(ANSIBLE_DIR_PROV)))
	$(info ** Generating boot files for '$(BUILD_DIR)')
	$(PACKER) build -only=$(PROVISIONER) \
	    $(PACKER_OPTS) \
	    -var='vm_name=rootfs-image.tmp' \
	    -var='output_directory=$(BUILD_DIR)/tmp' \
	    -var='base_image=$(BUILD_DIR)/rootfs-image' \
	    -var='playbook=$(ANSIBLE_DIR_CORE)/slx-builder.yml' \
	    -var='extra_ansible_args=-t,install,-t,build' \
	    -var='extra_ansible_args=$(ANSIBLE_PROV_EXTRA_ARGS)' \
	    ansible-provisioner.json
	@mkdir -p $(BUILD_DIR)/boot
	@mv -f $(ANSIBLE_DIR_CORE)/boot_files/* $(BUILD_DIR)/boot
	$(if $(DEBUG),,@rm -rf $(BUILD_DIR)/tmp)


$(COMPRESSTARGETS):
	$(info ** Commiting and Compressing all changes to the image **)
	$(eval IMAGE_NAME := $(@D)/build/rootfs-image)
	@virt-sparsify --compress --verbose $(IMAGE_NAME) $(IMAGE_NAME).tmp
	@rm -f $(IMAGE_NAME)
	@mv $(IMAGE_NAME).tmp $(IMAGE_NAME)


# Safe removal of images
$(BASERMTARGETS):
rm/%: %/*.backinglock
	$(eval BUILD_DIR := $(subst rm/,,$(@)))
	@rm -rf $(BUILD_DIR)

$(PROVRMTARGETS):
rm/%: %/build/*.backinglock
	$(eval BUILD_DIR := $(subst rm/,,$(@))/build)
	$(eval FATHER_BUILD := $(dir $(shell qemu-img info $(BUILD_DIR)/rootfs-image | grep "backing file" | cut -d\  -f3-)))
	$(eval BUILD_TIME := $(subst .,,$(suffix $(@))))
	@rm -rf $(FATHER_BUILD)/$(BUILD_TIME).backinglock
	@rm -rf $(BUILD_DIR)

%.backinglock: backinglock
	@qemu-img convert -f qcow2 $(@)/build/rootfs-image -O qcow2 $(@)/build/rootfs-image.tmp
	@rm $(@)/build/rootfs-image
	@mv $(@)/build/rootfs-image.tmp $(@)/build/rootfs-image
	@rm $(@)

backinglock:

clean_except_last:
	@-$(foreach template,$(TEMPLATES),\
		$(eval exclusions := $(shell test -d $(template) && \
		                                find $(template) \
		                                    -maxdepth 1 \
		                                    -type l \
		                                    -print0 \
		                                | xargs -r -0 -n1 readlink))\
		test -d $(template) && \
		    find $(template)/* \
		        -maxdepth 0 \
		        -type d \
		        $(foreach file,$(exclusions),-not -name $(file) ) \
		        -not -name base \
		        -print0 \
		    | xargs -r -0 -n1 rm -rf; )


clean_failed:
	@-$(foreach template,$(TEMPLATES),\
		test -d $(template) && \
		    find $(template)/* \
		        -maxdepth 0 \
		        -type d \
		        -not -name base \
		        -print0 \
		    | xargs -r -0 -n1 -i \
		        $(SHELL) -c 'test -d "{}/build" || rm -rf "{}"'; )


clean_bases:
	@-$(foreach template,$(TEMPLATES),\
		test -d $(template) && rm -rf $(template)/base;)


clean_all:
	@-$(foreach template,$(TEMPLATES),\
		test -d $(template) && rm -rf $(template);)


help:
	@printf "Usage:\n\tmake <template>/<flavor>[/boot]\n"
	@echo
	@echo "Base images targets:"
	@for T in $(BASETARGETS); do printf "\t%s\n" "$$T"; done
	@echo
	@echo "Provisioning images targets: "
	@for P in $(PROVTARGETS); do printf "\t%s\n" "$$P"; done
	@for P in $(REPROVTARGETS); do printf "\t%s\n" "$$P"; done
	@echo
	@echo "Generate boot files targets:"
	@printf "\t<{base,provisioning}_target>/boot\n"
	@echo
	@echo "Commiting backing files and Compressing targets:"
	@for C in $(COMPRESSTARGETS); do printf "\t%s\n" "$$C"; done
	@echo
	@echo "For safely removing targets:"
	@printf "\trm/<target>\n"
	@echo
	@echo "Available options are:"
	@printf "\tANSIBLE_DIR_CORE: Set directory with ansible roles for building initramfs  (def: ansible-dracut)\n"
	@printf "\tANSIBLE_DIR_PROV: Set directory with ansible roles for provisioning  (def: ansible-roles-prov)\n"
	@printf "\tBUILDER:          Set an ISO builder, do not autodetect\n"
	@printf "\tPROVISIONER:      Set a provisioning builder, do not autodetect\n"
	@printf "\tDEBUG:            Enable debug mode in packer        (includes VERBOSE)\n"
	@printf "\t  DEBUG=          Enable enhanced on-error handling\n"
	@printf "\t  DEBUG=STEP      Enable step by step debugging in packer\n"
	@printf "\tHASHER:           Set wanted hasher                  (def: sha256sum)\n"
	@printf "\tPACKER:           Set packer executable              (def: packer)\n"
	@printf "\tPACKER_OPTS:      Set packer options\n"
	@printf "\tROOTPW:           Set root password for output image\n"
	@printf "\tVERBOSE:          Enable verbose output\n"
	@printf "\tWINDOW:           Disable headless mode\n"
	@echo
	@echo "Clean targets are:"
	@printf "\tclean_except_last\n"
	@printf "\tclean_failed\n"
	@printf "\tclean_bases\n"
	@printf "\tclean_all\n"