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

✨ adding docker-api-proxy service ⚠️ #7070

Open
wants to merge 49 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
3398e50
aded docker-api-proxy with integration tests
Jan 23, 2025
47a1b9b
Merge remote-tracking branch 'upstream/master' into pr-osparc-docker-…
Jan 23, 2025
8a4c101
refactor tests
Jan 23, 2025
47eb688
refactor
Jan 23, 2025
c4f8276
using docker IP
Jan 23, 2025
7ab73c9
making test mandatory
Jan 23, 2025
ff6539e
Merge remote-tracking branch 'upstream/master' into pr-osparc-docker-…
Jan 23, 2025
9eeaee9
giving service more time to start
Jan 23, 2025
5bb62ea
added secure flag to settings
Jan 23, 2025
92b9086
removed unused
Jan 23, 2025
714dbfc
renamed
Jan 23, 2025
b080606
fixed dependencies
Jan 23, 2025
6ffef9e
updae healthcheck and remove logs
Jan 23, 2025
333ac43
trigger test on any package change
Jan 23, 2025
36b54fa
refactor
Jan 23, 2025
da0d8da
rename
Jan 23, 2025
10cd8ea
rename
Jan 23, 2025
0393243
refactor with new pattern
Jan 23, 2025
2999a03
refactor
Jan 23, 2025
a763834
rename
Jan 23, 2025
b4d34b6
fixed integration tests
Jan 23, 2025
ab35648
running as non root user
Jan 23, 2025
6168b5e
dropped depnencies
Jan 23, 2025
4771022
added required necessary dependencies
Jan 23, 2025
b0eb0d1
remove unused
Jan 29, 2025
1e54972
remove unused
Jan 29, 2025
ce13d5d
Merge remote-tracking branch 'upstream/master' into pr-osparc-docker-…
Jan 29, 2025
1837ca2
Merge remote-tracking branch 'upstream/master' into pr-osparc-docker-…
Jan 30, 2025
e71c113
added combine_lfiespan_context_managers
Jan 30, 2025
faaf938
using new docker client
Jan 30, 2025
096f943
Merge remote-tracking branch 'upstream/master' into pr-osparc-docker-…
Jan 30, 2025
f39fa91
refactor to use custom setting var name
Jan 30, 2025
b9da94d
added env vars
Jan 31, 2025
0d92121
adding check to see that API is responsive
Jan 31, 2025
e1fe41d
Merge remote-tracking branch 'upstream/master' into pr-osparc-docker-…
Jan 31, 2025
92a682d
ensure it timesout
Jan 31, 2025
e53c60c
renamed network
Jan 31, 2025
44d8ce4
added healthcheck error
Jan 31, 2025
959d1b6
fixed healthcheck
Jan 31, 2025
bd7e1c7
refaactor
Jan 31, 2025
911bcdf
Merge remote-tracking branch 'upstream/master' into pr-osparc-docker-…
Feb 4, 2025
cce8765
refactor position
Feb 4, 2025
b3bd6e6
typos and notes
Feb 4, 2025
d84efbf
refactor to use settings instead of property name
Feb 4, 2025
09a4a0b
fixed specs generation
Feb 4, 2025
72e2bc7
rename
Feb 4, 2025
f0709d3
fixed tests
Feb 4, 2025
5d87c53
fixed warning
Feb 4, 2025
8f062d2
added missing dependencies
Feb 4, 2025
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
3 changes: 3 additions & 0 deletions .env-devel
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ DIRECTOR_REGISTRY_CACHING=True
DIRECTOR_SERVICES_CUSTOM_CONSTRAINTS=null
DIRECTOR_TRACING=null

DOCKER_API_PROXY_HOST=docker-api-proxy
DOCKER_API_PROXY_PORT=8888

