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

Fix: DockerAPI Execute stream #52

Merged
merged 14 commits into from
Aug 13, 2024
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
16 changes: 16 additions & 0 deletions .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
name: "CodeQL"

on: [pull_request]
jobs:
lint:
name: CodeQL
runs-on: ubuntu-latest

steps:
- name: Check out the repo
uses: actions/checkout@v2

- name: Run CodeQL
run: |
docker run --rm -v $PWD:/app composer sh -c \
"composer install --profile --ignore-platform-reqs && composer check"
10 changes: 7 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,13 @@ jobs:

- name: Validate composer.json and composer.lock
run: composer validate --strict

- name: Install dependencies
run: composer install --ignore-platform-reqs --optimize-autoloader --no-plugins --no-scripts --prefer-dist

- name: Compose install
run: composer install --ignore-platform-reqs
- name: Start container
# For local testing, also run this before retrying tests: docker rm --force $(docker ps -aq)
run: docker compose up -d && sleep 15

- name: Run tests
run: composer test
run: docker compose exec tests vendor/bin/phpunit --configuration phpunit.xml
10 changes: 8 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,22 @@ COPY composer.lock /usr/local/src/
COPY composer.json /usr/local/src/

RUN composer install --ignore-platform-reqs --optimize-autoloader --no-plugins --no-scripts --prefer-dist


RUN docker-php-ext-install sockets

FROM php:8.0-cli-alpine as final

ENV DOCKER_CONFIG=${DOCKER_CONFIG:-$HOME/.docker}
ENV DOCKER_API_VERSION=1.43

LABEL maintainer="team@appwrite.io"

RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

RUN \
apk update \
&& apk add --no-cache make automake autoconf gcc g++ git brotli-dev \
&& apk add --no-cache make automake autoconf gcc g++ git brotli-dev docker-cli \
&& docker-php-ext-install sockets \
&& docker-php-ext-install opcache

WORKDIR /usr/src/code
Expand Down
28 changes: 14 additions & 14 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ services:
context: .
networks:
- orchestration
volumes:
environment:
HOST_DIR: "$PWD" # Nessessary to mount test resources to child containers
volumes:
- ./:/usr/src/code
- /var/run/docker.sock:/var/run/docker.sock

Expand Down
58 changes: 37 additions & 21 deletions src/Orchestration/Adapter/DockerAPI.php
Original file line number Diff line number Diff line change
Expand Up @@ -130,34 +130,50 @@ protected function streamCall(string $url, int $timeout = -1): array
$stdout = '';
$stderr = '';

$callback = function (mixed $ch, string $str) use (&$stdout, &$stderr): int {
$isHeader = true;
$currentHeader = null;
$currentData = '';

$callback = function (mixed $ch, string $str) use (&$stdout, &$stderr, &$isHeader, &$currentHeader, &$currentData): int {
if (empty($str)) {
return 0;
}

$rawStream = unpack('C*', $str);
$stream = $rawStream[1]; // 1-based index, not 0-based

// Ascii encoding support
if ($stream === \ord('1')) {
$stream = 1;
} elseif ($stream === \ord('2')) {
$stream = 2;
}

switch ($stream) { // only 1 or 2, as set while creating exec
case 1:
$packed = pack('C*', ...\array_slice($rawStream, 8));
$stdout .= $packed;
break;
case 2:
$packed = pack('C*', ...\array_slice($rawStream, 8));
$stderr .= $packed;
break;
$originalSize = \mb_strlen($str);

while (! empty($str)) {
if ($isHeader) {
$header = \unpack('Ctype/Cfill1/Cfill2/Cfill3/Nsize', $str);
$str = \mb_strcut($str, 8, null);
$isHeader = false;
$currentHeader = $header;
} else {
$size = $currentHeader['size'];
$type = $currentHeader['type'];

if (\strlen($str) >= $size) {
$currentData .= \mb_substr($str, 0, $size);
$str = \mb_strcut($str, $size, null);
$isHeader = true;
$currentHeader = null;

if ($type === 1) {
$stdout .= $currentData;
} else {
$stderr .= $currentData;
}
$currentData = '';
} else {
$currentHeader['size'] -= \mb_strlen($str);
$currentData .= $str;
$str = '';
}
}
}

return strlen($str); // must return full frame from callback
return $originalSize; // must return full frame from callback
};

\curl_setopt($ch, CURLOPT_WRITEFUNCTION, $callback);

\curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
Expand Down
Loading
Loading