Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CircleCI, CD, Python3, and PipEnv #552

Merged
merged 18 commits into from
Feb 3, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 26 additions & 31 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,45 +6,31 @@ version: 2
steps:
- checkout
- run:
command: |
# setup qemu/variables
docker run --rm --privileged multiarch/qemu-user-static:register --reset

HUB_NAMESPACE=$CIRCLE_PROJECT_USERNAME
[[ $CIRCLE_PROJECT_USERNAME == "pi-hole" ]] && HUB_NAMESPACE="pihole"
[[ $IMAGE != *"/"* ]] && IMAGE="${HUB_NAMESPACE}/${IMAGE}"
[[ $IMAGE != *":"* ]] && IMAGE="${IMAGE}:$CIRCLE_JOB"
if [[ -n "$CIRCLE_TAG" ]]; then
# remove latest tag if used
IMAGE="${IMAGE/:latest/:}"
# and tack the github tag (version) on the front of the tag. image:arch = image:v1.0-arch
IMAGE="${IMAGE/:/:${CIRCLE_TAG}-}"
# latest gets a trailing slash, remove it
IMAGE="${IMAGE/%-/}"
fi

# generate and build dockerfile
pip install --upgrade pip
pip install -r requirements.txt
./Dockerfile.py --arch=${CIRCLE_JOB} -v
docker images
# run docker build & tests
# 2 parallel max b/c race condition with docker fixture (I think?)
py.test -vv -n 2 -k "${CIRCLE_JOB}" ./test/

# push image
# [[ "$CIRCLE_PR_NUMBER" == "" ]] && sudo docker push $IMAGE

command: ./circle-test.sh
- persist_to_workspace:
root: .
paths: [ 'ci-workspace' ]

jobs:
amd64:
<<: *job_template
aarch64:
arm64:
<<: *job_template
armhf:
<<: *job_template
armel:
<<: *job_template
deploy:
docker:
- image: circleci/python:latest
steps:
- setup_remote_docker:
version: 18.06.0-ce
- checkout
- attach_workspace:
at: .
- run:
command: ./circle-deploy.sh



Expand All @@ -56,7 +42,7 @@ workflows:
filters:
tags:
only: /^v.*/
- aarch64:
- arm64:
filters:
tags:
only: /^v.*/
Expand All @@ -68,3 +54,12 @@ workflows:
filters:
tags:
only: /^v.*/
- deploy:
requires:
- amd64
- arm64
- armhf
- armel
filters:
tags:
only: /^v.*/
4 changes: 4 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
**/*.sw*
.tox
.git
**/__pycache__
.pipenv
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
.cache
__pycache__
.tox
.pipenv
.eggs
UNKNOWN.egg-info
.env
ci-workspace

# WIP/test stuff
doco.yml
83 changes: 47 additions & 36 deletions Dockerfile.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
#!/usr/bin/env python
#!/usr/bin/env python3
""" Dockerfile.py - generates and build dockerfiles

Usage:
Dockerfile.py [--arch=<arch> ...] [--skip=<arch> ...] [-v] [-t] [--no-build | --no-generate] [--no-cache]
Dockerfile.py [--hub_tag=<tag>] [--arch=<arch> ...] [-v] [-t] [--no-build | --no-generate] [--no-cache]

Options:
--no-build Skip building the docker images
--no-cache Build without using any cache data
--no-generate Skip generating Dockerfiles from template
--arch=<arch> What Architecture(s) to build [default: amd64 armel armhf aarch64]
--skip=<arch> What Architectures(s) to skip [default: None]
--hub_tag=<tag> What the Docker Hub Image should be tagged as [default: None]
--arch=<arch> What Architecture(s) to build [default: amd64 armel armhf arm64]
-v Print docker's command output [default: False]
-t Print docker's build time [default: False]

Examples:
"""
from __future__ import print_function


from docopt import docopt
from jinja2 import Environment, FileSystemLoader
Expand All @@ -29,7 +29,7 @@
base_vars = {
'name': 'pihole/pihole',
'maintainer' : 'adam@diginc.us',
's6_version' : 'v1.21.7.0',
's6_version' : 'v1.22.1.0',
}

os_base_vars = {
Expand All @@ -47,19 +47,23 @@
__version__: [
{
'base': 'pihole/debian-base:latest',
'arch': 'amd64'
'arch': 'amd64',
's6arch': 'amd64',
},
{
'base': 'multiarch/debian-debootstrap:armel-stretch-slim',
'arch': 'armel'
'arch': 'armel',
's6arch': 'arm',
},
{
'base': 'multiarch/debian-debootstrap:armhf-stretch-slim',
'arch': 'armhf'
'arch': 'arm',
's6arch' : 'arm',
},
{
'base': 'multiarch/debian-debootstrap:arm64-stretch-slim',
'arch': 'aarch64'
'arch': 'arm64',
's6arch' : 'aarch64',
}
]
}
Expand All @@ -69,19 +73,17 @@ def generate_dockerfiles(args):
print(" ::: Skipping Dockerfile generation")
return