EFS_USER_ID=8006
EFS_USER_NAME=efs
EFS_GROUP_ID=8106
Expand Down
70 changes: 70 additions & 0 deletions .github/workflows/ci-testing-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ jobs:
migration: ${{ steps.filter.outputs.migration }}
payments: ${{ steps.filter.outputs.payments }}
dynamic-scheduler: ${{ steps.filter.outputs.dynamic-scheduler }}
docker-api-proxy: ${{ steps.filter.outputs.docker-api-proxy }}
resource-usage-tracker: ${{ steps.filter.outputs.resource-usage-tracker }}
static-webserver: ${{ steps.filter.outputs.static-webserver }}
storage: ${{ steps.filter.outputs.storage }}
Expand Down Expand Up @@ -233,6 +234,9 @@ jobs:
- 'services/docker-compose*'
- 'scripts/mypy/*'
- 'mypy.ini'
docker-api-proxy:
- 'packages/**'
- 'services/docker-api-proxy/**'
resource-usage-tracker:
- 'packages/**'
- 'services/resource-usage-tracker/**'
Expand Down Expand Up @@ -2190,6 +2194,71 @@ jobs:
with:
flags: integrationtests #optional


integration-test-docker-api-proxy:
needs: [changes, build-test-images]
if: ${{ needs.changes.outputs.anything-py == 'true' || needs.changes.outputs.docker-api-proxy == 'true' || github.event_name == 'push'}}
timeout-minutes: 30 # if this timeout gets too small, then split the tests
name: "[int] docker-api-proxy"
runs-on: ${{ matrix.os }}
strategy:
matrix:
python: ["3.11"]
os: [ubuntu-22.04]
fail-fast: false
steps:
- uses: actions/checkout@v4
- name: setup docker buildx
id: buildx
uses: docker/setup-buildx-action@v3
with:
driver: docker-container
- name: setup python environment
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python }}
- name: expose github runtime for buildx
uses: crazy-max/ghaction-github-runtime@v3
# FIXME: Workaround for https://github.com/actions/download-artifact/issues/249
- name: download docker images with retry
uses: Wandalen/wretry.action@master
with:
action: actions/download-artifact@v4
with: |
name: docker-buildx-images-${{ runner.os }}-${{ github.sha }}-backend
path: /${{ runner.temp }}/build
attempt_limit: 5
attempt_delay: 1000
- name: load docker images
run: make load-images local-src=/${{ runner.temp }}/build
- name: install uv
uses: astral-sh/setup-uv@v5
with:
version: "0.5.x"
enable-cache: false
cache-dependency-glob: "**/docker-api-proxy/requirements/ci.txt"
- name: show system version
run: ./ci/helpers/show_system_versions.bash
- name: install
run: ./ci/github/integration-testing/docker-api-proxy.bash install
- name: test
run: ./ci/github/integration-testing/docker-api-proxy.bash test
- name: upload failed tests logs
if: ${{ failure() }}
uses: actions/upload-artifact@v4
with:
name: ${{ github.job }}_docker_logs
path: ./services/docker-api-proxy/test_failures
- name: cleanup
if: ${{ !cancelled() }}
run: ./ci/github/integration-testing/docker-api-proxy.bash clean_up
- uses: codecov/codecov-action@v5
if: ${{ !cancelled() }}
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
with:
flags: integrationtests #optional

integration-test-simcore-sdk:
needs: [changes, build-test-images]
if: ${{ needs.changes.outputs.anything-py == 'true' || needs.changes.outputs.simcore-sdk == 'true' || github.event_name == 'push' }}
Expand Down Expand Up @@ -2262,6 +2331,7 @@ jobs:
integration-test-director-v2-01,
integration-test-director-v2-02,
integration-test-dynamic-sidecar,
integration-test-docker-api-proxy,
integration-test-simcore-sdk,
integration-test-webserver-01,
integration-test-webserver-02,
Expand Down
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ SERVICES_NAMES_TO_BUILD := \
payments \
resource-usage-tracker \
dynamic-scheduler \
docker-api-proxy \
service-integration \
static-webserver \
storage \
Expand Down
40 changes: 40 additions & 0 deletions ci/github/integration-testing/docker-api-proxy.bash
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#!/bin/bash
# http://redsymbol.net/articles/unofficial-bash-strict-mode/
set -o errexit # abort on nonzero exitstatus
set -o nounset # abort on unbound variable
set -o pipefail # don't hide errors within pipes
IFS=$'\n\t'

