pyveth - veth driver for Docker Engine written in Python
8 files modified
162 ■■■■ changed files
.github/workflows/release.yml 43 ●●●● patch | view | raw | blame | history
.github/workflows/test.yml 9 ●●●●● patch | view | raw | blame | history
Dockerfile 4 ●●●● patch | view | raw | blame | history
config.json 2 ●●● patch | view | raw | blame | history
lib/NetworkDriver.py 19 ●●●● patch | view | raw | blame | history
package.sh 28 ●●●● patch | view | raw | blame | history
run.py 3 ●●●●● patch | view | raw | blame | history
test_integration.sh 54 ●●●●● patch | view | raw | blame | history
.github/workflows/release.yml
@@ -5,23 +5,48 @@
    tags:
      - 'v[0-9]+.*'
permissions:
  contents: read
jobs:
  deploy:
  deploy-to-ghcr-io:
    name: Publish to GitHub Container Registry
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    steps:
      - name: Checkout code
        uses: actions/checkout@v6
      - name: Log in to the Container registry
        uses: docker/login-action@v4
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - name: Prepare and push Docker plugin
        run: |
          export NAME="ghcr.io/${{ github.repository }}"
          export VERSIONS="latest ${GITHUB_REF/refs\/tags\//}"
          ./package.sh
  deploy-to-docker-hub:
    name: Publish to Docker Hub
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v2
        uses: actions/checkout@v6
      - name: Login to Docker Hub
        run: |
          docker login -u 'jacekkow' -p '${{ secrets.DOCKER_PASSWORD }}'
        uses: docker/login-action@v4
        with:
          username: ${{ github.actor }}
          password: ${{ secrets.DOCKER_PASSWORD }}
      - name: Prepare and push Docker plugin
        run: |
          VERSIONS="latest ${GITHUB_REF/refs\/tags\//}"
          export VERSIONS
          export NAME="${{ github.actor }}/pyveth"
          export VERSIONS="latest ${GITHUB_REF/refs\/tags\//}"
          ./package.sh
          for VERSION in ${VERSIONS}; do
            docker plugin push "jacekkow/pyveth:${VERSION}"
          done
.github/workflows/test.yml
@@ -6,16 +6,19 @@
    branches:
      - '**'
permissions:
  contents: read
jobs:
  unit_test:
    name: Unit tests
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v2
        uses: actions/checkout@v6
      - name: Set up Python
        uses: actions/setup-python@v1
        uses: actions/setup-python@v6
        with:
          python-version: 3.x
@@ -35,7 +38,7 @@
      VERSIONS: dev
    steps:
      - name: Checkout code
        uses: actions/checkout@v2
        uses: actions/checkout@v6
      - name: Prepare Docker plugin
        run: |
