1
0
mirror of https://github.com/Tecnativa/docker-socket-proxy synced 2024-12-21 14:18:07 +00:00

Merge pull request #40 from Tecnativa/migrate-to-template

Apply image template
This commit is contained in:
João Marques 2021-01-26 09:52:37 +00:00 committed by GitHub
commit f1de9a0ccf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 262 additions and 160 deletions

View File

@ -0,0 +1,21 @@
# Changes here will be overwritten by Copier; do NOT edit manually
_commit: v0.1.2
_src_path: https://github.com/Tecnativa/image-template.git
dockerhub_image: tecnativa/docker-socket-proxy
image_platforms:
- linux/386
- linux/amd64
- linux/arm/v6
- linux/arm/v7
- linux/arm/v8
- linux/arm64
- linux/ppc64le
- linux/s390x
main_branches:
- master
project_name: docker-socket-proxy
project_owner: Tecnativa
push_to_ghcr: true
pytest: true
python_versions:
- "3.8"

164
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,164 @@
name: Build, Test & Deploy
"on":
pull_request:
push:
branches:
- master
workflow_dispatch:
inputs:
pytest_addopts:
description:
Extra options for pytest; use -vv for full details; see
https://docs.pytest.org/en/latest/example/simple.html#how-to-change-command-line-options-defaults
required: false
env:
LANG: "en_US.utf-8"
LC_ALL: "en_US.utf-8"
PIP_CACHE_DIR: ${{ github.workspace }}/.cache.~/pip
PIPX_HOME: ${{ github.workspace }}/.cache.~/pipx
POETRY_CACHE_DIR: ${{ github.workspace }}/.cache.~/pypoetry
POETRY_VIRTUALENVS_IN_PROJECT: "true"
PYTEST_ADDOPTS: ${{ github.event.inputs.pytest_addopts }}
PYTHONIOENCODING: "UTF-8"
jobs:
build-test:
runs-on: ubuntu-20.04
strategy:
matrix:
python:
- 3.8
steps:
# Prepare environment
- uses: actions/checkout@v2
# Set up and run tests
- name: Install python
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python }}
- name: Generate cache key CACHE
run:
echo "CACHE=${{ secrets.CACHE_DATE }} ${{ runner.os }} $(python -VV |
sha256sum | cut -d' ' -f1) ${{ hashFiles('pyproject.toml') }} ${{
hashFiles('poetry.lock') }}" >> $GITHUB_ENV
- uses: actions/cache@v2
with:
path: |
.cache.~
.venv
~/.local/bin
key: venv ${{ env.CACHE }}
- run: pip install poetry
- name: Patch $PATH
run: echo "$HOME/.local/bin" >> $GITHUB_PATH
- run: poetry install
# Run tests
- run: poetry run pytest --prebuild
build-push:
runs-on: ubuntu-20.04
services:
registry:
image: registry:2
ports:
- 5000:5000
env:
DOCKER_IMAGE_NAME: ${{ github.repository }}
DOCKERHUB_IMAGE_NAME: tecnativa/docker-socket-proxy
PUSH: ${{ toJSON(github.event_name != 'pull_request') }}
steps:
# Set up Docker Environment
- uses: actions/checkout@v2
- uses: actions/cache@v2
with:
path: |
/tmp/.buildx-cache
key: buildx|${{ secrets.CACHE_DATE }}|${{ runner.os }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v1
with:
driver-opts: network=host
install: true
# Build and push
- name: Docker meta for local images
id: docker_meta_local
uses: crazy-max/ghaction-docker-meta@v1
with:
images: localhost:5000/${{ env.DOCKER_IMAGE_NAME }}
tag-edge: true
tag-semver: |
{{version}}
{{major}}
{{major}}.{{minor}}
- name: Build and push to local (test) registry
uses: docker/build-push-action@v2
with:
context: .
file: ./Dockerfile
platforms: |
linux/386
linux/amd64
linux/arm/v6
linux/arm/v7
linux/arm/v8
linux/arm64
linux/ppc64le
linux/s390x
load: false
push: true
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache,mode=max
labels: ${{ steps.docker_meta_local.outputs.labels }}
tags: ${{ steps.docker_meta_local.outputs.tags }}
# Next jobs only happen outside of pull requests and on main branches
- name: Login to DockerHub
if: ${{ fromJSON(env.PUSH) }}
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_LOGIN }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container Registry
if: ${{ fromJSON(env.PUSH) }}
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ secrets.BOT_LOGIN }}
password: ${{ secrets.BOT_TOKEN }}
- name: Docker meta for public images
if: ${{ fromJSON(env.PUSH) }}
id: docker_meta_public
uses: crazy-max/ghaction-docker-meta@v1
with:
images: |
ghcr.io/${{ env.DOCKER_IMAGE_NAME }}
${{ env.DOCKERHUB_IMAGE_NAME }}
tag-edge: true
tag-semver: |
{{version}}
{{major}}
{{major}}.{{minor}}
- name: Build and push to public registry(s)
if: ${{ fromJSON(env.PUSH) }}
uses: docker/build-push-action@v2
with:
context: .
file: ./Dockerfile
platforms: |
linux/386
linux/amd64
linux/arm/v6
linux/arm/v7
linux/arm/v8
linux/arm64
linux/ppc64le
linux/s390x
load: false
push: true
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache,mode=max
labels: ${{ steps.docker_meta_public.outputs.labels }}
tags: ${{ steps.docker_meta_public.outputs.tags }}