install() {
make devenv
# shellcheck source=/dev/null
source .venv/bin/activate
pushd services/docker-api-proxy
make install-ci
popd
uv pip list
make info-images
}

test() {
# shellcheck source=/dev/null
source .venv/bin/activate
pushd services/docker-api-proxy
make test-ci-integration
popd
}

clean_up() {
docker images
make down
}

# Check if the function exists (bash specific)
if declare -f "$1" >/dev/null; then
# call arguments verbatim
"$@"
else
# Show a helpful error
echo "'$1' is not a known function name" >&2
exit 1
fi
52 changes: 52 additions & 0 deletions packages/pytest-simcore/src/pytest_simcore/docker_api_proxy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import logging

import pytest
from aiohttp import ClientSession, ClientTimeout
from pydantic import TypeAdapter
from settings_library.docker_api_proxy import DockerApiProxysettings
from tenacity import before_sleep_log, retry, stop_after_delay, wait_fixed

from .helpers.docker import get_service_published_port
from .helpers.host import get_localhost_ip
from .helpers.typing_env import EnvVarsDict

_logger = logging.getLogger(__name__)


@retry(
wait=wait_fixed(1),
stop=stop_after_delay(10),
before_sleep=before_sleep_log(_logger, logging.INFO),
reraise=True,
)
async def _wait_till_docker_api_proxy_is_responsive(
settings: DockerApiProxysettings,
) -> None:
async with ClientSession(timeout=ClientTimeout(1, 1, 1, 1, 1)) as client:
response = await client.get(f"{settings.base_url}/version")
assert response.status == 200, await response.text()


@pytest.fixture
async def docker_api_proxy_settings(
docker_stack: dict, env_vars_for_docker_compose: EnvVarsDict
) -> DockerApiProxysettings:
"""Returns the settings of a redis service that is up and responsive"""

prefix = env_vars_for_docker_compose["SWARM_STACK_NAME"]
assert f"{prefix}_docker-api-proxy" in docker_stack["services"]

published_port = get_service_published_port(
"docker-api-proxy", int(env_vars_for_docker_compose["DOCKER_API_PROXY_PORT"])
)

settings = TypeAdapter(DockerApiProxysettings).validate_python(
{
"DOCKER_API_PROXY_HOST": get_localhost_ip(),
"DOCKER_API_PROXY_PORT": published_port,
}
)

await _wait_till_docker_api_proxy_is_responsive(settings)

return settings
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,12 @@
"invitations": "/",
"payments": "/",
"resource-usage-tracker": "/",
"docker-api-proxy": "/version",
}
AIOHTTP_BASED_SERVICE_PORT: int = 8080
FASTAPI_BASED_SERVICE_PORT: int = 8000
DASK_SCHEDULER_SERVICE_PORT: int = 8787
DOCKER_API_PROXY_SERVICE_PORT: int = 8888