Dockerfile
@@ -8,8 +8,8 @@
WORKDIR /usr/src/app
COPY --chown=nobody:nobody requirements.txt .
RUN pip3 install --user --no-cache-dir -r requirements.txt
RUN python -m venv venv && ./venv/bin/pip install --no-cache-dir -r requirements.txt
COPY --chown=nobody:nobody . .
CMD [ "./run.py" ]
CMD [ "./venv/bin/python", "run.py" ]
config.json
@@ -2,7 +2,7 @@
    "description": "pyveth - veth network driver in Python",
    "documentation": "https://github.com/jacekkow/docker-plugin-pyveth",
    "workdir": "/usr/src/app",
    "entrypoint": ["./run.py"],
    "entrypoint": ["./venv/bin/python", "run.py"],
    "env": [
        {
            "name": "ENVIRONMENT",
lib/NetworkDriver.py
@@ -40,7 +40,16 @@
            print(ip.link("set", index=idx, master=id_parent))
        endpoint.Interface.Peer = ifname1
    return ifname0
    return ifname0, ifname1
def delete_interface(interface):
    try:
        with pyroute2.IPRoute() as ip:
            idx = ip.link_lookup(ifname=interface)[0]
            ip.link("delete", index=idx)
    except:
        pass
@app.route('/NetworkDriver.GetCapabilities', methods=['POST'])
@@ -108,7 +117,10 @@
    network = networks[join.NetworkID]
    endpoint = endpoints['{}-{}'.format(join.NetworkID, join.EndpointID)]
    interface = create_interface(endpoint, network)
    interface, interface_external = create_interface(endpoint, network)
    endpoint.internal_interface_name = interface
    endpoint.external_interface_name = interface_external
    gw4 = None
    for net4 in network.IPv4:
@@ -141,6 +153,9 @@
@app.route('/NetworkDriver.Leave', methods=['POST'])
def Leave():
    leave = LeaveEntity(**flask.request.get_json(force=True))
    endpoint = endpoints.get('{}-{}'.format(leave.NetworkID, leave.EndpointID), None)
    if endpoint is not None and endpoint.external_interface_name:
        delete_interface(endpoint.external_interface_name)
    return {}
package.sh
@@ -1,6 +1,6 @@
#!/bin/sh
#!/bin/bash
set -x
set -e -x
NAME=${NAME:-jacekkow/pyveth}
VERSIONS=${VERSIONS:-latest}
@@ -14,14 +14,16 @@
sudo mkdir -p rootfs
docker export "${id}" | sudo tar -x -C rootfs
docker rm -vf "${id}"
docker plugin disable "${NAME}"
docker plugin rm "${NAME}"
sudo chmod 755 rootfs rootfs/usr/src/app/.local && sudo chmod -R o=g rootfs/usr/src
for VERSION in ${VERSIONS}; do
  sudo docker plugin create "${NAME}:${VERSION}" .
done
sudo du -hs rootfs
for VERSION in ${VERSIONS}; do
  docker plugin enable "${NAME}:${VERSION}" || exit 1
  break
done
docker plugin disable "${NAME}" || true
docker plugin rm "${NAME}" || true
sudo chmod 755 rootfs && sudo chmod -R o=g rootfs/usr/src
if [ `echo ${VERSIONS} | wc -w` -gt 1 ]; then
  for VERSION in ${VERSIONS}; do
    sudo docker plugin create "${NAME}:${VERSION}" .
    docker plugin push "${NAME}:${VERSION}"
    docker plugin rm "${NAME}:${VERSION}"
  done
else
  sudo docker plugin create "${NAME}:${VERSIONS}" .
  docker plugin enable "${NAME}:${VERSIONS}"
fi
run.py
@@ -2,6 +2,8 @@
import logging
import os
import signal
import sys
import docker_plugin_api.Plugin
import flask
@@ -20,4 +22,5 @@
    if os.environ.get('ENVIRONMENT', 'dev') == 'dev':
        app.run(debug=True)
    else:
        signal.signal(signal.SIGTERM, lambda: sys.exit(0))
        waitress.serve(app, unix_socket='/run/docker/plugins/pyveth.sock', threads=1)
test_integration.sh
@@ -1,12 +1,15 @@
#!/bin/sh
#!/bin/bash
set -e
set -e -x
NAME=${NAME:-jacekkow/pyveth}
VERSION=${VERSION:-latest}
PLUGIN="${NAME}:${VERSION}"
docker network rm test1 || true
docker network rm test2 || true
docker plugin install jacekkow/pyipam:latest || true
docker network create \
@@ -23,26 +26,37 @@
  test1
ADDRESSES=$(docker run --rm --network test1 \
  debian \
  /bin/ip addr show
  alpine \
  /sbin/ip addr show
)
echo "${ADDRESSES}" | grep 192.168.255.129/24
echo "${ADDRESSES}" | grep 2001:db8:aaaa:bbbb::1/32
if ! echo "${ADDRESSES}" | grep 192.168.255.129/24; then
    echo "ERROR: invalid IPv4 address assigned"
    exit 1
fi
if ! echo "${ADDRESSES}" | grep 2001:db8:aaaa:bbbb::1/32; then
    echo "ERROR: invalid IPv6 address assigned"
    exit 1
fi
ADDRESSES=$(docker run --rm --network test1 \
  --ip 192.168.255.25 --ip6 2001:db8:dddd:eeee:ffff:1:2:3 \
  debian \
  /bin/ip addr show
  alpine \
  /sbin/ip addr show
)
echo "${ADDRESSES}" | grep 192.168.255.25/24
echo "${ADDRESSES}" | grep 2001:db8:dddd:eeee:ffff:1:2:3/32
if ! echo "${ADDRESSES}" | grep 192.168.255.25/24; then
    echo "ERROR: invalid IPv4 address assigned"
    exit 1
fi
if ! echo "${ADDRESSES}" | grep 2001:db8:dddd:eeee:ffff:1:2:3/32; then
    echo "ERROR: invalid IPv6 address assigned"
    exit 1
fi
docker network rm test1
docker network create \
  --internal \
  --driver "${PLUGIN}" \
  --ipam-driver jacekkow/pyipam:latest \
  --ipv6 \
@@ -53,15 +67,21 @@
  test2
ROUTES=$(docker run --rm --network test2 \
  debian \
  /bin/ip route show
  alpine \
  /sbin/ip route show
)
echo "${ROUTES}" | grep 192.168.255.254
if ! echo "${ROUTES}" | grep 192.168.255.254; then
    echo "ERROR: invalid IPv4 route"
    exit 1
fi
ROUTES=$(docker run --rm --network test2 \
  debian \
  /bin/ip -6 route show
  alpine \
  /sbin/ip -6 route show
)
echo "${ROUTES}" | grep 2001:db8:ffff:ffff:ffff:ffff:ffff:ffff
if ! echo "${ROUTES}" | grep 2001:db8:ffff:ffff:ffff:ffff:ffff:ffff; then
    echo "ERROR: invalid IPv6 route"
    exit 1
fi
docker network rm test2