View File

@ -1,111 +0,0 @@
name: test
on:
pull_request:
push:
branches:
- master
workflow_dispatch:
inputs:
pytest_addopts:
description:
Extra options for pytest; use -vv for full details; see
https://docs.pytest.org/en/latest/example/simple.html#how-to-change-command-line-options-defaults
required: false
env:
LANG: "en_US.utf-8"
LC_ALL: "en_US.utf-8"
PIP_CACHE_DIR: ${{ github.workspace }}/.cache.~/pip
PIPX_HOME: ${{ github.workspace }}/.cache.~/pipx
POETRY_CACHE_DIR: ${{ github.workspace }}/.cache.~/pypoetry
POETRY_VIRTUALENVS_IN_PROJECT: "true"
PYTEST_ADDOPTS: ${{ github.event.inputs.pytest_addopts }}
PYTHONIOENCODING: "UTF-8"
jobs:
build-test-push:
runs-on: ubuntu-latest
env:
DOCKER_REPO: tecnativa/docker-socket-proxy
steps:
- name: Get date
run: echo "BUILD_DATE=$(date --rfc-3339 ns)" >> $GITHUB_ENV
# Prepare Docker environment and build
- uses: actions/checkout@v2
- uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Build image(s)
uses: docker/build-push-action@v2
with:
build-args: |
BUILD_DATE=${{ env.BUILD_DATE }}
VCS_REF=${{ github.sha }}
context: .
file: ./Dockerfile
# HACK: Build single platform image for testing. See https://github.com/docker/buildx/issues/59
load: true
push: false
tags: |
${{ env.DOCKER_REPO }}:local
# Set up and run tests
- name: Install python
uses: actions/setup-python@v1
with:
python-version: "3.9"
- name: Generate cache key CACHE
run:
echo "CACHE=${{ secrets.CACHE_DATE }} ${{ runner.os }} $(python -VV |
sha256sum | cut -d' ' -f1) ${{ hashFiles('pyproject.toml') }} ${{
hashFiles('poetry.lock') }}" >> $GITHUB_ENV
- uses: actions/cache@v2
with:
path: |
.cache.~
.venv
~/.local/bin
key: venv ${{ env.CACHE }}
- run: pip install poetry
- name: Patch $PATH
run: echo "$HOME/.local/bin" >> $GITHUB_PATH
- run: poetry install
# Run tests
- run: poetry run pytest
env:
DOCKER_IMAGE_NAME: ${{ env.DOCKER_REPO }}:local
# Build and push
- name: Login to DockerHub
if:
github.repository == 'Tecnativa/docker-socket-proxy' && github.ref ==
'refs/heads/master'
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_LOGIN }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container Registry
if:
github.repository == 'Tecnativa/docker-socket-proxy' && github.ref ==
'refs/heads/master'
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ secrets.BOT_LOGIN }}
password: ${{ secrets.BOT_TOKEN }}
- name: Build and push
if:
github.repository == 'Tecnativa/docker-socket-proxy' && github.ref ==
'refs/heads/master'
uses: docker/build-push-action@v2
with:
build-args: |
BUILD_DATE=${{ env.BUILD_DATE }}
VCS_REF=${{ github.sha }}
context: .
file: ./Dockerfile
platforms: linux/386,linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm/v8,linux/arm64,linux/ppc64le,linux/s390x
load: false
push: true
tags: |
ghcr.io/${{ env.DOCKER_REPO }}
${{ env.DOCKER_REPO }}

