diff --git a/backup/pvc/Dockerfile b/backup/pvc/Dockerfile new file mode 100644 index 00000000..bacfc120 --- /dev/null +++ b/backup/pvc/Dockerfile @@ -0,0 +1,12 @@ +FROM alpine:3.9 + +RUN set -x && \ + addgroup -S user -g 1000 && \ + adduser -S user -G user && \ + apk add --no-cache bash +WORKDIR /home/user/bin +COPY bin . +RUN chmod +x *.sh +USER user + +CMD exec /bin/sh -c "trap : TERM INT; (while true; do sleep 1000; done) & wait" diff --git a/backup/pvc/Makefile b/backup/pvc/Makefile new file mode 100644 index 00000000..bb300591 --- /dev/null +++ b/backup/pvc/Makefile @@ -0,0 +1,171 @@ +# Set POSIX sh for maximum interoperability +SHELL := /bin/sh +PATH := $(GOPATH)/bin:$(PATH) + +# Import config +# You can change the default config with `make config="config_special.env" build` +config ?= config.env +include $(config) + +# Set an output prefix, which is the local directory if not specified +PREFIX?=$(shell pwd) + +VERSION := $(shell cat VERSION.txt) +GITCOMMIT := $(shell git rev-parse --short HEAD) +GITBRANCH := $(shell git rev-parse --abbrev-ref HEAD) +GITUNTRACKEDCHANGES := $(shell git status --porcelain --untracked-files=no) +GITIGNOREDBUTTRACKEDCHANGES := $(shell git ls-files -i --exclude-standard) +ifneq ($(GITUNTRACKEDCHANGES),) + GITCOMMIT := $(GITCOMMIT)-dirty +endif +ifneq ($(GITIGNOREDBUTTRACKEDCHANGES),) + GITCOMMIT := $(GITCOMMIT)-dirty +endif + +VERSION_TAG := $(VERSION) +LATEST_TAG := latest +BUILD_TAG := $(GITBRANCH)-$(GITCOMMIT) + +ARGS ?= $(EXTRA_ARGS) + +.DEFAULT_GOAL := help + +.PHONY: all +all: status checkmake clean build verify install docker-build docker-images ## Build the image + @echo "+ $@" + +.PHONY: check-env +check-env: ## Checks the environment variables + @echo "+ $@" + @echo "NAME: $(NAME)" +ifeq ($(NAME),) + $(error You must provide name) +endif + @echo "VERSION: $(VERSION)" +ifeq ($(VERSION),) + $(error You must provide version) +endif + @echo "VERSION_TAG: $(VERSION_TAG)" + @echo "LATEST_TAG: $(LATEST_TAG)" + @echo "BUILD_TAG: $(BUILD_TAG)" +ifneq ($(GITUNTRACKEDCHANGES),) + @echo "Changes: \n$(GITUNTRACKEDCHANGES)" +endif + +.PHONY: spring-clean +spring-clean: ## Cleanup git ignored files (interactive) + git clean -Xdi + +.PHONY: checkmake +HAS_CHECKMAKE := $(shell which checkmake) +checkmake: ## Check this Makefile + @echo "+ $@" +ifndef HAS_CHECKMAKE + go get -u github.com/mrtazz/checkmake +endif + @checkmake Makefile + +define e2e +echo "\nRunning $(1) e2e test"; +@e2e/$(1)/test.sh $(DOCKER_REGISTRY)/$(NAME):$(GITCOMMIT); +endef + +.PHONY: docker-e2e +E2E_TESTS := $(shell ls e2e) +docker-e2e: docker-build ## Make e2e tests for docker image + @echo "+ $@" + $(foreach TEST_NAME,$(E2E_TESTS), $(call e2e,$(TEST_NAME))) + +.PHONY: docker-login +docker-login: ## Log in into the Docker repository + @echo "+ $@" + +.PHONY: docker-build +docker-build: check-env ## Build the container + @echo "+ $@" + docker build . -t $(DOCKER_REGISTRY)/$(NAME):$(GITCOMMIT) --file Dockerfile + +.PHONY: docker-images +docker-images: ## List all local containers + @echo "+ $@" + docker images + +.PHONY: docker-push +docker-push: ## Push the container + @echo "+ $@" + docker tag $(DOCKER_REGISTRY)/$(NAME):$(GITCOMMIT) $(DOCKER_ORGANIZATION)/$(DOCKER_REGISTRY)/$(NAME):$(BUILD_TAG) + docker push $(DOCKER_ORGANIZATION)/$(DOCKER_REGISTRY)/$(NAME):$(BUILD_TAG) + +.PHONY: docker-release-version +docker-release-version: ## Release image with version tag (in addition to build tag) + @echo "+ $@" + docker tag $(DOCKER_REGISTRY)/$(NAME):$(GITCOMMIT) $(DOCKER_ORGANIZATION)/$(DOCKER_REGISTRY)/$(NAME):$(VERSION_TAG) + docker push $(DOCKER_ORGANIZATION)/$(DOCKER_REGISTRY)/$(NAME):$(VERSION_TAG) + +.PHONY: docker-release-latest +docker-release-latest: ## Release image with latest tags (in addition to build tag) + @echo "+ $@" + docker tag $(DOCKER_REGISTRY)/$(NAME):$(GITCOMMIT) $(DOCKER_ORGANIZATION)/$(DOCKER_REGISTRY)/$(NAME):$(LATEST_TAG) + docker push $(DOCKER_ORGANIZATION)/$(DOCKER_REGISTRY)/$(NAME):$(LATEST_TAG) + +.PHONY: docker-release +docker-release: docker-release-version docker-release-latest ## Release image with version and latest tags (in addition to build tag) + @echo "+ $@" + +# if this session isn't interactive, then we don't want to allocate a +# TTY, which would fail, but if it is interactive, we do want to attach +# so that the user can send e.g. ^C through. +INTERACTIVE := $(shell [ -t 0 ] && echo 1 || echo 0) +ifeq ($(INTERACTIVE), 1) + DOCKER_FLAGS += -t +endif + +.PHONY: docker-run +docker-run: docker-build ## Run the container in docker, you can use EXTRA_ARGS + @echo "+ $@" + docker run --rm -i $(DOCKER_FLAGS) \ + $(DOCKER_REGISTRY)/$(NAME):$(GITCOMMIT) $(ARGS) + +.PHONY: bump-version +BUMP := patch +bump-version: ## Bump the version in the version file. Set BUMP to [ patch | major | minor ] + @echo "+ $@" + @go get -u github.com/jessfraz/junk/sembump # update sembump tool + $(eval NEW_VERSION=$(shell sembump --kind $(BUMP) $(VERSION))) + @echo "Bumping VERSION.txt from $(VERSION) to $(NEW_VERSION)" + echo $(NEW_VERSION) > VERSION.txt + @echo "Updating version from $(VERSION) to $(NEW_VERSION) in README.md" + sed -i s/$(VERSION)/$(NEW_VERSION)/g README.md + sed -i s/$(VERSION)/$(NEW_VERSION)/g deploy/operator.yaml + git add VERSION.txt README.md deploy/operator.yaml + git commit -vaem "Bump version to $(NEW_VERSION)" + @echo "Run make tag to create and push the tag for new version $(NEW_VERSION)" + +.PHONY: tag +tag: ## Create a new git tag to prepare to build a release + @echo "+ $@" + git tag -s -a $(VERSION) -m "$(VERSION)" + git push origin $(VERSION) + +.PHONY: help +help: + @grep -Eh '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' + +.PHONY: status +status: ## Shows git and dep status + @echo "+ $@" + @echo "Commit: $(GITCOMMIT), VERSION: $(VERSION)" + @echo +ifneq ($(GITUNTRACKEDCHANGES),) + @echo "Changed files:" + @git status --porcelain --untracked-files=no + @echo +endif +ifneq ($(GITIGNOREDBUTTRACKEDCHANGES),) + @echo "Ignored but tracked files:" + @git ls-files -i --exclude-standard + @echo +endif + @echo "Dependencies:" + dep status + @echo diff --git a/backup/pvc/VERSION.txt b/backup/pvc/VERSION.txt new file mode 100644 index 00000000..45c7a584 --- /dev/null +++ b/backup/pvc/VERSION.txt @@ -0,0 +1 @@ +v0.0.1 diff --git a/backup/pvc/bin/backup.sh b/backup/pvc/bin/backup.sh new file mode 100644 index 00000000..d998b280 --- /dev/null +++ b/backup/pvc/bin/backup.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +set -eo pipefail + +[[ ! $# -eq 1 ]] && echo "Usage: $0 backup_number" && exit 1; +[[ -z "${BACKUP_DIR}" ]] && echo "Required 'BACKUP_DIR' env not set" && exit 1; +[[ -z "${JENKINS_HOME}" ]] && echo "Required 'JENKINS_HOME' env not set" && exit 1; + +backup_number=$1 +echo "Running backup" + +tar -C ${JENKINS_HOME} -czf "${BACKUP_DIR}/${backup_number}.tar.gz" --exclude jobs/*/config.xml --exclude jobs/*/workspace* -c jobs + +echo Done +exit 0 \ No newline at end of file diff --git a/backup/pvc/bin/restore.sh b/backup/pvc/bin/restore.sh new file mode 100644 index 00000000..497f2a23 --- /dev/null +++ b/backup/pvc/bin/restore.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +set -eo pipefail + +[[ ! $# -eq 1 ]] && echo "Usage: $0 backup_number" && exit 1 +[[ -z "${BACKUP_DIR}" ]] && echo "Required 'BACKUP_DIR' env not set" && exit 1; +[[ -z "${JENKINS_HOME}" ]] && echo "Required 'JENKINS_HOME' env not set" && exit 1; + +backup_number=$1 +echo "Running restore backup" + +tar -C ${JENKINS_HOME} -zxf "${BACKUP_DIR}/${backup_number}.tar.gz" + +echo Done +exit 0 \ No newline at end of file diff --git a/backup/pvc/config.env b/backup/pvc/config.env new file mode 100644 index 00000000..4980c47d --- /dev/null +++ b/backup/pvc/config.env @@ -0,0 +1,4 @@ +# Setup variables for the Makefile +NAME=pvc +DOCKER_ORGANIZATION=virtuslab +DOCKER_REGISTRY=jenkins-operator/backup \ No newline at end of file diff --git a/backup/pvc/e2e/backup_and_restore/jenkins_home/jobs/some-job/builds/1/log b/backup/pvc/e2e/backup_and_restore/jenkins_home/jobs/some-job/builds/1/log new file mode 100644 index 00000000..7f236fe6 --- /dev/null +++ b/backup/pvc/e2e/backup_and_restore/jenkins_home/jobs/some-job/builds/1/log @@ -0,0 +1 @@ +build logs \ No newline at end of file diff --git a/backup/pvc/e2e/backup_and_restore/jenkins_home/jobs/some-job/nextBuildNumber b/backup/pvc/e2e/backup_and_restore/jenkins_home/jobs/some-job/nextBuildNumber new file mode 100644 index 00000000..d8263ee9 --- /dev/null +++ b/backup/pvc/e2e/backup_and_restore/jenkins_home/jobs/some-job/nextBuildNumber @@ -0,0 +1 @@ +2 \ No newline at end of file diff --git a/backup/pvc/e2e/backup_and_restore/test.sh b/backup/pvc/e2e/backup_and_restore/test.sh new file mode 100755 index 00000000..c58e8dee --- /dev/null +++ b/backup/pvc/e2e/backup_and_restore/test.sh @@ -0,0 +1,39 @@ +#!/bin/bash +set -eo pipefail + +[ "${DEBUG}" ] && set -x + +# set current working directory to the directory of the script +cd "$(dirname "$0")" + +docker_image=$1 + +if ! docker inspect ${docker_image} &> /dev/null; then + echo "Image '${docker_image}' does not exists" + false +fi + +JENKINS_HOME="$(pwd)/jenkins_home" +BACKUP_DIR="$(pwd)/backup" +RESTORE_FOLDER="$(pwd)/restore" +mkdir -p ${BACKUP_DIR} +mkdir -p ${RESTORE_FOLDER} + +# Create an instance of the container under testing +cid="$(docker run -e JENKINS_HOME=${JENKINS_HOME} -v ${JENKINS_HOME}:${JENKINS_HOME}:ro -e BACKUP_DIR=${BACKUP_DIR} -v ${BACKUP_DIR}:${BACKUP_DIR}:rw -e RESTORE_FOLDER=${RESTORE_FOLDER} -v ${RESTORE_FOLDER}:${RESTORE_FOLDER}:rw -d ${docker_image})" +echo "Docker container ID '${cid}'" + +# Remove test directory and container afterwards +trap "docker rm -vf $cid > /dev/null;rm -rf ${BACKUP_DIR}" EXIT + +backup_number=1 +docker exec -it ${cid} /home/user/bin/backup.sh ${backup_number} + +backup_file="${BACKUP_DIR}/${backup_number}.tar.gz" +[[ ! -f ${backup_file} ]] && echo "Backup file ${backup_file} not found" && exit 1; + +docker exec -it ${cid} /bin/bash -c "JENKINS_HOME=${RESTORE_FOLDER};/home/user/bin/restore.sh ${backup_number}" + +echo "Compare directories" +diff --brief --recursive ${JENKINS_HOME} ${RESTORE_FOLDER} +echo "Directories are the same"