_SERVICE_NAME_REPLACEMENTS: dict[str, str] = {
"dynamic-scheduler": "dynamic-schdlr",
Expand Down Expand Up @@ -133,6 +135,7 @@ def services_endpoint(
AIOHTTP_BASED_SERVICE_PORT,
FASTAPI_BASED_SERVICE_PORT,
DASK_SCHEDULER_SERVICE_PORT,
DOCKER_API_PROXY_SERVICE_PORT,
]
endpoint = URL(
f"http://{get_localhost_ip()}:{get_service_published_port(full_service_name, target_ports)}"
Expand Down
23 changes: 23 additions & 0 deletions packages/service-library/src/servicelib/docker_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@
from typing import Any, Final, Literal

import aiodocker
import aiohttp
import arrow
from aiohttp import ClientSession
from models_library.docker import DockerGenericTag
from models_library.generated_models.docker_rest_api import ProgressDetail
from models_library.utils.change_case import snake_to_camel
from pydantic import BaseModel, ByteSize, ConfigDict, TypeAdapter, ValidationError
from settings_library.docker_api_proxy import DockerApiProxysettings
from settings_library.docker_registry import RegistrySettings
from yarl import URL

Expand Down Expand Up @@ -281,3 +284,23 @@ async def pull_image(
f"pulling {image_short_name}: {pull_progress}...",
logging.DEBUG,
)


async def create_remote_docker_client(
settings: DockerApiProxysettings,
) -> aiodocker.Docker:
"""Allows to attach to a remote docker client istnace and use it in place
of the docker socket.

NOTE: Usually this is required to use a manager node's docker client for swarm commands.
"""
session: ClientSession | None = None
if settings.DOCKER_API_PROXY_USER and settings.DOCKER_API_PROXY_PASSWORD:
session = ClientSession(
auth=aiohttp.BasicAuth(
login=settings.DOCKER_API_PROXY_USER,
password=settings.DOCKER_API_PROXY_PASSWORD.get_secret_value(),
)
)

return aiodocker.Docker(url=settings.base_url, session=session)
GitHK marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from functools import cached_property

from pydantic import Field, SecretStr

from .base import BaseCustomSettings
from .basic_types import PortInt


class DockerApiProxysettings(BaseCustomSettings):
DOCKER_API_PROXY_HOST: str = Field(
description="hostname of the docker-api-proxy service"
)
DOCKER_API_PROXY_PORT: PortInt = Field(
8888, description="port of the docker-api-proxy service"
)
DOCKER_API_PROXY_SECURE: bool = False

DOCKER_API_PROXY_USER: str | None = None
DOCKER_API_PROXY_PASSWORD: SecretStr | None = None

@cached_property
def base_url(self) -> str:
protocl = "https" if self.DOCKER_API_PROXY_SECURE else "http"
return f"{protocl}://{self.DOCKER_API_PROXY_HOST}:{self.DOCKER_API_PROXY_PORT}"
19 changes: 19 additions & 0 deletions services/docker-api-proxy/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
FROM alpine:3.21 AS base

RUN apk add --no-cache socat curl

# Health check to ensure the proxy is running
HEALTHCHECK \
--interval=10s \
--timeout=5s \
--start-period=30s \
--start-interval=1s \
--retries=5 \
CMD curl http://localhost:8888/version || exit 1

ENTRYPOINT ["socat"]

CMD ["TCP-LISTEN:8888,fork,reuseaddr", "UNIX-CONNECT:/var/run/docker.sock"]

FROM base AS development
FROM base AS production
GitHK marked this conversation as resolved.
Show resolved Hide resolved
2 changes: 2 additions & 0 deletions services/docker-api-proxy/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
include ../../scripts/common.Makefile
include ../../scripts/common-service.Makefile
6 changes: 6 additions & 0 deletions services/docker-api-proxy/requirements/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#
# Targets to pip-compile requirements
#
include ../../../requirements/base.Makefile

# Add here any extra explicit dependency: e.g. _migration.txt: _base.txt
Empty file.
Empty file.
18 changes: 18 additions & 0 deletions services/docker-api-proxy/requirements/_test.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
--constraint ../../../requirements/constraints.txt

--requirement ../../../packages/common-library/requirements/_base.in
--requirement ../../../packages/models-library/requirements/_base.in
--requirement ../../../packages/service-library/requirements/_base.in
GitHK marked this conversation as resolved.
Show resolved Hide resolved

aiodocker
docker
faker
flaky
pytest
pytest-asyncio
pytest-cov
pytest-mock
python-dotenv
PyYAML
tenacity
arrow
Loading
Loading