View File

@ -1,3 +1,7 @@
[![Last image-template](https://img.shields.io/badge/last%20template%20update-v0.1.2-informational)](https://github.com/Tecnativa/image-template/tree/v0.1.2)
[![GitHub Container Registry](https://img.shields.io/badge/GitHub%20Container%20Registry-latest-%2324292e)](https://github.com/orgs/Tecnativa/packages/container/package/docker-socket-proxy)
[![Docker Hub](https://img.shields.io/badge/Docker%20Hub-latest-%23099cec)](https://hub.docker.com/r/tecnativa/docker-socket-proxy)
# Docker Socket Proxy # Docker Socket Proxy
[![Docker Hub](https://img.shields.io/badge/Docker%20Hub-docker.io%2Ftecnativa%2Fdocker--socket--proxy-%23099cec)](https://hub.docker.com/r/tecnativa/docker-socket-proxy) [![Docker Hub](https://img.shields.io/badge/Docker%20Hub-docker.io%2Ftecnativa%2Fdocker--socket--proxy-%23099cec)](https://hub.docker.com/r/tecnativa/docker-socket-proxy)

View File

@ -1,64 +1,89 @@
import json import json
import os import logging
from contextlib import contextmanager from contextlib import contextmanager
from logging import info
from pathlib import Path from pathlib import Path
import pytest import pytest
from plumbum import local from plumbum import local
from plumbum.cmd import docker from plumbum.cmd import docker
DOCKER_IMAGE_NAME = os.environ.get("DOCKER_IMAGE_NAME", "docker-socket-proxy:local") _logger = logging.getLogger(__name__)
def pytest_addoption(parser): def pytest_addoption(parser):
"""Allow prebuilding image for local testing.""" """Allow prebuilding image for local testing."""
parser.addoption("--prebuild", action="store_const", const=True) parser.addoption(
"--prebuild", action="store_true", help="Build local image before testing"
)
parser.addoption(
"--image",
action="store",
default="test:docker-socket-proxy",
help="Specify testing image name",
)
@pytest.fixture(autouse=True, scope="session") @pytest.fixture(scope="session")
def prebuild_docker_image(request): def image(request):
"""Build local docker image once before starting test suite.""" """Get image name. Builds it if needed."""
image = request.config.getoption("--image")
if request.config.getoption("--prebuild"): if request.config.getoption("--prebuild"):
info(f"Building {DOCKER_IMAGE_NAME}...") build = docker["image", "build", "-t", image, Path(__file__).parent.parent]
docker("build", "-t", DOCKER_IMAGE_NAME, Path(__file__).parent.parent) retcode, stdout, stderr = build.run()
_logger.log(
# Pytest prints warnings if a test fails, so this is a warning if
# the build succeeded, to allow debugging the build logs
logging.ERROR if retcode else logging.WARNING,
"Build logs for COMMAND: %s\nEXIT CODE:%d\nSTDOUT:%s\nSTDERR:%s",
build.bound_command(),
retcode,
stdout,
stderr,
)
assert not retcode, "Image build failed"
return image
@contextmanager @pytest.fixture(scope="session")
def proxy(**env_vars): def proxy_factory(image):
"""A context manager that starts the proxy with the specified env. """A context manager that starts the proxy with the specified env.
While inside the block, `$DOCKER_HOST` will be modified to talk to the proxy While inside the block, `$DOCKER_HOST` will be modified to talk to the proxy
instead of the raw docker socket. instead of the raw docker socket.
""" """
container_id = None
env_list = [f"--env={key}={value}" for key, value in env_vars.items()] @contextmanager
info(f"Starting {DOCKER_IMAGE_NAME} container with: {env_list}") def _proxy(**env_vars):
try: container_id = None
container_id = docker( env_list = [f"--env={key}={value}" for key, value in env_vars.items()]
"container", _logger.info(f"Starting {image} container with: {env_list}")
"run", try:
"--detach", container_id = docker(
"--privileged",
"--publish=2375",
"--volume=/var/run/docker.sock:/var/run/docker.sock",
*env_list,
DOCKER_IMAGE_NAME,
).strip()
container_data = json.loads(
docker("container", "inspect", container_id.strip())
)
socket_port = container_data[0]["NetworkSettings"]["Ports"]["2375/tcp"][0][
"HostPort"
]
with local.env(DOCKER_HOST=f"tcp://localhost:{socket_port}"):
yield container_id
finally:
if container_id:
info(f"Removing {container_id}...")
docker(
"container", "container",
"rm", "run",
"-f", "--detach",
container_id, "--privileged",
"--publish=2375",
"--volume=/var/run/docker.sock:/var/run/docker.sock",
*env_list,
image,
).strip()
container_data = json.loads(
docker("container", "inspect", container_id.strip())
) )
socket_port = container_data[0]["NetworkSettings"]["Ports"]["2375/tcp"][0][
"HostPort"
]
with local.env(DOCKER_HOST=f"tcp://localhost:{socket_port}"):
yield container_id
finally:
if container_id:
_logger.info(f"Removing {container_id}...")
docker(
"container",
"rm",
"-f",
container_id,
)
return _proxy

View File

@ -1,7 +1,6 @@
import logging import logging
import pytest import pytest
from conftest import proxy
from plumbum import ProcessExecutionError from plumbum import ProcessExecutionError
from plumbum.cmd import docker from plumbum.cmd import docker
@ -16,8 +15,8 @@ def _check_permissions(allowed_calls, forbidden_calls):
docker(*args) docker(*args)
def test_default_permissions(): def test_default_permissions(proxy_factory):
with proxy() as test_container: with proxy_factory() as test_container:
allowed_calls = (("version",),) allowed_calls = (("version",),)
forbidden_calls = ( forbidden_calls = (
("pull", "alpine"), ("pull", "alpine"),
@ -40,8 +39,8 @@ def test_default_permissions():
_check_permissions(allowed_calls, forbidden_calls) _check_permissions(allowed_calls, forbidden_calls)
def test_container_permissions(): def test_container_permissions(proxy_factory):
with proxy(CONTAINERS=1) as test_container: with proxy_factory(CONTAINERS=1) as test_container:
allowed_calls = [ allowed_calls = [
("logs", test_container), ("logs", test_container),
("inspect", test_container), ("inspect", test_container),
@ -55,8 +54,8 @@ def test_container_permissions():
_check_permissions(allowed_calls, forbidden_calls) _check_permissions(allowed_calls, forbidden_calls)
def test_post_permissions(): def test_post_permissions(proxy_factory):
with proxy(POST=1) as test_container: with proxy_factory(POST=1) as test_container:
allowed_calls = [] allowed_calls = []
forbidden_calls = [ forbidden_calls = [
("rm", "-f", test_container), ("rm", "-f", test_container),
@ -67,8 +66,8 @@ def test_post_permissions():
_check_permissions(allowed_calls, forbidden_calls) _check_permissions(allowed_calls, forbidden_calls)
def test_network_post_permissions(): def test_network_post_permissions(proxy_factory):
with proxy(POST=1, NETWORKS=1): with proxy_factory(POST=1, NETWORKS=1):
allowed_calls = [ allowed_calls = [
("network", "ls"), ("network", "ls"),
("network", "create", "foo"), ("network", "create", "foo"),