for version, archs in images.iteritems():
for version, archs in images.items():
for image in archs:
if image['arch'] not in args['--arch'] or image['arch'] in args['--skip']:
return
s6arch = image['arch']
if image['arch'] == 'armel':
s6arch = 'arm'
if image['arch'] not in args['--arch']:
continue
s6arch = image['s6arch'] if image['s6arch'] else image['arch']
merged_data = dict(
{ 'version': version }.items() +
base_vars.items() +
os_base_vars.items() +
image.items() +
{ 's6arch': s6arch }.items()
list({ 'version': version }.items()) +
list(base_vars.items()) +
list(os_base_vars.items()) +
list(image.items()) +
list({ 's6arch': s6arch }.items())
)
j2_env = Environment(loader=FileSystemLoader(THIS_DIR),
trim_blocks=True)
Expand All @@ -98,17 +100,28 @@ def build_dockerfiles(args):
return

for arch in args['--arch']:
# TODO: include from external .py that can be shared with Dockerfile.py / Tests / deploy scripts '''
#if arch == 'armel':
# print("Skipping armel, incompatible upstream binaries/broken")
# continue
build('pihole', arch, args)


def run_and_stream_command_output(command, args):
print("Running", command)
build_result = subprocess.Popen(command.split(), stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
bufsize=1, universal_newlines=True)
if args['-v']:
while build_result.poll() is None:
for line in build_result.stdout:
print(line, end='')
build_result.wait()
if build_result.returncode != 0:
print(" ::: Error running".format(command))
print(build_result.stderr)


def build(docker_repo, arch, args):
dockerfile = 'Dockerfile_{}'.format(arch)
repo_tag = '{}:{}_{}'.format(docker_repo, __version__, arch)
cached_image = '{}/{}'.format('pihole', repo_tag)
print(" ::: Building {}".format(repo_tag))
time=''
if args['-t']:
time='time '
Expand All @@ -118,22 +131,20 @@ def build(docker_repo, arch, args):
build_command = '{time}docker build {no_cache} --pull --cache-from="{cache},{create_tag}" -f {dockerfile} -t {create_tag} .'\
.format(time=time, no_cache=no_cache, cache=cached_image, dockerfile=dockerfile, create_tag=repo_tag)
print(" ::: Building {} into {}".format(dockerfile, repo_tag))
run_and_stream_command_output(build_command, args)
if args['-v']:
print(build_command, '\n')
build_result = subprocess.Popen(build_command.split(), stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
if args['-v']:
for c in iter(lambda: build_result.stdout.read(1), b''):
sys.stdout.write(c)
build_result.wait()
if build_result.returncode != 0:
print(" ::: Building {} encountered an error".format(dockerfile))
print(build_result.stderr)
assert build_result.returncode == 0
if args['--hub_tag']:
hub_tag_command = "{time}docker tag {create_tag} {hub_tag}"\
.format(time=time, create_tag=repo_tag, hub_tag=args['--hub_tag'])
print(" ::: Tagging {} into {}".format(repo_tag, args['--hub_tag']))
run_and_stream_command_output(hub_tag_command, args)


if __name__ == '__main__':
args = docopt(__doc__, version='Dockerfile 1.0')
# print args
args = docopt(__doc__, version='Dockerfile 1.1')
if args['-v']:
print(args)

generate_dockerfiles(args)
build_dockerfiles(args)
8 changes: 8 additions & 0 deletions Dockerfile.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/usr/bin/env sh
# alpine sh only

set -eux
./Dockerfile.py -v --arch="${ARCH}" --hub_tag="${ARCH_IMAGE}"
# TODO: Add junitxml output and have circleci consume it
# 2 parallel max b/c race condition with docker fixture (I think?)
py.test -vv -n 2 -k "${ARCH}" ./test/
2 changes: 1 addition & 1 deletion Dockerfile_amd64
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
FROM pihole/debian-base:latest

ENV ARCH amd64
ENV S6OVERLAY_RELEASE https://github.com/just-containers/s6-overlay/releases/download/v1.21.7.0/s6-overlay-amd64.tar.gz
ENV S6OVERLAY_RELEASE https://github.com/just-containers/s6-overlay/releases/download/v1.22.1.0/s6-overlay-amd64.tar.gz

COPY install.sh /usr/local/bin/install.sh
COPY VERSION /etc/docker-pi-hole-version
Expand Down
6 changes: 3 additions & 3 deletions Dockerfile_aarch64 → Dockerfile_arm64
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
FROM multiarch/debian-debootstrap:arm64-stretch-slim

ENV ARCH aarch64
ENV S6OVERLAY_RELEASE https://github.com/just-containers/s6-overlay/releases/download/v1.21.7.0/s6-overlay-aarch64.tar.gz
ENV ARCH arm64
ENV S6OVERLAY_RELEASE https://github.com/just-containers/s6-overlay/releases/download/v1.22.1.0/s6-overlay-aarch64.tar.gz

COPY install.sh /usr/local/bin/install.sh
COPY VERSION /etc/docker-pi-hole-version
Expand Down Expand Up @@ -40,7 +40,7 @@ ENV DNSMASQ_USER root
ENV VERSION v4.3.2
ENV PATH /opt/pihole:${PATH}

LABEL image="pihole/pihole:v4.3.2_aarch64"
LABEL image="pihole/pihole:v4.3.2_arm64"
LABEL maintainer="adam@diginc.us"
LABEL url="https://www.github.com/pi-hole/docker-pi-hole"

Expand Down
2 changes: 1 addition & 1 deletion Dockerfile_armel
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
FROM multiarch/debian-debootstrap:armel-stretch-slim

ENV ARCH armel
ENV S6OVERLAY_RELEASE https://github.com/just-containers/s6-overlay/releases/download/v1.21.7.0/s6-overlay-arm.tar.gz
ENV S6OVERLAY_RELEASE https://github.com/just-containers/s6-overlay/releases/download/v1.22.1.0/s6-overlay-arm.tar.gz

COPY install.sh /usr/local/bin/install.sh
COPY VERSION /etc/docker-pi-hole-version
Expand Down
23 changes: 23 additions & 0 deletions Dockerfile_build
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
FROM docker:latest

# Based on https://github.com/Ilhicas/alpine-pipenv
ARG packages
RUN apk --update add python3 python3-dev curl gcc make \
musl-dev libffi-dev openssl-dev ${packages} \
&& rm -rf /var/cache/apk/* \
&& pip3 install -U pip pipenv


# -v "$(pwd):/$(pwd)" -w "$(pwd)" to prevent nested docker path confusion
COPY ./Dockerfile.sh /usr/local/bin/
COPY Pipfile* /root/
WORKDIR /root

RUN pipenv install --system \
&& sed -i 's|/bin/sh|/bin/bash|g' /usr/lib/python3.8/site-packages/testinfra/backend/docker.py


RUN echo "set -ex && Dockerfile.sh && \$@" > /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh
ENTRYPOINT entrypoint.sh
CMD Dockerfile.sh
63 changes: 63 additions & 0 deletions Pipfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true

[dev-packages]

[packages]
apipkg = "==1.5"
atomicwrites = "==1.3.0"
attrs = "==19.3.0"
bcrypt = "==3.1.7"
cached-property = "==1.5.1"
certifi = "==2019.11.28"
cffi = "==1.13.2"
chardet = "==3.0.4"
configparser = "==4.0.2"
contextlib2 = "==0.6.0.post1"
coverage = "==5.0.1"
cryptography = "==2.8"
docker = "==4.1.0"
dockerpty = "==0.4.1"
docopt = "==0.6.2"
enum34 = "==1.1.6"
execnet = "==1.7.1"
filelock = "==3.0.12"
funcsigs = "==1.0.2"
idna = "==2.8"
importlib-metadata = "==1.3.0"
ipaddress = "==1.0.23"
jsonschema = "==3.2.0"
more-itertools = "==5.0.0"
pathlib2 = "==2.3.5"
pluggy = "==0.13.1"
py = "==1.8.1"
pycparser = "==2.19"
pyparsing = "==2.4.6"
pyrsistent = "==0.15.6"
pytest = "==4.6.8"
pytest-cov = "==2.8.1"
pytest-forked = "==1.1.3"
pytest-xdist = "==1.31.0"
requests = "==2.22.0"
scandir = "==1.10.0"
six = "==1.13.0"
subprocess32 = "==3.5.4"
testinfra = "==3.3.0"
texttable = "==1.6.2"
toml = "==0.10.0"
tox = "==3.14.3"
urllib3 = "==1.25.7"
virtualenv = "==16.7.9"
wcwidth = "==0.1.7"
zipp = "==0.6.0"
"backports.shutil_get_terminal_size" = "==1.0.0"
"backports.ssl_match_hostname" = "==3.7.0.1"
Jinja2 = "==2.10.3"
MarkupSafe = "==1.1.1"
PyYAML = "==5.2"
websocket_client = "==0.57.0"

[requires]
python_version = "3.8"
Loading