diff --git a/.github/wait.sh b/.github/wait.sh deleted file mode 100755 index 5f9e39a4..00000000 --- a/.github/wait.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# -echo "You have an hour to debug this build" -echo 'do "touch /tmp/continue" to stop the wait' -for i in $(seq 1 60) -do echo $i - sleep 60 - if test -e /tmp/continue - then break - fi -done diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index e50b7347..feea6e0a 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -16,33 +16,40 @@ # under the License. # -name: openserverless-operator-check -on: - push: - branches: [main] +name: Check +on: pull_request: - branches: [main] + workflow_dispatch: jobs: check: name: Check OpenServerless Operator runs-on: ubuntu-22.04 + permissions: + contents: read steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v7 with: submodules: recursive - - name: License - uses: apache/skywalking-eyes@main + - name: License + uses: apache/skywalking-eyes@v0.9.0 - name: Assign TAG run: | TAG=$(git rev-parse --short HEAD) echo "IMG_TAG=$TAG" >> "$GITHUB_ENV" echo "OPERATOR_TAG=$TAG" >> "$GITHUB_ENV" - - name: Assign Custom Image Name - if: ${{ github.repository_owner != 'apache'}} + - name: Generate .env run: | - echo "MY_OPERATOR_IMAGE=${{ vars.MY_OPERATOR_IMAGE }}" >> "$GITHUB_ENV" + OWNER=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]') + if [ "$OWNER" = "apache" ]; then + IMAGE=$(awk -F= '/ARG OPERATOR_IMAGE_DEFAULT=/ { print $2 ; exit }' Dockerfile) + else + IMAGE="ghcr.io/$OWNER/openserverless-operator" + fi + echo "GITHUB_USER=$OWNER" > .env + echo "MY_OPERATOR_IMAGE=$IMAGE" >> .env + cat .env - name: Kind uses: container-tools/kind-action@v2.0.4 with: @@ -50,7 +57,7 @@ jobs: cluster_name: openserverless config: .github/kind.yaml - name: Set up Python 3.12 - uses: actions/setup-python@v4 + uses: actions/setup-python@v7 with: python-version: 3.12 - name: Setup @@ -61,18 +68,9 @@ jobs: run: task build-and-load - name: AddIngress run: task kind:ingress - - name: Ngrok - if: ${{ env.NGROK == 'on' }} - run: bash .github/debug-github-actions.sh - env: - NGROK_TOKEN: ${{ secrets.NGROK_TOKEN }} - USER_PASS: ${{ secrets.USER_PASS }} - - name: Wait - if: ${{ env.NGROK == 'on' }} - run: bash .github/wait.sh - name: Unit Test run: task utest continue-on-error: false - name: Integration Test run: task itest - continue-on-error: false + continue-on-error: false diff --git a/.github/workflows/image.yml b/.github/workflows/image.yml index 42ccfd81..b5ba46e9 100644 --- a/.github/workflows/image.yml +++ b/.github/workflows/image.yml @@ -16,42 +16,65 @@ # under the License. # -name: Build OpenServerless Operator +name: Build Image on: push: tags: - - '[0-9]*' + - '*.*.*.*' + branches-ignore: + - '*' + workflow_dispatch: + jobs: image: name: Build OpenServerless Operator Image runs-on: ubuntu-22.04 + permissions: + contents: write + packages: write steps: - - name: Remove unnecessary files - run: | - sudo rm -rf /usr/share/dotnet - sudo rm -rf "$AGENT_TOOLSDIRECTORY" + #- name: Remove unnecessary files + # run: | + # sudo rm -rf /usr/share/dotnet + # sudo rm -rf "$AGENT_TOOLSDIRECTORY" - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v7 with: submodules: recursive + # full history and tags: "task tag" replaces the existing tags + # and "git describe" needs them to be present + fetch-depth: 0 - name: License - uses: apache/skywalking-eyes@main - - name: Assign TAG from pull request - if: ${{ github.event_name == 'pull_request' }} + uses: apache/skywalking-eyes@v0.9.0 + # Manual run: create a new snapshot tag on the current HEAD and push it. + # The push of the tag does not start another run (GITHUB_TOKEN pushes do + # not trigger workflows), so this same run goes on and builds it. + - name: Tag HEAD (manual run) + if: ${{ github.event_name == 'workflow_dispatch' }} run: | - echo "IMG_TAG=$(git rev-parse --short HEAD)" >> "$GITHUB_ENV" + # task is installed later by cisetup.sh, get it now + sudo sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b /usr/local/bin + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + task tag + task ci - name: Assign TAG from push - if: ${{ github.event_name != 'pull_request' }} run: | echo "IMG_TAG=$(git describe --tags --abbrev=0)" >> "$GITHUB_ENV" - #- name: Assign Custom Image Name - # if: ${{ github.repository_owner != 'apache'}} - # run: | - # echo "MY_OPERATOR_IMAGE=ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}" >> "$GITHUB_ENV" - # echo "IMAGE_REGISTRY=ghcr.io" >> "$GITHUB_ENV" + - name: Generate .env + run: | + OWNER=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]') + if [ "$OWNER" = "apache" ]; then + IMAGE=$(awk -F= '/ARG OPERATOR_IMAGE_DEFAULT=/ { print $2 ; exit }' Dockerfile) + else + IMAGE="ghcr.io/$OWNER/openserverless-operator" + fi + echo "GITHUB_USER=$OWNER" > .env + echo "MY_OPERATOR_IMAGE=$IMAGE" >> .env + cat .env - name: Set up Python 3.12 - uses: actions/setup-python@v4 + uses: actions/setup-python@v7 with: python-version: 3.12 - name: Setup @@ -74,17 +97,42 @@ jobs: #- name: Integration Test # run: task itest # continue-on-error: false + + - name: Select registry + env: + DOCKERHUB_REGISTRY: ${{ secrets.DOCKERHUB_REGISTRY }} + run: | + if [ -n "$DOCKERHUB_REGISTRY" ]; then + REGISTRY="$DOCKERHUB_REGISTRY" + elif [ "${{ github.repository_owner }}" = "apache" ]; then + REGISTRY="docker.io" + else + REGISTRY="ghcr.io" + fi + echo "REGISTRY=$REGISTRY" >> "$GITHUB_ENV" + + - name: Select registry credentials + run: | + if [ "${REGISTRY%%/*}" = "ghcr.io" ]; then + echo "REGISTRY_USER=${{ github.actor }}" >> "$GITHUB_ENV" + echo "REGISTRY_TOKEN=${{ secrets.GITHUB_TOKEN }}" >> "$GITHUB_ENV" + else + echo "REGISTRY_USER=${{ secrets.DOCKERHUB_USER }}" >> "$GITHUB_ENV" + echo "REGISTRY_TOKEN=${{ secrets.DOCKERHUB_TOKEN }}" >> "$GITHUB_ENV" + fi - name: Registry login uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0 with: - registry: 'docker.io' - username: ${{ secrets.DOCKERHUB_USER }} - password: ${{ secrets.DOCKERHUB_TOKEN }} + registry: ${{ env.REGISTRY }} + username: ${{ env.REGISTRY_USER }} + password: ${{ env.REGISTRY_TOKEN }} + - name: Set up QEMU uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4.2.0 with: image: tonistiigi/binfmt:qemu-v7.0.0-28 - name: Set up Docker Buildx uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0 + - name: Image run: task buildx-and-push TAG="$IMG_TAG" diff --git a/.github/workflows/trigger-testing.yaml b/.github/workflows/trigger-testing.yaml deleted file mode 100644 index 1dfcaba1..00000000 --- a/.github/workflows/trigger-testing.yaml +++ /dev/null @@ -1,94 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# -name: Trigger Testing -run-name: Dispatch operator PR test for PR #${{ github.event.pull_request.number }} - -on: - pull_request_target: - types: [labeled, synchronize, reopened] - -jobs: - dispatch: - name: Dispatch operator-pr-test - runs-on: ubuntu-22.04 - steps: - - name: Parse testing tag - id: parse - env: - PR_NUMBER: ${{ github.event.pull_request.number }} - PR_REF: ${{ github.event.pull_request.head.ref }} - PR_SHA: ${{ github.event.pull_request.head.sha }} - PR_REPO: ${{ github.event.pull_request.head.repo.full_name }} - run: | - VALID_TEST_RE='^(kind|k3s|k3sarm|k8s|k8sarm|mk8s|mk8sarm|eks|eksarm|aks|aksarm|gke|gkearm|osh|osharm)-([0-9a-f]{7,40})$' - MATCHING_TAGS=() - - while IFS= read -r label; do - [[ -n "$label" ]] || continue - if [[ "$label" =~ $VALID_TEST_RE ]]; then - label_hash="${BASH_REMATCH[2]}" - if [[ "$PR_SHA" == "$label_hash"* ]]; then - MATCHING_TAGS+=("$label") - fi - fi - done < <(jq -r '.pull_request.labels[]?.name // empty' "$GITHUB_EVENT_PATH") - - echo "pr_number=$PR_NUMBER" >> "$GITHUB_OUTPUT" - echo "pr_ref=$PR_REF" >> "$GITHUB_OUTPUT" - echo "pr_sha=$PR_SHA" >> "$GITHUB_OUTPUT" - echo "repo=$PR_REPO" >> "$GITHUB_OUTPUT" - - if [[ "${#MATCHING_TAGS[@]}" -gt 1 ]]; then - echo "Found multiple matching - tags for PR SHA $PR_SHA:" >&2 - printf ' - %s\n' "${MATCHING_TAGS[@]}" >&2 - exit 1 - fi - - if [[ "${#MATCHING_TAGS[@]}" -eq 1 ]]; then - TEST_TAG="${MATCHING_TAGS[0]}" - TEST_NAME="${TEST_TAG%-*}" - TEST_HASH="${TEST_TAG##*-}" - echo "enabled=true" >> "$GITHUB_OUTPUT" - echo "test_tag=$TEST_TAG" >> "$GITHUB_OUTPUT" - echo "test_name=$TEST_NAME" >> "$GITHUB_OUTPUT" - echo "test_hash=$TEST_HASH" >> "$GITHUB_OUTPUT" - echo "reason=Matched testing tag $TEST_TAG for PR SHA $PR_SHA" >> "$GITHUB_OUTPUT" - else - echo "enabled=false" >> "$GITHUB_OUTPUT" - echo "reason=No - tag found on PR labels for current SHA $PR_SHA" >> "$GITHUB_OUTPUT" - fi - - - name: Skip when no matching testing tag is present - if: steps.parse.outputs.enabled != 'true' - run: echo "${{ steps.parse.outputs.reason }}" - - - name: Dispatch to testing repo - if: steps.parse.outputs.enabled == 'true' - env: - GH_TOKEN: ${{ secrets.OPENSERVERLESS_TESTING_PAT }} - run: | - gh api repos/${{ github.repository_owner }}/openserverless-testing/dispatches \ - -X POST \ - -f event_type=operator-pr-test \ - -f 'client_payload[pr_number]=${{ steps.parse.outputs.pr_number }}' \ - -f 'client_payload[pr_ref]=${{ steps.parse.outputs.pr_ref }}' \ - -f 'client_payload[pr_sha]=${{ steps.parse.outputs.pr_sha }}' \ - -f 'client_payload[operator_repo]=${{ steps.parse.outputs.repo }}' \ - -f 'client_payload[test_tag]=${{ steps.parse.outputs.test_tag }}' \ - -f 'client_payload[test_name]=${{ steps.parse.outputs.test_name }}' \ - -f 'client_payload[test_hash]=${{ steps.parse.outputs.test_hash }}' diff --git a/.licenserc.yaml b/.licenserc.yaml index 37ea1186..4cabfb12 100644 --- a/.licenserc.yaml +++ b/.licenserc.yaml @@ -31,6 +31,7 @@ header: - '**/LICENSE' - '**/DISCLAIMER' - '**/NOTICE' + - '**/WARN' - '**/package-lock.json' - '**/.*-version' - '**/*.json' diff --git a/Dockerfile b/Dockerfile index 489de389..f2130457 100644 --- a/Dockerfile +++ b/Dockerfile @@ -94,7 +94,7 @@ RUN echo "Installing poetry" && \ # Install minimal dependencies echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections && \ apt-get update && apt-get install -y --no-install-recommends \ - curl gnupg zip unzip && \ + curl zip unzip && \ curl -sSL https://install.python-poetry.org | python - && \ cd /home/openserverless && poetry install --no-root --no-interaction --no-ansi && rm -rf $POETRY_CACHE_DIR @@ -125,7 +125,7 @@ RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone & # Install minimal dependencies echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections && \ apt-get update && apt-get install -y --no-install-recommends \ - curl gnupg zip unzip && \ + curl zip unzip && \ apt-get clean && rm -rf /var/lib/apt/lists/* && \ # install kubectl KVER="v1.23.0" && \ @@ -137,10 +137,6 @@ RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone & WSK_VERSION=1.2.0 && \ WSK_BASE=https://github.com/apache/openwhisk-cli/releases/download && \ curl -sL "$WSK_BASE/$WSK_VERSION/OpenWhisk_CLI-$WSK_VERSION-linux-$ARCH.tgz" | tar xzvf - -C /usr/bin/ && \ - # install minio - MINIO_BASE=https://dl.min.io/client/mc/release/linux && \ - MC_VER=RELEASE.2025-05-21T01-59-54Z && \ - curl -sL "$MINIO_BASE-$ARCH/archive/mc.${MC_VER}" -o /usr/bin/mc && chmod +x /usr/bin/mc && \ # install taskfile curl -sL https://taskfile.dev/install.sh | sh -s -- -d -b /usr/bin @@ -157,4 +153,7 @@ RUN poetry install --only main --no-interaction --no-ansi && rm -rf ${POETRY_CAC RUN mkdir -p /home/openserverless/deploy/whisk-system && \ ./whisk-system.sh && \ cd deploy && tar cvf ../deploy.tar * +# Apache release metadata (see DISCLAIMER, LICENSE, NOTICE) +COPY DISCLAIMER LICENSE NOTICE / + CMD ["./run.sh"] \ No newline at end of file diff --git a/LICENSE b/LICENSE index dc2c22e2..1bffe5eb 100644 --- a/LICENSE +++ b/LICENSE @@ -215,4 +215,238 @@ Spray Caching 1.3.4 (io.spray:spray-caching_2.11:1.3.4 - http://spray.io/documen This product bundles the files gradlew and gradlew.bat from Gradle v5.5 which are distributed under the Apache License, Version 2.0. -For details see ./gradlew and ./gradlew.bat. \ No newline at end of file +For details see ./gradlew and ./gradlew.bat. + + +============================================================================== +BEGIN BUNDLED THIRD-PARTY SOFTWARE INVENTORY (container image) +============================================================================== + +============================================================================== +LICENSES OF BUNDLED THIRD-PARTY SOFTWARE +Container image: apache/openserverless-operator +============================================================================== + +Apache OpenServerless itself is licensed under the Apache License, +Version 2.0, reproduced above. The container image built from the +Dockerfile beside this file additionally bundles the third-party +components listed below, each under its own license. + +Refer to https://www.apache.org/legal/resolved.html for the ASF +classification of these licenses. Components that are NOT compatible +with the Apache License 2.0 are enumerated in the WARN file. + +------------------------------------------------------------------------------ +Summary by license +------------------------------------------------------------------------------ + +Apache-2.0 + OpenWhisk CLI (wsk) (1.2.0) + aiosignal (1.4.0) + asttokens (3.0.0) + bcrypt (4.2.1) + frozenlist (1.5.0) + google-auth (2.37.0) + kubectl (v1.23.0) + kubernetes (31.0.0) + kustomize (v4.5.7) + minio (7.2.14) + multidict (6.1.0) + propcache (0.2.1) + pykube-ng (23.6.0) + requests (2.32.4) + rsa (4.9) + tzdata (2024.2) + websocket-client (1.8.0) + yarl (1.18.3) + +Apache-2.0 AND MIT + aiohttp (3.14.3) + +Apache-2.0 OR BSD-3-Clause + python-dateutil (2.9.0.post0) + +BSD-2-Clause + decorator (5.1.1) + pyasn1 (0.6.4) + pyasn1-modules (0.4.2) + pygments (2.19.1) + python-json-logger (3.2.1) + +BSD-2-Clause AND Public Domain + pycryptodome (3.21.0) + +BSD-3-Clause + click (8.1.8) + colorama (0.4.6) + flatdict (4.1.0) + idna (3.10) + ipython (8.31.0) + jinja2 (3.1.5) + markupsafe (3.0.2) + matplotlib-inline (0.1.7) + oauthlib (3.2.2) + pg8000 (1.31.5) + prompt-toolkit (3.0.48) + pycparser (2.22) + traitlets (5.14.3) + +ISC + pexpect (4.9.0) + ptyprocess (0.7.0) + requests-oauthlib (2.0.0) + +MIT-0 + scramp (1.4.17) + +MIT + Task (taskfile) (latest) + argon2-cffi (23.1.0) + asn1crypto (1.5.1) + argon2-cffi-bindings (21.2.0) + attrs (24.3.0) + backoff (2.2.1) + cachetools (5.5.0) + cffi (1.17.1) + charset-normalizer (3.4.1) + croniter (1.4.1) + durationpy (0.9) + executing (2.1.0) + iso8601 (2.1.0) + jedi (0.19.2) + kopf (1.37.4) + parso (0.8.4) + poetry (2.3.2) + pure-eval (0.2.3) + pyyaml (6.0.2) + redis (5.2.1) + six (1.17.0) + stack-data (0.6.3) + urllib3 (2.3.0) + wcwidth (0.2.13) + +MPL-2.0 + certifi (2024.12.14) + +PSF-2.0 + aiohappyeyeballs (2.7.1) + typing-extensions (4.12.2) + +curl: MIT; zip/unzip: Info-ZIP + curl, zip, unzip ((Debian bullseye)) + +mixed; see WARN + (see the FROM line of the Dockerfile) (-) + +------------------------------------------------------------------------------ +Detail by component +------------------------------------------------------------------------------ + +Python runtime dependencies (pyproject.toml / poetry.lock) +---------------------------------------------------------- + aiohappyeyeballs (2.7.1) -- PSF-2.0 + aiohttp (3.14.3) -- Apache-2.0 AND MIT + aiosignal (1.4.0) -- Apache-2.0 + argon2-cffi (23.1.0) -- MIT + argon2-cffi-bindings (21.2.0) -- MIT + asn1crypto (1.5.1) -- MIT + asttokens (3.0.0) -- Apache-2.0 + attrs (24.3.0) -- MIT + backoff (2.2.1) -- MIT + bcrypt (4.2.1) -- Apache-2.0 + cachetools (5.5.0) -- MIT + certifi (2024.12.14) -- MPL-2.0 + Category B: used unmodified, as a separate file. + cffi (1.17.1) -- MIT + charset-normalizer (3.4.1) -- MIT + click (8.1.8) -- BSD-3-Clause + colorama (0.4.6) -- BSD-3-Clause + croniter (1.4.1) -- MIT + decorator (5.1.1) -- BSD-2-Clause + durationpy (0.9) -- MIT + executing (2.1.0) -- MIT + flatdict (4.1.0) -- BSD-3-Clause + frozenlist (1.5.0) -- Apache-2.0 + google-auth (2.37.0) -- Apache-2.0 + idna (3.10) -- BSD-3-Clause + ipython (8.31.0) -- BSD-3-Clause + Dev-only dependency. + iso8601 (2.1.0) -- MIT + jedi (0.19.2) -- MIT + jinja2 (3.1.5) -- BSD-3-Clause + kopf (1.37.4) -- MIT + kubernetes (31.0.0) -- Apache-2.0 + markupsafe (3.0.2) -- BSD-3-Clause + matplotlib-inline (0.1.7) -- BSD-3-Clause + minio (7.2.14) -- Apache-2.0 + multidict (6.1.0) -- Apache-2.0 + oauthlib (3.2.2) -- BSD-3-Clause + parso (0.8.4) -- MIT + pexpect (4.9.0) -- ISC + pg8000 (1.31.5) -- BSD-3-Clause + prompt-toolkit (3.0.48) -- BSD-3-Clause + propcache (0.2.1) -- Apache-2.0 + ptyprocess (0.7.0) -- ISC + pure-eval (0.2.3) -- MIT + pyasn1 (0.6.4) -- BSD-2-Clause + pyasn1-modules (0.4.2) -- BSD-2-Clause + pycparser (2.22) -- BSD-3-Clause + pycryptodome (3.21.0) -- BSD-2-Clause AND Public Domain + pygments (2.19.1) -- BSD-2-Clause + pykube-ng (23.6.0) -- Apache-2.0 + python-dateutil (2.9.0.post0) -- Apache-2.0 OR BSD-3-Clause + python-json-logger (3.2.1) -- BSD-2-Clause + pyyaml (6.0.2) -- MIT + redis (5.2.1) -- MIT + requests (2.32.4) -- Apache-2.0 + requests-oauthlib (2.0.0) -- ISC + rsa (4.9) -- Apache-2.0 + scramp (1.4.17) -- MIT-0 + six (1.17.0) -- MIT + stack-data (0.6.3) -- MIT + traitlets (5.14.3) -- BSD-3-Clause + typing-extensions (4.12.2) -- PSF-2.0 + tzdata (2024.2) -- Apache-2.0 + urllib3 (2.3.0) -- MIT + wcwidth (0.2.13) -- MIT + websocket-client (1.8.0) -- Apache-2.0 + yarl (1.18.3) -- Apache-2.0 + +Build-time and bundled command-line tools +----------------------------------------- + poetry (2.3.2) -- MIT + Build-time dependency manager. + kubectl (v1.23.0) -- Apache-2.0 + Downloaded from dl.k8s.io at build time. + kustomize (v4.5.7) -- Apache-2.0 + kubernetes-sigs/kustomize. + OpenWhisk CLI (wsk) (1.2.0) -- Apache-2.0 + Apache OpenWhisk. + Task (taskfile) (latest) -- MIT + Installed from taskfile.dev at build time. + curl, zip, unzip ((Debian bullseye)) -- curl: MIT; zip/unzip: Info-ZIP + +Base operating system image +--------------------------- + (see the FROM line of the Dockerfile) (-) -- mixed; see WARN + Operating-system components are supplied by the upstream base image and are not + redistributed by the ASF in source form. + +------------------------------------------------------------------------------ +Full license texts +------------------------------------------------------------------------------ + +The complete text of the Apache License 2.0 is reproduced at the top of +this file. The complete texts of the other licenses referenced above are +distributed with each component inside the container image, in the +package metadata directories of the respective package manager: + + * Python packages: /.dist-info/ (LICENSE, METADATA) + * Node.js packages: /node_modules//LICENSE + * PHP packages: /phpAction/composer/vendor//LICENSE + * Go modules: vendored module sources / pkg.go.dev + * Debian packages: /usr/share/doc//copyright + +============================================================================== +END BUNDLED THIRD-PARTY SOFTWARE INVENTORY +============================================================================== diff --git a/NOTICE b/NOTICE index 9b094683..7f6547bc 100644 --- a/NOTICE +++ b/NOTICE @@ -4,3 +4,193 @@ Copyright 2024 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (http://www.apache.org/). + +============================================================================== +BEGIN BUNDLED THIRD-PARTY SOFTWARE INVENTORY (container image) +============================================================================== + +------------------------------------------------------------------------------ +Container image: apache/openserverless-operator +------------------------------------------------------------------------------ + +This image bundles third-party software. The components below are +redistributed under the licenses recorded in the LICENSE file that +accompanies this NOTICE. Components flagged '*** SEE WARN ***' are +listed in the WARN file as incompatible with the Apache License 2.0. + +Python runtime dependencies (pyproject.toml / poetry.lock) +---------------------------------------------------------- + * aiohappyeyeballs (2.7.1) + License: PSF-2.0 + * aiohttp (3.14.3) + License: Apache-2.0 AND MIT + * aiosignal (1.4.0) + License: Apache-2.0 + * argon2-cffi (23.1.0) + License: MIT + * argon2-cffi-bindings (21.2.0) + License: MIT + * asn1crypto (1.5.1) + License: MIT + * asttokens (3.0.0) + License: Apache-2.0 + * attrs (24.3.0) + License: MIT + * backoff (2.2.1) + License: MIT + * bcrypt (4.2.1) + License: Apache-2.0 + * cachetools (5.5.0) + License: MIT + * certifi (2024.12.14) + License: MPL-2.0 + Category B: used unmodified, as a separate file. + * cffi (1.17.1) + License: MIT + * charset-normalizer (3.4.1) + License: MIT + * click (8.1.8) + License: BSD-3-Clause + * colorama (0.4.6) + License: BSD-3-Clause + * croniter (1.4.1) + License: MIT + * decorator (5.1.1) + License: BSD-2-Clause + * durationpy (0.9) + License: MIT + * executing (2.1.0) + License: MIT + * flatdict (4.1.0) + License: BSD-3-Clause + * frozenlist (1.5.0) + License: Apache-2.0 + * google-auth (2.37.0) + License: Apache-2.0 + * idna (3.10) + License: BSD-3-Clause + * ipython (8.31.0) + License: BSD-3-Clause + Dev-only dependency. + * iso8601 (2.1.0) + License: MIT + * jedi (0.19.2) + License: MIT + * jinja2 (3.1.5) + License: BSD-3-Clause + * kopf (1.37.4) + License: MIT + * kubernetes (31.0.0) + License: Apache-2.0 + * markupsafe (3.0.2) + License: BSD-3-Clause + * matplotlib-inline (0.1.7) + License: BSD-3-Clause + * minio (7.2.14) + License: Apache-2.0 + * multidict (6.1.0) + License: Apache-2.0 + * oauthlib (3.2.2) + License: BSD-3-Clause + * parso (0.8.4) + License: MIT + * pexpect (4.9.0) + License: ISC + * pg8000 (1.31.5) + License: BSD-3-Clause + * prompt-toolkit (3.0.48) + License: BSD-3-Clause + * propcache (0.2.1) + License: Apache-2.0 + * ptyprocess (0.7.0) + License: ISC + * pure-eval (0.2.3) + License: MIT + * pyasn1 (0.6.4) + License: BSD-2-Clause + * pyasn1-modules (0.4.2) + License: BSD-2-Clause + * pycparser (2.22) + License: BSD-3-Clause + * pycryptodome (3.21.0) + License: BSD-2-Clause AND Public Domain + * pygments (2.19.1) + License: BSD-2-Clause + * pykube-ng (23.6.0) + License: Apache-2.0 + * python-dateutil (2.9.0.post0) + License: Apache-2.0 OR BSD-3-Clause + * python-json-logger (3.2.1) + License: BSD-2-Clause + * pyyaml (6.0.2) + License: MIT + * redis (5.2.1) + License: MIT + * requests (2.32.4) + License: Apache-2.0 + * requests-oauthlib (2.0.0) + License: ISC + * rsa (4.9) + License: Apache-2.0 + * scramp (1.4.17) + License: MIT-0 + * six (1.17.0) + License: MIT + * stack-data (0.6.3) + License: MIT + * traitlets (5.14.3) + License: BSD-3-Clause + * typing-extensions (4.12.2) + License: PSF-2.0 + * tzdata (2024.2) + License: Apache-2.0 + * urllib3 (2.3.0) + License: MIT + * wcwidth (0.2.13) + License: MIT + * websocket-client (1.8.0) + License: Apache-2.0 + * yarl (1.18.3) + License: Apache-2.0 + +Build-time and bundled command-line tools +----------------------------------------- + * poetry (2.3.2) + License: MIT + Build-time dependency manager. + * kubectl (v1.23.0) + License: Apache-2.0 + Downloaded from dl.k8s.io at build time. + * kustomize (v4.5.7) + License: Apache-2.0 + kubernetes-sigs/kustomize. + * OpenWhisk CLI (wsk) (1.2.0) + License: Apache-2.0 + Apache OpenWhisk. + * Task (taskfile) (latest) + License: MIT + Installed from taskfile.dev at build time. + * curl, zip, unzip ((Debian bullseye)) + License: curl: MIT; zip/unzip: Info-ZIP + +Base operating system image +--------------------------- + * (see the FROM line of the Dockerfile) (-) + License: mixed; see WARN + Operating-system components are supplied by the upstream base image and are not + redistributed by the ASF in source form. + +------------------------------------------------------------------------------ +Attribution required by bundled components +------------------------------------------------------------------------------ + +This product includes software developed by the Apache Software +Foundation (http://www.apache.org/), including Apache OpenWhisk +components and Apache-2.0 licensed libraries listed above. + +This product includes software developed by third parties under the +MIT, BSD, ISC, PSF, MPL-2.0 and other licenses, as recorded in LICENSE. + +============================================================================== +END BUNDLED THIRD-PARTY SOFTWARE INVENTORY +============================================================================== diff --git a/Taskfile.yml b/Taskfile.yml index 943494ec..efc0aa97 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -27,7 +27,9 @@ vars: sh: ./detect.sh NS: "openserverless" PREFIX: "" - TAG: + SUFFIX: "-snapshot" + TAG: "" + dotenv: - .env @@ -76,6 +78,24 @@ tasks: cmds: - task: use + tag: + silent: true + vars: + TS: + sh: date '+%y %m %d %H %M' | awk '{printf "%s%s%02d%s%02d\n", $1, substr("abcdefghijkl",$2+0,1), $3, substr("abcdefghijklmnopqrstuvwx",$4+1,1), $5}' + cmds: + - git tag -d $(git tag) + - | + TAG="{{.BASETAG}}.{{.TS}}{{.SUFFIX}}" + git tag $TAG + echo New tag $TAG + + ci: + - git push --tags + + build: + - task: b:build + setup: desc: setup the local development environment (deps, ssh key, env config, poetry) deps: @@ -226,13 +246,15 @@ tasks: cmds: - task: d:defin - # old., to be removed + # deprecated image-tag: desc: "[deprecated] generate and force a new git tag based on the current time" cmds: - - git tag -d $(git tag) - - git tag -f {{.P}}{{.BASETAG}}.$(date +%y%m%d%H%M)-SNAPSHOT - - env PAGER= git tag + - echo "deprecated use, task tag" + #- git tag -d $(git tag) + #- git tag -f {{.P}}{{.BASETAG}}.$(date +%y%m%d%H%M)-SNAPSHOT + #- env PAGER= git tag + # configure env configure-env: @@ -417,15 +439,6 @@ tasks: cmds: - task: b:docker-login - # openserverless-operator section - tag: - silent: true - desc: generate a new tag based on the current time - cmds: - - git tag -d $(git tag) || true - - git tag -f {{.BASETAG}}.$(date +%y%m%d%H%M) - - env PAGER= git tag - install-registry: desc: install a local registry cmds: @@ -441,38 +454,6 @@ tasks: status: - kubectl -n kube-system get po -l app=docker-registry | grep docker-registry - tag-commit-push: - desc: tag, commit and push to your default upstream repo (see README.md) - cmds: - - task: tag - - git commit -m "{{.TAG}}" -a || true - - git push || true - - kaniko-build: - desc: build in current kubernetes with Kaniko - env: - TAG: - sh: git describe --tags --abbrev=0 2>/dev/null || echo latest - cmds: - - test -n "$GITHUB_USER" || true "did you configure your .env?" - - envsubst _kaniko-build.yaml - - kubectl -n default delete job/kaniko-build || true - - kubectl -n default apply -f _kaniko-build.yaml - - kubectl wait po --for=condition=ready -l app=kaniko-build -n default - - build-logs: - desc: show logs of the latest build - cmds: - - kubectl -n default logs -l app=kaniko-build -f - - build: - desc: build the operator image - cmds: - - task: install-registry - - task: tag-commit-push - - task: kaniko-build - - task: build-logs - deploy: desc: deploy the current operator image env: diff --git a/WARN b/WARN new file mode 100644 index 00000000..bd2c0b41 --- /dev/null +++ b/WARN @@ -0,0 +1,120 @@ +============================================================================== +WARN -- Apache License 2.0 incompatibilities +Container image: apache/openserverless-operator +============================================================================== + +This file lists third-party components bundled by the Dockerfile beside +it whose licenses are NOT compatible with the Apache License, Version 2.0, +together with components that require special handling. + +Classification follows the ASF third-party licensing policy: + + https://www.apache.org/legal/resolved.html + + Category A Apache-2.0 compatible. May be included in a release. + Category B May be included in BINARY form only, unmodified and as a + separate file, with the reciprocal license disclosed. + Category X MUST NOT be included in an Apache release artifact. + +IMPORTANT SCOPE NOTE +------------------------------------------------------------------------------ +The Apache release artifact for OpenServerless is the SOURCE distribution, +which contains this Dockerfile and the files beside it -- all Apache-2.0 +licensed. The published container image is a CONVENIENCE BINARY assembled +at build time from third-party base images and package repositories. +Category X components inherited from a base operating system image are +disclosed below but are not redistributed by the ASF in source form. +Category X components that this Dockerfile ADDS on purpose must be removed. + + +============================================================================== +CATEGORY X +============================================================================== + + None. This Dockerfile does not add any Category X component. + + +============================================================================== +CATEGORY X (as bundled) +============================================================================== + + Component : Debian/Ubuntu/Alpine base image userland + License : mixed, incl. GPL-2.0/GPL-3.0 + Category : X (as bundled) + Action : + The base image contains GPL-licensed operating-system components. The Apache release + artifact is the SOURCE (this Dockerfile and the files beside it); the published + container image is a convenience binary assembled from third-party base images. + No action required beyond this disclosure. + + +============================================================================== +CATEGORY B +============================================================================== + + Component : certifi / ca-certificates + License : MPL-2.0 + Category : B + Action : + Category B: MPL-2.0 may be included in binary form only, unmodified and as a + separate file. That is satisfied here. No action required. + + +============================================================================== +CATEGORY A +============================================================================== + + Component : zip / unzip (Info-ZIP) + License : Info-ZIP License + Category : A + Action : + BSD-like and Apache-compatible. No action required. + + Component : curl + License : MIT (curl License) + Category : A + Action : + Apache-compatible. The `gnupg` package previously installed beside it has + been REMOVED from the Dockerfile: nothing in the operator invokes gpg, and + apt verifies stock Debian repository signatures with its own bundled gpgv + and the debian-archive-keyring, so gnupg was never required. No action + required. + + Component : pg8000 + License : BSD-3-Clause + Category : A + Action : + Pure-Python PostgreSQL driver, Apache-compatible. It REPLACES the LGPL + `psycopg` / `psycopg-binary` pair previously listed as Category X, which + are no longer dependencies. Its transitive dependencies `scramp` (MIT-0) + and `asn1crypto` (MIT) are Category A. No action required. + + Component : minio (Python SDK) + License : Apache-2.0 + Category : A + Action : + Apache-compatible. It REPLACES the AGPL-3.0 MinIO Client (`mc`) previously + listed as Category X: the `mc` binary is no longer downloaded or baked into + the image, and MinIO is accessed through this SDK instead. No action + required. + + +============================================================================== +SUMMARY OF REQUIRED REMOVALS (Category X added by this Dockerfile) +============================================================================== + + None outstanding. + + [x] MinIO Client (mc) -- AGPL-3.0 -- REMOVED from the Dockerfile + +============================================================================== +ADDITIONAL NOTES +============================================================================== + +The operator image also embeds Kubernetes manifests under deploy/ that +reference third-party container images (CouchDB, Redis, MongoDB, Kafka, +Zookeeper, MinIO, PostgreSQL, cert-manager, ingress-nginx, Milvus, etcd, +FerretDB, SeaweedFS). Those images are pulled at deployment time by the +end user and are NOT redistributed as part of this image; their licenses +are governed by their respective publishers. + diff --git a/actions/Taskfile.yml b/actions/Taskfile.yml index fe2f2250..70b8cef0 100644 --- a/actions/Taskfile.yml +++ b/actions/Taskfile.yml @@ -22,13 +22,13 @@ vars: sh: ../detect.sh WHISK: '{{default "whisk" .WHISK}}' CONFIG: "../tests/{{.KUBE}}/{{.WHISK}}.yaml" - APIHOST: + APIHOST: sh: kubectl -n openserverless get cm/config -o yaml | awk '/apihost:/ {print $2}' SYSTEM: sh: kubectl -n openserverless get wsk/controller -o jsonpath='{.spec.openwhisk.namespaces.whisk-system}' WSKSYS: wsk --apihost {{.APIHOST}} -u {{.SYSTEM}} DEPLOY: ../deploy/whisk-system - + CDB_USER: sh: kubectl -n openserverless get wsk/controller -o jsonpath='{.spec.couchdb.admin.user}' @@ -36,12 +36,12 @@ vars: sh: kubectl -n openserverless get wsk/controller -o jsonpath='{.spec.couchdb.admin.password}' NUV_MINIO_AUTH: - sh: kubectl -n openserverless get wsk/controller -o jsonpath='{.spec.minio.openserverless.password}' + sh: kubectl -n openserverless get wsk/controller -o jsonpath='{.spec.minio.openserverless.password}' - CDB_HOST: couchdb + CDB_HOST: couchdb - MINIO_HOST: minio.openserverless.svc.cluster.local - MINIO_PORT: 9000 + MINIO_HOST: minio.openserverless.svc.cluster.local + MINIO_PORT: 9000 #GIHTUB_SA_TOKEN: # sh: kubectl -n openserverless get secrets/openserverless-github-secret -o jsonpath='{.data.token}' @@ -50,7 +50,7 @@ vars: # sh: kubectl -n openserverless get secrets/openserverless-github-secret -o jsonpath='{.data.ca\.crt}' #GIHTUB_SA_NS: - # sh: kubectl -n openserverless get secrets/openserverless-github-secret -o jsonpath='{.data.namespace}' + # sh: kubectl -n openserverless get secrets/openserverless-github-secret -o jsonpath='{.data.namespace}' tasks: @@ -68,10 +68,10 @@ tasks: echo: - zip -j {{.DEPLOY}}/echo.zip echo/* - - "{{.WSKSYS}} action update echo {{.DEPLOY}}/echo.zip --kind python:3" + - "{{.WSKSYS}} action update echo {{.DEPLOY}}/echo.zip --kind python:sys" login:prepare: - - |- + - |- mkdir -p login/openserverless cp ../openserverless/config.py ../openserverless/couchdb_util.py ../openserverless/bcrypt_util.py login/openserverless - |- @@ -80,21 +80,21 @@ tasks: zip -r ../{{.DEPLOY}}/login.zip * login:deploy: - - > + - > {{.WSKSYS}} package update nuv -p couchdb_user "{{.CDB_USER}}" -p couchdb_password "{{.CDB_PASSWORD}}" - -p couchdb_host "{{.CDB_HOST}}" - - > - {{.WSKSYS}} action update nuv/login - {{.DEPLOY}}/login.zip --kind python:3 --web true - - > + -p couchdb_host "{{.CDB_HOST}}" + - > + {{.WSKSYS}} action update nuv/login + {{.DEPLOY}}/login.zip --kind python:sys --web true + - > {{.WSKSYS}} action get nuv/login --url content:prepare: - - |- + - |- mkdir -p content/common - cp ./common/minio_util.py content/common + cp ./common/minio_util.py content/common - |- cd content rm -f ../{{.DEPLOY}}/content.zip @@ -102,18 +102,18 @@ tasks: content:deploy: - task: content:prepare - - > + - > {{.WSKSYS}} package update nuv -p minio_host {{.MINIO_HOST}} - -p minio_port {{.MINIO_PORT}} - - > - {{.WSKSYS}} action update nuv/content - {{.DEPLOY}}/content.zip --kind python:3 --web true - - > + -p minio_port {{.MINIO_PORT}} + - > + {{.WSKSYS}} action update nuv/content + {{.DEPLOY}}/content.zip --kind python:sys --web true + - > {{.WSKSYS}} action get nuv/content --url devel:redis:prepare: - - |- + - |- mkdir -p devel/redis/openserverless mkdir -p devel/redis/common cp ./common/*.py devel/redis/common @@ -125,19 +125,19 @@ tasks: devel:redis:deploy: - task: devel:redis:prepare - - > + - > {{.WSKSYS}} package update nuv -p couchdb_user "{{.CDB_USER}}" -p couchdb_password "{{.CDB_PASSWORD}}" -p couchdb_host "{{.CDB_HOST}}" - - > + - > {{.WSKSYS}} action update nuv/redis - {{.DEPLOY}}/redis.zip --kind python:3 --web raw - - > - {{.WSKSYS}} action get nuv/redis --url + {{.DEPLOY}}/redis.zip --kind python:sys --web raw + - > + {{.WSKSYS}} action get nuv/redis --url devel:psql:prepare: - - |- + - |- mkdir -p devel/psql/openserverless mkdir -p devel/psql/common cp ./common/*.py devel/psql/common @@ -149,19 +149,19 @@ tasks: devel:psql:deploy: - task: devel:psql:prepare - - > + - > {{.WSKSYS}} package update nuv -p couchdb_user "{{.CDB_USER}}" -p couchdb_password "{{.CDB_PASSWORD}}" -p couchdb_host "{{.CDB_HOST}}" - - > + - > {{.WSKSYS}} action update nuv/psql - {{.DEPLOY}}/psql.zip --kind python:3 --web raw - - > - {{.WSKSYS}} action get nuv/psql --url + {{.DEPLOY}}/psql.zip --kind python:sys --web raw + - > + {{.WSKSYS}} action get nuv/psql --url devel:minio:prepare: - - |- + - |- mkdir -p devel/minio/openserverless mkdir -p devel/minio/common cp ./common/*.py devel/minio/common @@ -173,19 +173,19 @@ tasks: devel:minio:deploy: - task: devel:minio:prepare - - > + - > {{.WSKSYS}} package update nuv -p couchdb_user "{{.CDB_USER}}" -p couchdb_password "{{.CDB_PASSWORD}}" -p couchdb_host "{{.CDB_HOST}}" - - > + - > {{.WSKSYS}} action update nuv/minio - {{.DEPLOY}}/minio.zip --kind python:3 --web raw - - > - {{.WSKSYS}} action get nuv/minio --url + {{.DEPLOY}}/minio.zip --kind python:sys --web raw + - > + {{.WSKSYS}} action get nuv/minio --url devel:upload:prepare: - - |- + - |- mkdir -p devel/upload/openserverless mkdir -p devel/upload/common cp ./common/*.py devel/upload/common @@ -197,19 +197,19 @@ tasks: devel:upload:deploy: - task: devel:upload:prepare - - > + - > {{.WSKSYS}} package update nuv -p couchdb_user "{{.CDB_USER}}" -p couchdb_password "{{.CDB_PASSWORD}}" -p couchdb_host "{{.CDB_HOST}}" - - > + - > {{.WSKSYS}} action update nuv/devel_upload - {{.DEPLOY}}/devel_upload.zip --kind python:3 --web raw - - > - {{.WSKSYS}} action get nuv/devel_upload --url + {{.DEPLOY}}/devel_upload.zip --kind python:sys --web raw + - > + {{.WSKSYS}} action get nuv/devel_upload --url devel:ferretdb:prepare: - - |- + - |- mkdir -p devel/ferretdb/openserverless mkdir -p devel/ferretdb/common cp ./common/*.py devel/ferretdb/common @@ -221,19 +221,19 @@ tasks: devel:ferretdb:deploy: - task: devel:ferretdb:prepare - - > + - > {{.WSKSYS}} package update nuv -p couchdb_user "{{.CDB_USER}}" -p couchdb_password "{{.CDB_PASSWORD}}" -p couchdb_host "{{.CDB_HOST}}" - - > + - > {{.WSKSYS}} action update nuv/ferretdb - {{.DEPLOY}}/ferretdb.zip --kind python:3 --web raw - - > + {{.DEPLOY}}/ferretdb.zip --kind python:sys --web raw + - > {{.WSKSYS}} action get nuv/ferretdb --url devel:download:prepare: - - |- + - |- mkdir -p devel/download/openserverless mkdir -p devel/download/common cp ./common/*.py devel/download/common @@ -245,16 +245,16 @@ tasks: devel:download:deploy: - task: devel:download:prepare - - > + - > {{.WSKSYS}} package update nuv -p couchdb_user "{{.CDB_USER}}" -p couchdb_password "{{.CDB_PASSWORD}}" -p couchdb_host "{{.CDB_HOST}}" - - > + - > {{.WSKSYS}} action update nuv/devel_download - {{.DEPLOY}}/devel_download.zip --kind python:3 --web raw - - > - {{.WSKSYS}} action get nuv/devel_download --url + {{.DEPLOY}}/devel_download.zip --kind python:sys --web raw + - > + {{.WSKSYS}} action get nuv/devel_download --url login:webtest: - > @@ -282,7 +282,7 @@ tasks: content-delete:yaml: - > - curl -X DELETE -H "minioauth: {{.NUV_MINIO_AUTH}}" {{.APIHOST}}/api/v1/web/whisk-system/nuv/content/openserverless/yaml/couchdb-init.yaml + curl -X DELETE -H "minioauth: {{.NUV_MINIO_AUTH}}" {{.APIHOST}}/api/v1/web/whisk-system/nuv/content/openserverless/yaml/couchdb-init.yaml content:chess: - > @@ -294,11 +294,11 @@ tasks: devel:redis:get: - > - curl {{.APIHOST}}/api/v1/web/whisk-system/nuv/redis -H "x-impersonate-auth: Y2JkNjgwNzUtZGFjMi00NzVlLThjMDctZDYyYTMwYzdlNjgzOjEyM3pPM3haQ0xyTU42djJCS0sxZFhZRnBYbFBrY2NPRnFtMTJDZEFzTWdSVTRWck5aOWx5R1ZDR3VNREdJd1A=" -H "Content-Type: application/json" -d@../demos/devel/redis_get.json + curl {{.APIHOST}}/api/v1/web/whisk-system/nuv/redis -H "x-impersonate-auth: Y2JkNjgwNzUtZGFjMi00NzVlLThjMDctZDYyYTMwYzdlNjgzOjEyM3pPM3haQ0xyTU42djJCS0sxZFhZRnBYbFBrY2NPRnFtMTJDZEFzTWdSVTRWck5aOWx5R1ZDR3VNREdJd1A=" -H "Content-Type: application/json" -d@../demos/devel/redis_get.json devel:psql:select: - > - curl {{.APIHOST}}/api/v1/web/whisk-system/nuv/psql -H "x-impersonate-auth: Y2JkNjgwNzUtZGFjMi00NzVlLThjMDctZDYyYTMwYzdlNjgzOjEyM3pPM3haQ0xyTU42djJCS0sxZFhZRnBYbFBrY2NPRnFtMTJDZEFzTWdSVTRWck5aOWx5R1ZDR3VNREdJd1A=" -H "Content-Type: application/json" -d@../demos/devel/psql_select.json + curl {{.APIHOST}}/api/v1/web/whisk-system/nuv/psql -H "x-impersonate-auth: Y2JkNjgwNzUtZGFjMi00NzVlLThjMDctZDYyYTMwYzdlNjgzOjEyM3pPM3haQ0xyTU42djJCS0sxZFhZRnBYbFBrY2NPRnFtMTJDZEFzTWdSVTRWck5aOWx5R1ZDR3VNREdJd1A=" -H "Content-Type: application/json" -d@../demos/devel/psql_select.json devel:psql:desc: - > @@ -306,31 +306,31 @@ tasks: devel:psql:create: - > - curl {{.APIHOST}}/api/v1/web/whisk-system/nuv/psql -H "x-impersonate-auth: Y2JkNjgwNzUtZGFjMi00NzVlLThjMDctZDYyYTMwYzdlNjgzOjEyM3pPM3haQ0xyTU42djJCS0sxZFhZRnBYbFBrY2NPRnFtMTJDZEFzTWdSVTRWck5aOWx5R1ZDR3VNREdJd1A=" -H "Content-Type: application/json" -d@../demos/devel/psql_create1.json + curl {{.APIHOST}}/api/v1/web/whisk-system/nuv/psql -H "x-impersonate-auth: Y2JkNjgwNzUtZGFjMi00NzVlLThjMDctZDYyYTMwYzdlNjgzOjEyM3pPM3haQ0xyTU42djJCS0sxZFhZRnBYbFBrY2NPRnFtMTJDZEFzTWdSVTRWck5aOWx5R1ZDR3VNREdJd1A=" -H "Content-Type: application/json" -d@../demos/devel/psql_create1.json - > - curl {{.APIHOST}}/api/v1/web/whisk-system/nuv/psql -H "x-impersonate-auth: Y2JkNjgwNzUtZGFjMi00NzVlLThjMDctZDYyYTMwYzdlNjgzOjEyM3pPM3haQ0xyTU42djJCS0sxZFhZRnBYbFBrY2NPRnFtMTJDZEFzTWdSVTRWck5aOWx5R1ZDR3VNREdJd1A=" -H "Content-Type: application/json" -d@../demos/devel/psql_create2.json + curl {{.APIHOST}}/api/v1/web/whisk-system/nuv/psql -H "x-impersonate-auth: Y2JkNjgwNzUtZGFjMi00NzVlLThjMDctZDYyYTMwYzdlNjgzOjEyM3pPM3haQ0xyTU42djJCS0sxZFhZRnBYbFBrY2NPRnFtMTJDZEFzTWdSVTRWck5aOWx5R1ZDR3VNREdJd1A=" -H "Content-Type: application/json" -d@../demos/devel/psql_create2.json - > - curl {{.APIHOST}}/api/v1/web/whisk-system/nuv/psql -H "x-impersonate-auth: Y2JkNjgwNzUtZGFjMi00NzVlLThjMDctZDYyYTMwYzdlNjgzOjEyM3pPM3haQ0xyTU42djJCS0sxZFhZRnBYbFBrY2NPRnFtMTJDZEFzTWdSVTRWck5aOWx5R1ZDR3VNREdJd1A=" -H "Content-Type: application/json" -d@../demos/devel/psql_create3.json + curl {{.APIHOST}}/api/v1/web/whisk-system/nuv/psql -H "x-impersonate-auth: Y2JkNjgwNzUtZGFjMi00NzVlLThjMDctZDYyYTMwYzdlNjgzOjEyM3pPM3haQ0xyTU42djJCS0sxZFhZRnBYbFBrY2NPRnFtMTJDZEFzTWdSVTRWck5aOWx5R1ZDR3VNREdJd1A=" -H "Content-Type: application/json" -d@../demos/devel/psql_create3.json devel:minio:ls: - > - curl {{.APIHOST}}/api/v1/web/whisk-system/nuv/minio -H "x-impersonate-auth: Y2JkNjgwNzUtZGFjMi00NzVlLThjMDctZDYyYTMwYzdlNjgzOjEyM3pPM3haQ0xyTU42djJCS0sxZFhZRnBYbFBrY2NPRnFtMTJDZEFzTWdSVTRWck5aOWx5R1ZDR3VNREdJd1A=" -H "Content-Type: application/json" -d@../demos/devel/minio_buckets.json + curl {{.APIHOST}}/api/v1/web/whisk-system/nuv/minio -H "x-impersonate-auth: Y2JkNjgwNzUtZGFjMi00NzVlLThjMDctZDYyYTMwYzdlNjgzOjEyM3pPM3haQ0xyTU42djJCS0sxZFhZRnBYbFBrY2NPRnFtMTJDZEFzTWdSVTRWck5aOWx5R1ZDR3VNREdJd1A=" -H "Content-Type: application/json" -d@../demos/devel/minio_buckets.json devel:minio:lsb: - > - curl {{.APIHOST}}/api/v1/web/whisk-system/nuv/minio -H "x-impersonate-auth: Y2JkNjgwNzUtZGFjMi00NzVlLThjMDctZDYyYTMwYzdlNjgzOjEyM3pPM3haQ0xyTU42djJCS0sxZFhZRnBYbFBrY2NPRnFtMTJDZEFzTWdSVTRWck5aOWx5R1ZDR3VNREdJd1A=" -H "Content-Type: application/json" -d@../demos/devel/minio_ls.json + curl {{.APIHOST}}/api/v1/web/whisk-system/nuv/minio -H "x-impersonate-auth: Y2JkNjgwNzUtZGFjMi00NzVlLThjMDctZDYyYTMwYzdlNjgzOjEyM3pPM3haQ0xyTU42djJCS0sxZFhZRnBYbFBrY2NPRnFtMTJDZEFzTWdSVTRWck5aOWx5R1ZDR3VNREdJd1A=" -H "Content-Type: application/json" -d@../demos/devel/minio_ls.json devel:minio:rm: - > - curl {{.APIHOST}}/api/v1/web/whisk-system/nuv/minio -H "x-impersonate-auth: Y2JkNjgwNzUtZGFjMi00NzVlLThjMDctZDYyYTMwYzdlNjgzOjEyM3pPM3haQ0xyTU42djJCS0sxZFhZRnBYbFBrY2NPRnFtMTJDZEFzTWdSVTRWck5aOWx5R1ZDR3VNREdJd1A=" -H "Content-Type: application/json" -d@../demos/devel/minio_rm.json + curl {{.APIHOST}}/api/v1/web/whisk-system/nuv/minio -H "x-impersonate-auth: Y2JkNjgwNzUtZGFjMi00NzVlLThjMDctZDYyYTMwYzdlNjgzOjEyM3pPM3haQ0xyTU42djJCS0sxZFhZRnBYbFBrY2NPRnFtMTJDZEFzTWdSVTRWck5aOWx5R1ZDR3VNREdJd1A=" -H "Content-Type: application/json" -d@../demos/devel/minio_rm.json devel:minio:mv: - > - curl {{.APIHOST}}/api/v1/web/whisk-system/nuv/minio -H "x-impersonate-auth: Y2JkNjgwNzUtZGFjMi00NzVlLThjMDctZDYyYTMwYzdlNjgzOjEyM3pPM3haQ0xyTU42djJCS0sxZFhZRnBYbFBrY2NPRnFtMTJDZEFzTWdSVTRWck5aOWx5R1ZDR3VNREdJd1A=" -H "Content-Type: application/json" -d@../demos/devel/minio_mv.json + curl {{.APIHOST}}/api/v1/web/whisk-system/nuv/minio -H "x-impersonate-auth: Y2JkNjgwNzUtZGFjMi00NzVlLThjMDctZDYyYTMwYzdlNjgzOjEyM3pPM3haQ0xyTU42djJCS0sxZFhZRnBYbFBrY2NPRnFtMTJDZEFzTWdSVTRWck5aOWx5R1ZDR3VNREdJd1A=" -H "Content-Type: application/json" -d@../demos/devel/minio_mv.json devel:minio:cp: - > - curl {{.APIHOST}}/api/v1/web/whisk-system/nuv/minio -H "x-impersonate-auth: Y2JkNjgwNzUtZGFjMi00NzVlLThjMDctZDYyYTMwYzdlNjgzOjEyM3pPM3haQ0xyTU42djJCS0sxZFhZRnBYbFBrY2NPRnFtMTJDZEFzTWdSVTRWck5aOWx5R1ZDR3VNREdJd1A=" -H "Content-Type: application/json" -d@../demos/devel/minio_cp.json + curl {{.APIHOST}}/api/v1/web/whisk-system/nuv/minio -H "x-impersonate-auth: Y2JkNjgwNzUtZGFjMi00NzVlLThjMDctZDYyYTMwYzdlNjgzOjEyM3pPM3haQ0xyTU42djJCS0sxZFhZRnBYbFBrY2NPRnFtMTJDZEFzTWdSVTRWck5aOWx5R1ZDR3VNREdJd1A=" -H "Content-Type: application/json" -d@../demos/devel/minio_cp.json devel:upload:webtest: - > @@ -357,7 +357,7 @@ tasks: content:all: - task: content:prepare - - task: content:deploy + - task: content:deploy # prepares all the system related actions prepare: @@ -370,7 +370,7 @@ tasks: all: - task: prepare - - task: deploy + - task: deploy deploy:devel: - task: devel:redis:deploy @@ -388,7 +388,7 @@ tasks: - echo "{{.GIHTUB_SA_CRT}}" github:package:deploy: - - > + - > {{.WSKSYS}} package update github -p couchdb_user "{{.CDB_USER}}" -p couchdb_passwo986149f29564be9d0543a11d6657bca63deb7ff5rd "{{.CDB_PASSWORD}}" @@ -404,7 +404,7 @@ tasks: github:callback:prepare: - - |- + - |- mkdir -p github/callback/openserverless mkdir -p github/callback/common cp github/common/*.py github/callback/common @@ -416,14 +416,14 @@ tasks: github:callback:deploy: - task: github:callback:prepare - - > + - > {{.WSKSYS}} action update github/callback - {{.DEPLOY}}/callback.zip --kind python:3 --web true - - > - {{.WSKSYS}} action get github/callback --url + {{.DEPLOY}}/callback.zip --kind python:sys --web true + - > + {{.WSKSYS}} action get github/callback --url github:webhook:prepare: - - |- + - |- mkdir -p github/webhook/openserverless mkdir -p github/webhook/common cp github/common/*.py github/webhook/common @@ -435,14 +435,14 @@ tasks: github:webhook:deploy: - task: github:webhook:prepare - - > + - > {{.WSKSYS}} action update github/webhook - {{.DEPLOY}}/webhook.zip --kind python:3 --web raw - - > + {{.DEPLOY}}/webhook.zip --kind python:sys --web raw + - > {{.WSKSYS}} action get github/webhook --url deploy:github: - task: github:package:deploy - task: github:callback:deploy - - task: github:webhook:deploy - + - task: github:webhook:deploy + diff --git a/actions/devel/psql/command/psql.py b/actions/devel/psql/command/psql.py index 8ccd4816..71ea0c46 100644 --- a/actions/devel/psql/command/psql.py +++ b/actions/devel/psql/command/psql.py @@ -16,11 +16,11 @@ # under the License. # -import psycopg +import pg8000.dbapi import common.util as ut from common.command_data import CommandData -from psycopg.rows import dict_row +from urllib.parse import urlparse, unquote import json class Psql(): @@ -43,30 +43,57 @@ def validate(self): raise Exception("user does not have valid POSTGRES environment set") + def _connect(self): + """ + Opens a pg8000 connection from the configured postgres URL. pg8000 takes + connection arguments as keywords, so the URL is parsed here. + """ + url = urlparse(self._postgres_url) + return pg8000.dbapi.connect( + host=url.hostname, + port=url.port or 5432, + user=unquote(url.username) if url.username else None, + password=unquote(url.password) if url.password else None, + database=url.path.lstrip("/") or None, + ) + def _query(self, input:CommandData): """ Queries for matching query records and returns datas in a key, value format. """ query = input.command() - with psycopg.connect(self._postgres_url) as conn: + conn = self._connect() + try: # Open a cursor to perform database operations - with conn.cursor(row_factory=dict_row) as cur: + cur = conn.cursor() + try: cur.execute(query) - result = cur.fetchall() - input.result(json.dumps(result)) + columns = [desc[0] for desc in cur.description] + result = [dict(zip(columns, row)) for row in cur.fetchall()] + input.result(json.dumps(result, default=str)) input.status(200) return input + finally: + cur.close() + finally: + conn.close() def _script(self, input:CommandData): script = input.command() - with psycopg.connect(self._postgres_url) as conn: + conn = self._connect() + try: # Open a cursor to perform database operations - with conn.cursor() as cur: + cur = conn.cursor() + try: cur.execute(script) conn.commit() - input.result(cur.statusmessage) + input.result(f"{cur.rowcount} row(s) affected") input.status(200) - return input + return input + finally: + cur.close() + finally: + conn.close() def _is_a_query(self, input:CommandData): return 'select' in input.command().lower() diff --git a/deploy/openserverless-permissions/whisk-crd.yaml b/deploy/openserverless-permissions/whisk-crd.yaml index 6e9b7f0e..17af9b44 100644 --- a/deploy/openserverless-permissions/whisk-crd.yaml +++ b/deploy/openserverless-permissions/whisk-crd.yaml @@ -86,15 +86,6 @@ spec: slim: description: boolean flag to enable/disable openserverless slim mode. Defaulted to false type: boolean - preload: - description: used to preload some default actions in the openserverless namespace. Defaulted to false - type: object - properties: - only-apache: - description: boolean flag to preload only the apache openserverless actions - type: boolean - required: - - only-apache required: - password components: diff --git a/kaniko-build.yaml b/kaniko-build.yaml deleted file mode 100644 index 2e7dda6f..00000000 --- a/kaniko-build.yaml +++ /dev/null @@ -1,39 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# -apiVersion: batch/v1 -kind: Job -metadata: - name: kaniko-build - namespace: default -spec: - template: - metadata: - labels: - app: kaniko-build - spec: - containers: - - name: kaniko - image: gcr.io/kaniko-project/executor:latest - args: - - --context=git://github.com/${GITHUB_USER}/openserverless-operator - - --destination=docker-registry.kube-system.svc.cluster.local:5000/${GITHUB_USER}/openserverless-operator:${TAG} - - --insecure - - --insecure-registry=docker-registry - - --build-arg=OPERATOR_IMAGE_DEFAULT=ghcr.io/${GITHUB_USER}/openserverless-operator - - --build-arg=OPERATOR_TAG_DEFAULT=${TAG} - restartPolicy: Never diff --git a/openserverless/postgres_client.py b/openserverless/postgres_client.py index a9e07920..ec71d3b3 100644 --- a/openserverless/postgres_client.py +++ b/openserverless/postgres_client.py @@ -16,7 +16,7 @@ # under the License. # import logging, json, os -import psycopg +import pg8000.dbapi class PostgresClient: @@ -26,19 +26,38 @@ def __init__(self, hostname, port, admin_username, password): self.username = admin_username self.password = password - def get_connection(self): + def get_connection(self, db_name=None): """ - Builds a PGDB Connection. + Builds a PGDB Connection, optionally to the specified database name. return: predefined connection for this PostgresDB client """ - return psycopg.connect(f"host={self.hostname} port={self.port} user={self.username} password={self.password}") + kwargs = { + "host": self.hostname, + "port": int(self.port), + "user": self.username, + "password": self.password, + } + if db_name: + kwargs["database"] = db_name + return pg8000.dbapi.connect(**kwargs) def get_connection_to_db(self, db_name): """ Builds a PGDB Connection to the specified database name return: predefined connection for this PostgresDB client """ - return psycopg.connect(f"host={self.hostname} port={self.port} user={self.username} password={self.password} dbname={db_name}") + return self.get_connection(db_name) + + def _list_user_schemas(self, conn): + """ + Returns the list of non system schema names visible on the given connection. + """ + cur = conn.cursor() + try: + cur.execute("SELECT nspname FROM pg_catalog.pg_namespace where nspname not in ('information_schema','pg_catalog','pg_toast');") + return [record[0] for record in cur.fetchall()] + finally: + cur.close() def query_all_pg_database_size(self): """ @@ -46,15 +65,21 @@ def query_all_pg_database_size(self): return: a dictionary mapping the PG database name to the used space in bytes """ pg_dbsize_list = {} - with self.get_connection() as conn: - logging.info("Querying all Postgres database used space") + conn = self.get_connection() + try: + logging.info("Querying all Postgres database used space") # Open a cursor to perform database operations - with conn.cursor() as cur: + cur = conn.cursor() + try: cur.execute("SELECT datname as db_name, pg_database_size(datname) as db_usage FROM pg_database;") - for record in cur: + for record in cur.fetchall(): logging.info(record) pg_dbsize_list[record[0]]=record[1] + finally: + cur.close() + finally: + conn.close() return pg_dbsize_list @@ -65,44 +90,49 @@ def revoke_access_from_db(self, pg_username, pg_db_name): param: pg_username param: pg_db_name database to revoke privileges from """ - try: - with self.get_connection_to_db(pg_db_name) as conn: - logging.info(f"Querying database {pg_db_name} existing schemas") - - pg_user_schema_names = [] - with conn.cursor() as curs: - curs.execute("SELECT nspname FROM pg_catalog.pg_namespace where nspname not in ('information_schema','pg_catalog','pg_toast');") - for record in curs: - pg_user_schema_names.append(record[0]) - - with conn.cursor() as cur: - logging.info(f"executing REVOKE ALL PRIVILEGES ON DATABASE {pg_db_name} FROM {pg_username} and setting RO access") - - cur.execute(f"REVOKE ALL PRIVILEGES ON DATABASE {pg_db_name} FROM {pg_username};") - - for schema in pg_user_schema_names: - logging.info(f"processing revocation on schema {schema}") - - cur.execute(f""" - REVOKE ALL ON ALL TABLES IN SCHEMA {schema} FROM {pg_username}; - REVOKE ALL ON ALL SEQUENCES IN SCHEMA {schema} FROM {pg_username}; - REVOKE ALL ON SCHEMA {schema} FROM {pg_username}; - REVOKE ALL ON SCHEMA {schema} FROM public; - GRANT USAGE ON SCHEMA {schema} TO {pg_username}; - GRANT SELECT ON ALL TABLES IN SCHEMA {schema} TO {pg_username}; - GRANT SELECT ON ALL SEQUENCES IN SCHEMA {schema} TO {pg_username}; - ALTER DEFAULT PRIVILEGES IN SCHEMA {schema} GRANT SELECT ON TABLES TO {pg_username}; - ALTER DEFAULT PRIVILEGES IN SCHEMA {schema} GRANT SELECT ON SEQUENCES TO {pg_username}; - """) - - cur.execute(f"GRANT CONNECT ON DATABASE {pg_db_name} TO {pg_username};") - - conn.commit() - logging.info(f"user {pg_username} updated with RO access on {pg_db_name}") - return True + conn = None + try: + conn = self.get_connection_to_db(pg_db_name) + logging.info(f"Querying database {pg_db_name} existing schemas") + + pg_user_schema_names = self._list_user_schemas(conn) + + cur = conn.cursor() + try: + logging.info(f"executing REVOKE ALL PRIVILEGES ON DATABASE {pg_db_name} FROM {pg_username} and setting RO access") + + cur.execute(f"REVOKE ALL PRIVILEGES ON DATABASE {pg_db_name} FROM {pg_username};") + + for schema in pg_user_schema_names: + logging.info(f"processing revocation on schema {schema}") + + # pg8000 uses the extended query protocol: one statement per execute() + for statement in [ + f"REVOKE ALL ON ALL TABLES IN SCHEMA {schema} FROM {pg_username};", + f"REVOKE ALL ON ALL SEQUENCES IN SCHEMA {schema} FROM {pg_username};", + f"REVOKE ALL ON SCHEMA {schema} FROM {pg_username};", + f"REVOKE ALL ON SCHEMA {schema} FROM public;", + f"GRANT USAGE ON SCHEMA {schema} TO {pg_username};", + f"GRANT SELECT ON ALL TABLES IN SCHEMA {schema} TO {pg_username};", + f"GRANT SELECT ON ALL SEQUENCES IN SCHEMA {schema} TO {pg_username};", + f"ALTER DEFAULT PRIVILEGES IN SCHEMA {schema} GRANT SELECT ON TABLES TO {pg_username};", + f"ALTER DEFAULT PRIVILEGES IN SCHEMA {schema} GRANT SELECT ON SEQUENCES TO {pg_username};", + ]: + cur.execute(statement) + + cur.execute(f"GRANT CONNECT ON DATABASE {pg_db_name} TO {pg_username};") + + conn.commit() + logging.info(f"user {pg_username} updated with RO access on {pg_db_name}") + return True + finally: + cur.close() except Exception as ex: logging.error(f"failed to set postgres user {pg_username} in R/O mode. Reason: {ex}") return False + finally: + if conn: + conn.close() def grant_access_on_db(self, pg_username, pg_db_name): """ @@ -111,35 +141,40 @@ def grant_access_on_db(self, pg_username, pg_db_name): param: pg_username param: pg_db_name database to grant privileges from """ - try: - with self.get_connection_to_db(pg_db_name) as conn: - logging.info(f"Querying database {pg_db_name} existing schemas") - - pg_user_schema_names = [] - with conn.cursor() as curs: - curs.execute("SELECT nspname FROM pg_catalog.pg_namespace where nspname not in ('information_schema','pg_catalog','pg_toast');") - for record in curs: - pg_user_schema_names.append(record[0]) - - with conn.cursor() as cur: - logging.info(f"executing GRANT ALL PRIVILEGES ON DATABASE {pg_db_name} FROM {pg_username} and setting RO access") - - cur.execute(f"GRANT ALL PRIVILEGES ON DATABASE {pg_db_name} TO {pg_username};") - - for schema in pg_user_schema_names: - logging.info(f"processing grant on schema {schema}") - - cur.execute(f""" - GRANT ALL ON ALL TABLES IN SCHEMA {schema} TO {pg_username}; - GRANT ALL ON ALL SEQUENCES IN SCHEMA {schema} TO {pg_username}; - GRANT ALL ON SCHEMA {schema} TO {pg_username}; - ALTER DEFAULT PRIVILEGES IN SCHEMA {schema} GRANT ALL ON TABLES TO {pg_username}; - ALTER DEFAULT PRIVILEGES IN SCHEMA {schema} GRANT ALL ON SEQUENCES TO {pg_username}; - """) - - conn.commit() - logging.info(f"user {pg_username} updated with FULL access on {pg_db_name}") - return True + conn = None + try: + conn = self.get_connection_to_db(pg_db_name) + logging.info(f"Querying database {pg_db_name} existing schemas") + + pg_user_schema_names = self._list_user_schemas(conn) + + cur = conn.cursor() + try: + logging.info(f"executing GRANT ALL PRIVILEGES ON DATABASE {pg_db_name} FROM {pg_username} and setting RO access") + + cur.execute(f"GRANT ALL PRIVILEGES ON DATABASE {pg_db_name} TO {pg_username};") + + for schema in pg_user_schema_names: + logging.info(f"processing grant on schema {schema}") + + # pg8000 uses the extended query protocol: one statement per execute() + for statement in [ + f"GRANT ALL ON ALL TABLES IN SCHEMA {schema} TO {pg_username};", + f"GRANT ALL ON ALL SEQUENCES IN SCHEMA {schema} TO {pg_username};", + f"GRANT ALL ON SCHEMA {schema} TO {pg_username};", + f"ALTER DEFAULT PRIVILEGES IN SCHEMA {schema} GRANT ALL ON TABLES TO {pg_username};", + f"ALTER DEFAULT PRIVILEGES IN SCHEMA {schema} GRANT ALL ON SEQUENCES TO {pg_username};", + ]: + cur.execute(statement) + + conn.commit() + logging.info(f"user {pg_username} updated with FULL access on {pg_db_name}") + return True + finally: + cur.close() except Exception as ex: logging.error(f"failed to set FULL ACCESS to postgres user {pg_username}. Reason: {ex}") - return False \ No newline at end of file + return False + finally: + if conn: + conn.close() \ No newline at end of file diff --git a/openserverless/runtimes_preloader.py b/openserverless/runtimes_preloader.py index 84ef7384..741e8c0e 100644 --- a/openserverless/runtimes_preloader.py +++ b/openserverless/runtimes_preloader.py @@ -25,14 +25,8 @@ def create(owner=None): logging.info(f"*** configuring runtime preloader") - config = cfg.getall() - only_apache = config.get( - "openserverless.preload.only-apache", - config.get("openserverless.preloader.only_apache", True) - ) - runtimes_as_json = util.get_runtimes_json_from_config_map() - data=rutil.parse_runtimes(json.loads(runtimes_as_json), only_apache) + data=rutil.parse_runtimes(json.loads(runtimes_as_json)) if not data['containers']: logging.info("*** skipped runtime preloader: no valid runtime images found") return "skipped runtime preloader" diff --git a/openserverless/runtimes_util.py b/openserverless/runtimes_util.py index e0dab160..eea9a306 100644 --- a/openserverless/runtimes_util.py +++ b/openserverless/runtimes_util.py @@ -21,7 +21,32 @@ import openserverless.config as cfg import openserverless.util as util -def find_default_container(containers: list, container_name, runtime_list, only_apache=True): +# Kinds whose version is this suffix are always preloaded, whether or not they +# are the default for their family. The system actions deployed by +# whisk_actions_deployer run on python:sys, so such images must be on the node +# before the first invocation. +ALWAYS_PRELOADED_SUFFIX = "sys" + + +def add_container(containers: list, container_name, runtime): + """ Append a preloader container entry for a runtime, if it has a usable image. + Images are preloaded whatever registry or organization they come from. + :param containers, the global containers array + :param container_name, the name that will be assigned for the containers preloader + :param runtime, the single runtime entry to add + """ + img = runtime['image'] + if not img.get('tag'): + logging.warning(f"skipping runtime preloader for {container_name}: missing image tag") + return + container = { + "name": container_name, + "image": f"{img['prefix']}/{img['name']}:{img['tag']}" + } + containers.append(container) + + +def find_default_container(containers: list, container_name, runtime_list): """ Scans for the inner runtime list and add an entry into the containers for the default one if any :param containers, the global containers array :param container_name, the name that will be assigned for the containers preloader @@ -29,32 +54,43 @@ def find_default_container(containers: list, container_name, runtime_list, only_ """ for runtime in runtime_list: if runtime['default']: - img = runtime['image'] - is_apache = img['prefix'] == 'apache' - if not only_apache or is_apache: - if not img.get('tag'): - logging.warning(f"skipping runtime preloader for {container_name}: missing image tag") - continue - container = { - "name": container_name, - "image": f"{img['prefix']}/{img['name']}:{img['tag']}" - } - containers.append(container) - -def parse_runtimes(runtimes_as_json, only_apache=True): + add_container(containers, container_name, runtime) + + +def find_always_preloaded_containers(containers: list, container_name, runtime_list): + """ Adds every `:sys` kind, which is preloaded even when it is not the + default of its family. A kind already added as the default is not added twice. + :param containers, the global containers array + :param container_name, the family name, used to build the container name + :param runtime_list the runtime list where to look for the kinds + """ + for runtime in runtime_list: + kind = runtime.get('kind') or '' + if not kind.endswith(f":{ALWAYS_PRELOADED_SUFFIX}") or runtime.get('default'): + continue + name = kind.replace(':', '-').replace('.', '-') + if any(c['name'] == name for c in containers): + continue + add_container(containers, name, runtime) + +def parse_runtimes(runtimes_as_json): """ parse an openwhisk runtimes json and returns a stuitable data structure to customize the preloader jon + + Every default kind is preloaded, plus every `:sys` kind, whatever + registry or organization the image comes from. :param runtimes_as_json a runtime json typically extracted from a config map >>> import openserverless.testutil as tutil >>> runtimes_as_json = tutil.load_sample_runtimes() >>> data = parse_runtimes(runtimes_as_json) - >>> len(data['containers']) == 5 + >>> len(data['containers']) == 8 True """ data = {} containers = list() for name in runtimes_as_json["runtimes"]: - find_default_container(containers, name, runtimes_as_json["runtimes"][name], only_apache) + find_default_container(containers, name, runtimes_as_json["runtimes"][name]) + find_always_preloaded_containers(containers, name, runtimes_as_json["runtimes"][name]) data['containers']=containers return data diff --git a/openserverless/seaweedfs_util.py b/openserverless/seaweedfs_util.py index c532b554..933b2f32 100644 --- a/openserverless/seaweedfs_util.py +++ b/openserverless/seaweedfs_util.py @@ -86,12 +86,13 @@ def _multipart_request(self, endpoint: str, files=None, method: str = "POST"): raise SeaweedfsUnauthorizedException() def _exec_weed_command(self,command): - # the command is wrapped in a single quoted shell string, so any quote or - # shell metacharacter reaching this point would break out of it. - if not isinstance(command, str) or re.search(r"[\'\"`$;&|<>\n\r\\]", command): + # the command is piped to weed shell via stdin, so it is never interpreted + # by a shell and no quoting or character filtering is needed. Only newlines + # are rejected, since they would be read as separate weed shell commands. + if not isinstance(command, str) or re.search(r"[\n\r]", command): raise ValueError("invalid characters in weed shell command") logging.debug(f"executing command: {command} inside pod {self.pod_name}") - res = kube.kubectl("exec","-it",self.pod_name,"--","/bin/sh","-c",f"echo '{command}' | weed shell") + res = kube.kubectl("exec","-i",self.pod_name,"--","weed","shell",input=f"{command}\n") return res def make_bucket(self, bucket_name, quota_in_mb=None): diff --git a/openserverless/util.py b/openserverless/util.py index df70d493..0560f06f 100644 --- a/openserverless/util.py +++ b/openserverless/util.py @@ -459,8 +459,11 @@ def validate_ow_auth(auth): >>> auth = tutil.generate_ow_auth() >>> util.validate_ow_auth(auth) True + >>> # the errors logged below are expected: the inputs are invalid on purpose + >>> logging.disable(logging.ERROR) >>> util.validate_ow_auth('21321:3213216') False + >>> logging.disable(logging.NOTSET) """ try: parts = auth.split(':') diff --git a/openserverless/whisk_actions_deployer.py b/openserverless/whisk_actions_deployer.py index 63d45194..e7b2dba8 100644 --- a/openserverless/whisk_actions_deployer.py +++ b/openserverless/whisk_actions_deployer.py @@ -46,7 +46,7 @@ def prepare_login_action(): login = { "name":"login", "function":"login.zip", - "runtime":"python:3", + "runtime":"python:sys", "web":"true", "inputs":get_couchdb_inputs() } @@ -57,7 +57,7 @@ def prepare_secrets_action(): secrets = { "name":"secrets", "function":"secrets.zip", - "runtime":"python:3", + "runtime":"python:sys", "web":"true", "inputs":get_couchdb_inputs() } @@ -71,84 +71,84 @@ def prepare_content_action(): content_inputs=[] content_inputs.append({"key":"minio_host", "value":minio_full_host}) - content_inputs.append({"key":"minio_port", "value":minio_port}) + content_inputs.append({"key":"minio_port", "value":minio_port}) content = { "name":"content", "function":"content.zip", - "runtime":"python:3", + "runtime":"python:sys", "web":"true", "inputs":content_inputs } - return content + return content def prepare_redis_action(): redis = { "name":"redis", "function":"redis.zip", - "runtime":"python:3", + "runtime":"python:sys", "web":"raw", "inputs":get_couchdb_inputs() } - return redis + return redis def prepare_psql_action(): psql = { "name":"psql", "function":"psql.zip", - "runtime":"python:3", + "runtime":"python:sys", "web":"raw", "inputs":get_couchdb_inputs() } - return psql + return psql def prepare_minio_action(): minio = { "name":"minio", "function":"minio.zip", - "runtime":"python:3", + "runtime":"python:sys", "web":"raw", "inputs":get_couchdb_inputs() } - return minio + return minio def prepare_dev_upload_action(): dev_upload = { "name":"devel_upload", "function":"devel_upload.zip", - "runtime":"python:3", + "runtime":"python:sys", "web":"raw", "inputs":get_couchdb_inputs() } - return dev_upload + return dev_upload def prepare_ferretdb_action(): ferretdb = { "name":"ferretdb", "function":"ferretdb.zip", - "runtime":"python:3", + "runtime":"python:sys", "web":"raw", "inputs":get_couchdb_inputs() } - return ferretdb + return ferretdb def prepare_dev_download_action(): dev_download = { "name":"devel_download", "function":"devel_download.zip", - "runtime":"python:3", + "runtime":"python:sys", "web":"raw", "inputs":get_couchdb_inputs() } - return dev_download + return dev_download def prepare_system_actions(): @@ -200,7 +200,7 @@ def safe_deploy(wskClient): deployProjectResponse = wskClient.wsk("project","deploy","--project","deploy/whisk-system") process_wsk_result(deployProjectResponse, "Success") - actionListResult = wskClient.wsk("action","list") + actionListResult = wskClient.wsk("action","list") process_wsk_result(actionListResult, "whisk-system/nuv/login") return True diff --git a/poetry.lock b/poetry.lock index 842ac829..182eec4e 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,129 +1,174 @@ -# This file is automatically @generated by Poetry 2.3.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.4.2 and should not be changed by hand. [[package]] name = "aiohappyeyeballs" -version = "2.4.4" +version = "2.7.1" description = "Happy Eyeballs for asyncio" optional = false -python-versions = ">=3.8" +python-versions = ">=3.10" groups = ["main"] files = [ - {file = "aiohappyeyeballs-2.4.4-py3-none-any.whl", hash = "sha256:a980909d50efcd44795c4afeca523296716d50cd756ddca6af8c65b996e27de8"}, - {file = "aiohappyeyeballs-2.4.4.tar.gz", hash = "sha256:5fdd7d87889c63183afc18ce9271f9b0a7d32c2303e394468dd45d514a757745"}, + {file = "aiohappyeyeballs-2.7.1-py3-none-any.whl", hash = "sha256:9243213661e29250eb41368e5daa826fc017156c3b8a11440826b2e3ed376472"}, + {file = "aiohappyeyeballs-2.7.1.tar.gz", hash = "sha256:065665c041c42a5938ed220bdcd7230f22527fbec085e1853d2402c8a3615d9d"}, ] [[package]] name = "aiohttp" -version = "3.11.11" +version = "3.14.3" description = "Async http client/server framework (asyncio)" optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" groups = ["main"] files = [ - {file = "aiohttp-3.11.11-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a60804bff28662cbcf340a4d61598891f12eea3a66af48ecfdc975ceec21e3c8"}, - {file = "aiohttp-3.11.11-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4b4fa1cb5f270fb3eab079536b764ad740bb749ce69a94d4ec30ceee1b5940d5"}, - {file = "aiohttp-3.11.11-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:731468f555656767cda219ab42e033355fe48c85fbe3ba83a349631541715ba2"}, - {file = "aiohttp-3.11.11-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb23d8bb86282b342481cad4370ea0853a39e4a32a0042bb52ca6bdde132df43"}, - {file = "aiohttp-3.11.11-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f047569d655f81cb70ea5be942ee5d4421b6219c3f05d131f64088c73bb0917f"}, - {file = "aiohttp-3.11.11-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd7659baae9ccf94ae5fe8bfaa2c7bc2e94d24611528395ce88d009107e00c6d"}, - {file = "aiohttp-3.11.11-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af01e42ad87ae24932138f154105e88da13ce7d202a6de93fafdafb2883a00ef"}, - {file = "aiohttp-3.11.11-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5854be2f3e5a729800bac57a8d76af464e160f19676ab6aea74bde18ad19d438"}, - {file = "aiohttp-3.11.11-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6526e5fb4e14f4bbf30411216780c9967c20c5a55f2f51d3abd6de68320cc2f3"}, - {file = "aiohttp-3.11.11-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:85992ee30a31835fc482468637b3e5bd085fa8fe9392ba0bdcbdc1ef5e9e3c55"}, - {file = "aiohttp-3.11.11-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:88a12ad8ccf325a8a5ed80e6d7c3bdc247d66175afedbe104ee2aaca72960d8e"}, - {file = "aiohttp-3.11.11-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:0a6d3fbf2232e3a08c41eca81ae4f1dff3d8f1a30bae415ebe0af2d2458b8a33"}, - {file = "aiohttp-3.11.11-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:84a585799c58b795573c7fa9b84c455adf3e1d72f19a2bf498b54a95ae0d194c"}, - {file = "aiohttp-3.11.11-cp310-cp310-win32.whl", hash = "sha256:bfde76a8f430cf5c5584553adf9926534352251d379dcb266ad2b93c54a29745"}, - {file = "aiohttp-3.11.11-cp310-cp310-win_amd64.whl", hash = "sha256:0fd82b8e9c383af11d2b26f27a478640b6b83d669440c0a71481f7c865a51da9"}, - {file = "aiohttp-3.11.11-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ba74ec819177af1ef7f59063c6d35a214a8fde6f987f7661f4f0eecc468a8f76"}, - {file = "aiohttp-3.11.11-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4af57160800b7a815f3fe0eba9b46bf28aafc195555f1824555fa2cfab6c1538"}, - {file = "aiohttp-3.11.11-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ffa336210cf9cd8ed117011085817d00abe4c08f99968deef0013ea283547204"}, - {file = "aiohttp-3.11.11-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81b8fe282183e4a3c7a1b72f5ade1094ed1c6345a8f153506d114af5bf8accd9"}, - {file = "aiohttp-3.11.11-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3af41686ccec6a0f2bdc66686dc0f403c41ac2089f80e2214a0f82d001052c03"}, - {file = "aiohttp-3.11.11-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:70d1f9dde0e5dd9e292a6d4d00058737052b01f3532f69c0c65818dac26dc287"}, - {file = "aiohttp-3.11.11-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:249cc6912405917344192b9f9ea5cd5b139d49e0d2f5c7f70bdfaf6b4dbf3a2e"}, - {file = "aiohttp-3.11.11-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0eb98d90b6690827dcc84c246811feeb4e1eea683c0eac6caed7549be9c84665"}, - {file = "aiohttp-3.11.11-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ec82bf1fda6cecce7f7b915f9196601a1bd1a3079796b76d16ae4cce6d0ef89b"}, - {file = "aiohttp-3.11.11-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9fd46ce0845cfe28f108888b3ab17abff84ff695e01e73657eec3f96d72eef34"}, - {file = "aiohttp-3.11.11-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:bd176afcf8f5d2aed50c3647d4925d0db0579d96f75a31e77cbaf67d8a87742d"}, - {file = "aiohttp-3.11.11-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:ec2aa89305006fba9ffb98970db6c8221541be7bee4c1d027421d6f6df7d1ce2"}, - {file = "aiohttp-3.11.11-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:92cde43018a2e17d48bb09c79e4d4cb0e236de5063ce897a5e40ac7cb4878773"}, - {file = "aiohttp-3.11.11-cp311-cp311-win32.whl", hash = "sha256:aba807f9569455cba566882c8938f1a549f205ee43c27b126e5450dc9f83cc62"}, - {file = "aiohttp-3.11.11-cp311-cp311-win_amd64.whl", hash = "sha256:ae545f31489548c87b0cced5755cfe5a5308d00407000e72c4fa30b19c3220ac"}, - {file = "aiohttp-3.11.11-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e595c591a48bbc295ebf47cb91aebf9bd32f3ff76749ecf282ea7f9f6bb73886"}, - {file = "aiohttp-3.11.11-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3ea1b59dc06396b0b424740a10a0a63974c725b1c64736ff788a3689d36c02d2"}, - {file = "aiohttp-3.11.11-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8811f3f098a78ffa16e0ea36dffd577eb031aea797cbdba81be039a4169e242c"}, - {file = "aiohttp-3.11.11-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7227b87a355ce1f4bf83bfae4399b1f5bb42e0259cb9405824bd03d2f4336a"}, - {file = "aiohttp-3.11.11-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d40f9da8cabbf295d3a9dae1295c69975b86d941bc20f0a087f0477fa0a66231"}, - {file = "aiohttp-3.11.11-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ffb3dc385f6bb1568aa974fe65da84723210e5d9707e360e9ecb51f59406cd2e"}, - {file = "aiohttp-3.11.11-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8f5f7515f3552d899c61202d99dcb17d6e3b0de777900405611cd747cecd1b8"}, - {file = "aiohttp-3.11.11-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3499c7ffbfd9c6a3d8d6a2b01c26639da7e43d47c7b4f788016226b1e711caa8"}, - {file = "aiohttp-3.11.11-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8e2bf8029dbf0810c7bfbc3e594b51c4cc9101fbffb583a3923aea184724203c"}, - {file = "aiohttp-3.11.11-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b6212a60e5c482ef90f2d788835387070a88d52cf6241d3916733c9176d39eab"}, - {file = "aiohttp-3.11.11-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:d119fafe7b634dbfa25a8c597718e69a930e4847f0b88e172744be24515140da"}, - {file = "aiohttp-3.11.11-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:6fba278063559acc730abf49845d0e9a9e1ba74f85f0ee6efd5803f08b285853"}, - {file = "aiohttp-3.11.11-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:92fc484e34b733704ad77210c7957679c5c3877bd1e6b6d74b185e9320cc716e"}, - {file = "aiohttp-3.11.11-cp312-cp312-win32.whl", hash = "sha256:9f5b3c1ed63c8fa937a920b6c1bec78b74ee09593b3f5b979ab2ae5ef60d7600"}, - {file = "aiohttp-3.11.11-cp312-cp312-win_amd64.whl", hash = "sha256:1e69966ea6ef0c14ee53ef7a3d68b564cc408121ea56c0caa2dc918c1b2f553d"}, - {file = "aiohttp-3.11.11-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:541d823548ab69d13d23730a06f97460f4238ad2e5ed966aaf850d7c369782d9"}, - {file = "aiohttp-3.11.11-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:929f3ed33743a49ab127c58c3e0a827de0664bfcda566108989a14068f820194"}, - {file = "aiohttp-3.11.11-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0882c2820fd0132240edbb4a51eb8ceb6eef8181db9ad5291ab3332e0d71df5f"}, - {file = "aiohttp-3.11.11-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b63de12e44935d5aca7ed7ed98a255a11e5cb47f83a9fded7a5e41c40277d104"}, - {file = "aiohttp-3.11.11-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aa54f8ef31d23c506910c21163f22b124facb573bff73930735cf9fe38bf7dff"}, - {file = "aiohttp-3.11.11-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a344d5dc18074e3872777b62f5f7d584ae4344cd6006c17ba12103759d407af3"}, - {file = "aiohttp-3.11.11-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b7fb429ab1aafa1f48578eb315ca45bd46e9c37de11fe45c7f5f4138091e2f1"}, - {file = "aiohttp-3.11.11-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c341c7d868750e31961d6d8e60ff040fb9d3d3a46d77fd85e1ab8e76c3e9a5c4"}, - {file = "aiohttp-3.11.11-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ed9ee95614a71e87f1a70bc81603f6c6760128b140bc4030abe6abaa988f1c3d"}, - {file = "aiohttp-3.11.11-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:de8d38f1c2810fa2a4f1d995a2e9c70bb8737b18da04ac2afbf3971f65781d87"}, - {file = "aiohttp-3.11.11-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:a9b7371665d4f00deb8f32208c7c5e652059b0fda41cf6dbcac6114a041f1cc2"}, - {file = "aiohttp-3.11.11-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:620598717fce1b3bd14dd09947ea53e1ad510317c85dda2c9c65b622edc96b12"}, - {file = "aiohttp-3.11.11-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:bf8d9bfee991d8acc72d060d53860f356e07a50f0e0d09a8dfedea1c554dd0d5"}, - {file = "aiohttp-3.11.11-cp313-cp313-win32.whl", hash = "sha256:9d73ee3725b7a737ad86c2eac5c57a4a97793d9f442599bea5ec67ac9f4bdc3d"}, - {file = "aiohttp-3.11.11-cp313-cp313-win_amd64.whl", hash = "sha256:c7a06301c2fb096bdb0bd25fe2011531c1453b9f2c163c8031600ec73af1cc99"}, - {file = "aiohttp-3.11.11-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3e23419d832d969f659c208557de4a123e30a10d26e1e14b73431d3c13444c2e"}, - {file = "aiohttp-3.11.11-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:21fef42317cf02e05d3b09c028712e1d73a9606f02467fd803f7c1f39cc59add"}, - {file = "aiohttp-3.11.11-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1f21bb8d0235fc10c09ce1d11ffbd40fc50d3f08a89e4cf3a0c503dc2562247a"}, - {file = "aiohttp-3.11.11-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1642eceeaa5ab6c9b6dfeaaa626ae314d808188ab23ae196a34c9d97efb68350"}, - {file = "aiohttp-3.11.11-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2170816e34e10f2fd120f603e951630f8a112e1be3b60963a1f159f5699059a6"}, - {file = "aiohttp-3.11.11-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8be8508d110d93061197fd2d6a74f7401f73b6d12f8822bbcd6d74f2b55d71b1"}, - {file = "aiohttp-3.11.11-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4eed954b161e6b9b65f6be446ed448ed3921763cc432053ceb606f89d793927e"}, - {file = "aiohttp-3.11.11-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6c9af134da4bc9b3bd3e6a70072509f295d10ee60c697826225b60b9959acdd"}, - {file = "aiohttp-3.11.11-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:44167fc6a763d534a6908bdb2592269b4bf30a03239bcb1654781adf5e49caf1"}, - {file = "aiohttp-3.11.11-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:479b8c6ebd12aedfe64563b85920525d05d394b85f166b7873c8bde6da612f9c"}, - {file = "aiohttp-3.11.11-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:10b4ff0ad793d98605958089fabfa350e8e62bd5d40aa65cdc69d6785859f94e"}, - {file = "aiohttp-3.11.11-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:b540bd67cfb54e6f0865ceccd9979687210d7ed1a1cc8c01f8e67e2f1e883d28"}, - {file = "aiohttp-3.11.11-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1dac54e8ce2ed83b1f6b1a54005c87dfed139cf3f777fdc8afc76e7841101226"}, - {file = "aiohttp-3.11.11-cp39-cp39-win32.whl", hash = "sha256:568c1236b2fde93b7720f95a890741854c1200fba4a3471ff48b2934d2d93fd3"}, - {file = "aiohttp-3.11.11-cp39-cp39-win_amd64.whl", hash = "sha256:943a8b052e54dfd6439fd7989f67fc6a7f2138d0a2cf0a7de5f18aa4fe7eb3b1"}, - {file = "aiohttp-3.11.11.tar.gz", hash = "sha256:bb49c7f1e6ebf3821a42d81d494f538107610c3a705987f53068546b0e90303e"}, + {file = "aiohttp-3.14.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:eb0495d778817619273c108784292be161a924b9f5ae5cbbc70a2caa6838250b"}, + {file = "aiohttp-3.14.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c3c200cf9757edd785051dc699c7ecbec22110dbfcb3fefc7a9f9695eda8ea7a"}, + {file = "aiohttp-3.14.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd51ebf9d3a00c074df4ede271023f4d2dba289bcc740b88191872716014e3c5"}, + {file = "aiohttp-3.14.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:134ac5ddcf61c6fad984b9a5727d83492ada43d63471db20fb73042c13fca62f"}, + {file = "aiohttp-3.14.3-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:70c987b27534f9ae1a723f47ae921571d616da21d3208282bf4c52af5164ac43"}, + {file = "aiohttp-3.14.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1b59533861b70a2185c8f4f350f791f39d64358ef6944ce71c5240c9ec0982c9"}, + {file = "aiohttp-3.14.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1c5281acc88b92396f88c7e1e2748f8466689df22b80170e4f51efa712fb47a8"}, + {file = "aiohttp-3.14.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:48d67b87db6279c044760787eb01f6413032c2e6f3ba1cafaa492b1c8e578479"}, + {file = "aiohttp-3.14.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f53bcd52f585e1ac3e590d61434eb61f9a88c38df041b4ea126d97144344a77b"}, + {file = "aiohttp-3.14.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0fdea2281997af69da84c77ffa6f5938a0285f21fb3887c249d67419ca865b3d"}, + {file = "aiohttp-3.14.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:cda5fd5c95ad7a125a2e8464acc78b98b94c475a3780d6aa0aa157c93f470f4d"}, + {file = "aiohttp-3.14.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:6debfa7312ff9d4c124dc71d72e9a0a4b9e0879e48ba6fcb42bef5c3300289e2"}, + {file = "aiohttp-3.14.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:f4e05329faa0ea1a404b37de4f034fd2c2defcca06a68dc6745e4e56c88e8a48"}, + {file = "aiohttp-3.14.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:a3a8296e7ab5c295f53f1041487cb088e1480775aafbf7fe545d93b770a0f96f"}, + {file = "aiohttp-3.14.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5373dc80ad1aa2fb9ad95c83f24eef418bbda3a61375f128e5b0192e4f3f9b32"}, + {file = "aiohttp-3.14.3-cp310-cp310-win32.whl", hash = "sha256:a3e22975f905b89a55a488c2a08f2fdb2186175349e917d48985cc468a3d4c6e"}, + {file = "aiohttp-3.14.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdd0e2834dce1a26c1bbe26464861e16bbe217042cbff619247c11594472518c"}, + {file = "aiohttp-3.14.3-cp310-cp310-win_arm64.whl", hash = "sha256:eac645b09bcfdf73df7536331f0678c1086ea250981118ddb5199e17ccef72bb"}, + {file = "aiohttp-3.14.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e568e14940c09955aa51f4e645b6daa18a581c5dcfcd73744dcc86a856e3ced3"}, + {file = "aiohttp-3.14.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:54cfcdee2770dac994417cbb0ee1f3eb0e7cb6b30c79bf44f2c02ff79ec5124a"}, + {file = "aiohttp-3.14.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:21c016079415ed3fd676963e9793700a566d85dbbd6bfc564b9b2d209147dcc8"}, + {file = "aiohttp-3.14.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d6088ec9894113802bddb3c09e974929aed2c7b3a8c456219b8aab4481f1a239"}, + {file = "aiohttp-3.14.3-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:16ea7e24c309fb7c0bbd505d149abe4fe4dccfb8db911db7dbec0921bc889a6f"}, + {file = "aiohttp-3.14.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:56f355e79f71aef2a85c80305cc915f894b170dba76de5fe84f6351939b83c06"}, + {file = "aiohttp-3.14.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:18c441d0a8fca6de8d1f546849b9f0ab20d435993e2c5b59562b2fae6be2f929"}, + {file = "aiohttp-3.14.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:53e7b4ce82b54a8bcc71b3b67a5cbd177ca1d7f592cbc92cd38b7349f73482db"}, + {file = "aiohttp-3.14.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f55119f7bf25f49ed210f6096090715da24f2943c62102448915fde3c62877ce"}, + {file = "aiohttp-3.14.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9aa6e61fdf20105c4144e755bd586008ff450791d67b1c8146fdc15959c4d51c"}, + {file = "aiohttp-3.14.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:ccd4893707b3e2a13e39c90d43cf80edf2e4d0457935bcc103bf2346214c3f15"}, + {file = "aiohttp-3.14.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:b2466434105a4e03113c36ec775cc2ebe6676b62eae326fa670bb607ef788c1c"}, + {file = "aiohttp-3.14.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:ba59d59aba08ac02fc03b0c8983ccd5ee39a199d0552ce9e6d2b4845b34d59ae"}, + {file = "aiohttp-3.14.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:ed099d105449c4f9e84f24af203cd131349d4761d8813fa7e02c32e7128cd910"}, + {file = "aiohttp-3.14.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:152516815ef926786a0b6ae2b8f1fd2e0c71582dee0b435636865316fd4891b7"}, + {file = "aiohttp-3.14.3-cp311-cp311-win32.whl", hash = "sha256:a4af35c443e0b1a1bd6a8af3f3485d7fda15c142751a00f3ff8090f0b93346fa"}, + {file = "aiohttp-3.14.3-cp311-cp311-win_amd64.whl", hash = "sha256:e1e74298bab6ee0d6e749ed4fd1901c7e604bdda32c03d787a2cc71c46d0433d"}, + {file = "aiohttp-3.14.3-cp311-cp311-win_arm64.whl", hash = "sha256:03cd2bde3d7f085b64e549c985f4bb928cad7e8ecf5323bfca320db548d81b39"}, + {file = "aiohttp-3.14.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:39aded8c7f3b935b54aab1d8d73c70ec0ee2d3ec3b943e0e86611bc150ba47f5"}, + {file = "aiohttp-3.14.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5bcb6ff3fdab1258a192679ff1a05d44f59626430aa05cd1a9d2447423599228"}, + {file = "aiohttp-3.14.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:617105e2c3018ee38d0c8ce5ee3c84f621a6d8b9f723202aacaff28449ca91ee"}, + {file = "aiohttp-3.14.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f631fe87a6f30df5fbe6d79640b25e4cffb38c31c7fb6f10871517b84b0f8c1a"}, + {file = "aiohttp-3.14.3-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a94dbaae5ae27bd849c93570669bff91e0510f33a80805738e3de72a7be0447b"}, + {file = "aiohttp-3.14.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8f2f1c4c032c7cedd7d8da6f54c97b70266c6570c3108d3fdffee7188bb70529"}, + {file = "aiohttp-3.14.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ea05e1f97ceea523942d9b2a7d7c0359d781d683d6b043f5943a602b14da4787"}, + {file = "aiohttp-3.14.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:543906c127fb1d929b95076db19b83fa2d46751006ff1e23b093aa5ac4d8db42"}, + {file = "aiohttp-3.14.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0a5ff2dfbb9ce645fa5b8ef3e02c6c0b9cc3f6030ff863d0c51fffc50cb5541b"}, + {file = "aiohttp-3.14.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:041badb8f84396357c4d3ad26de6afd7a32b112f43d3c63045c0c8278cfd2043"}, + {file = "aiohttp-3.14.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:530125ee1163c4219af35dc3aa1206e541e7b31b6efc1a3f93b70a136f65d427"}, + {file = "aiohttp-3.14.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c8653fd547c93a61aadc612007790f5555cdd18946fa48cf45e26d8ea4ea473d"}, + {file = "aiohttp-3.14.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:89176250f686cb9853c0fb7ead90e639e915b84a6f43eedc2a4e7ec21f1037f0"}, + {file = "aiohttp-3.14.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3a26434dafe408229ff3403458ca58de24fb51936504decac49ce6755f77e59d"}, + {file = "aiohttp-3.14.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d1558173930a5a8d3069cee5c92fc91c87c4dbcb099debbb3622053717145a19"}, + {file = "aiohttp-3.14.3-cp312-cp312-win32.whl", hash = "sha256:16100ad3ab8d649fdfbee87602d9d2dcdca9df0b9eda8a1b5fdc0d41f96da559"}, + {file = "aiohttp-3.14.3-cp312-cp312-win_amd64.whl", hash = "sha256:33a2d7c28d33797a2e99923dffa63f83d908a19b6bf26cfe80fa790aa5e1a75a"}, + {file = "aiohttp-3.14.3-cp312-cp312-win_arm64.whl", hash = "sha256:362a3fd481769cac1a824514bcd86fda51c65e8fe6e051099e008fddde6db17c"}, + {file = "aiohttp-3.14.3-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:2e9878ae68e4a5f1c0abe4dd497dbc3d51946f5837b56759e2a02e78fa90ef86"}, + {file = "aiohttp-3.14.3-cp313-cp313-android_21_x86_64.whl", hash = "sha256:f3d2669fe7dec7fc359ecdb5984b29b50d85d5d00f8c1cb61de4f4a24ee42627"}, + {file = "aiohttp-3.14.3-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:cc7cb243a68167172f48c1fd43cee91ec4b1d40cefd190edd43369d1a6bc9c82"}, + {file = "aiohttp-3.14.3-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:78253b573e6ffab5028924fc98bc281aae05445969982a10864bc360dea2016c"}, + {file = "aiohttp-3.14.3-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:7041d52c3a7fa20c9e8c182b534704abb19502c8bdcbde7ab23bfda6f642394f"}, + {file = "aiohttp-3.14.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ac74facc01463f138b0da5580329cfcc82818dea5656e83ddcd11268fc12ff80"}, + {file = "aiohttp-3.14.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d6218d92e450824e9b4881f44e8c09f1853b490f9a64130801024a4793b1b3b0"}, + {file = "aiohttp-3.14.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:11fb37ef075669eee52ab1928fbf6e1741fada40409fa309ebde9607a962aebf"}, + {file = "aiohttp-3.14.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:55bdcc472aafe2de4a253045cc128007a64f1e0264fb675791e132ea5edaa3bd"}, + {file = "aiohttp-3.14.3-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c39846c3aad97a8530c89d7a3869a8f8e9e3762c6ac0504481e5c80948f7e807"}, + {file = "aiohttp-3.14.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5895ef58c4620afe02fa16044f023dc4dafec08158f9d08874a46a7dbc0341b8"}, + {file = "aiohttp-3.14.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fa9467a8113aa69d3d7c55a70ef0b7c636010a40993f3df9d9d0d73b3eb7ef24"}, + {file = "aiohttp-3.14.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d7d2deec16eeedf55f2c7cf75b521ea3856a5177e123844f8fd0f114ce252cb5"}, + {file = "aiohttp-3.14.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:dd54d0e8717de95939766febac482ac0474d8ac3b048115f9f2b1d23a16e7db4"}, + {file = "aiohttp-3.14.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:df82f3787c940c94986b34222d59c9e38843fba85139f36e85255a82ad5355a9"}, + {file = "aiohttp-3.14.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:42a67efc36300d052fb4508a53e8b6901b9284b599ae63945c377569c5fcc1e1"}, + {file = "aiohttp-3.14.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7a75aa63cbf9b21cfaf60dc2657e19df2c2867d91707d653fee171ffeedd1371"}, + {file = "aiohttp-3.14.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:e92eb8acc45eb6a9f4935071a77edf5b85cc6f8dfad5cd99e97653c26593cdde"}, + {file = "aiohttp-3.14.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b014a6ed7cf912e787149fdc529166d3ceabac23f26efeea3158c9aba2354e7e"}, + {file = "aiohttp-3.14.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3d4f72af88ac2474bb5bca640030320e3d38a0163a1d7533500e87be458eef71"}, + {file = "aiohttp-3.14.3-cp313-cp313-win32.whl", hash = "sha256:5f08ec777f35ee70720233b8b9811d3bb5d728137f30ac91b7457709c3261ac0"}, + {file = "aiohttp-3.14.3-cp313-cp313-win_amd64.whl", hash = "sha256:dff9461ec275f22135650d5ba4b4931a11f3958df7dfbb8db630000d4dee0883"}, + {file = "aiohttp-3.14.3-cp313-cp313-win_arm64.whl", hash = "sha256:ddcac3c6b382e81f1dd0499199d4136b877beb4cb5ef770bbbfba56c4b8f55d2"}, + {file = "aiohttp-3.14.3-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:49f7325beb0f85ef4aef5f48f490269575f83e6e2acad00a1d80b807eb027062"}, + {file = "aiohttp-3.14.3-cp314-cp314-android_24_x86_64.whl", hash = "sha256:e3be98a7c30b8c25d573dafba7171d66dfb05ee6a9070fc46535464ff97700a6"}, + {file = "aiohttp-3.14.3-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:614c61d478b83953e261d02bb2df750f17227cd33ef8002945bf5aebbde21919"}, + {file = "aiohttp-3.14.3-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:1caa7b0d05f3e3a36f87788c59e970a7ee1cefcfcbb924a9f138c4a6551c9cb7"}, + {file = "aiohttp-3.14.3-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:dfa68deb2a443bdaa3ea5297b0699c1464f08aef3812b486d1348eee61b07dc0"}, + {file = "aiohttp-3.14.3-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:e72ee89e28d907a18f46959b4eb0bb06701cc7f8cf4366e00029e2ccfaaf5924"}, + {file = "aiohttp-3.14.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:ad4c8b7488d745d2ca4838ebd8ae5ba9b56341d30b1da43640e4ce87f9f49646"}, + {file = "aiohttp-3.14.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:db332af25642007330fca8be5c4d194caf2bea7a7fc84415aff3497af5dfee6b"}, + {file = "aiohttp-3.14.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:25bd2708db6bdf6a6630dd37bdcdfcb47c4434d22ac69c64665b802910140b30"}, + {file = "aiohttp-3.14.3-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:cef89a58e628c4efcac3275c2d68083f82426dcdc89c1492a6f654f9f7ea6ab9"}, + {file = "aiohttp-3.14.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c23ec8ee9d5ab2f5421f9c7fffce208435607af27fd46d4a44e031954352838f"}, + {file = "aiohttp-3.14.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e2667f0bbe7eb6c74eae5e9691441ad186e5845ca3cff63230fc09c4e7514f5d"}, + {file = "aiohttp-3.14.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18cb43369747b2ae007bd2655fb8e63a099c2ff1d207962943636dac989b3147"}, + {file = "aiohttp-3.14.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d77640cc618c1d99fc4f8589c0f24a730adfa54eb1e57ef7bf0c8dfb78da898c"}, + {file = "aiohttp-3.14.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:53e5179d8abb5710f8e83ba207c41c8d1261fcffd4616500e15ca2b7a33be10a"}, + {file = "aiohttp-3.14.3-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:cd817772b2fcf2b8c0905795318485f9ec16eae60b29feb7f4c77085311637f0"}, + {file = "aiohttp-3.14.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:4e3ac92d90e92773b2362d506068e9a948192bd553e743c5b2429e28527c8661"}, + {file = "aiohttp-3.14.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:3f42e9b78301f11c8f861746175d8b9c1ccef713fcad9eab396e2f6db8ed4a22"}, + {file = "aiohttp-3.14.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:9d9edccfe496b476db5f398d97b865e9a6752bcf8aec4eef8390ce20fb64bb41"}, + {file = "aiohttp-3.14.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:1c5ec8fb1bcc31a8466f74aaf26c345d5c386fa4bd08a3f0eb9c7a4a3fe8b5bf"}, + {file = "aiohttp-3.14.3-cp314-cp314-win32.whl", hash = "sha256:38901a84da3ce22249f6e860bf8f90d141bcab7da090cc398f8bb58c0e44b7da"}, + {file = "aiohttp-3.14.3-cp314-cp314-win_amd64.whl", hash = "sha256:8b3b60de05f3dcb6f6a00f818bb2ec781cee4de0645f59ccaf99b1d1823b6100"}, + {file = "aiohttp-3.14.3-cp314-cp314-win_arm64.whl", hash = "sha256:1576145bdceeb92382d899751e12743a3a5b8e460a841e3e50543859e54864dc"}, + {file = "aiohttp-3.14.3-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:8800c996b01c2772a783e3e46f3e1abd5823029adca0df54231960de9bfefa5b"}, + {file = "aiohttp-3.14.3-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:ebe8e504f058fe91223351cecd2d9d6946c9d241bb0250d898ffbdf584cc72b0"}, + {file = "aiohttp-3.14.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:30402d03a7c0ff52bce290b57e564e9079fd9d0cb545c8aba73f86a103162d2e"}, + {file = "aiohttp-3.14.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9fc7b5bfec6573f3ae844f457fdde5adeb713f8b8e4a81ad64fc207b49383716"}, + {file = "aiohttp-3.14.3-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8a5fd34f7f7410d1730d5c2ba873cacb2eed3fede366feb268a70ba22581ed8f"}, + {file = "aiohttp-3.14.3-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:270d3dace9ca2f10f0da5d8ebe519b7a310fc6112ed916e32df5866df0888553"}, + {file = "aiohttp-3.14.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3ae5b3a59436d089b5395d910121a390feed4d00578eb95a0fd1a329fe963100"}, + {file = "aiohttp-3.14.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2498f0fe69ead802f9675beca44a7c21c62fdaa4ec5145ea1c3ad6edbee29f85"}, + {file = "aiohttp-3.14.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a0dc483c00da8b673abbb367eb6f8d8f4bcec30eb58529ea13cb42e7fd2dfa33"}, + {file = "aiohttp-3.14.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c7d3a97c678d34fc5b59da671ee9cd630096ddc643e7b5a30d54a2a6f3574d3f"}, + {file = "aiohttp-3.14.3-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:f8fb78a83c9e5f741ca3a68cfb455c1f5bb83b4e7249a3848b3cd78d0a8563b0"}, + {file = "aiohttp-3.14.3-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:74ab5b6a9fb13e873e5a90946588baecaf488745e1db1a4a5c433f971f035098"}, + {file = "aiohttp-3.14.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:bd52f811e65f6fb634b1047159657c98f52b407f8efec907bcfc09da9a4c0a25"}, + {file = "aiohttp-3.14.3-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:f0f177d1b195b9e06376cfd7d308d8a1b920909a609d03ac82a8c73bbb16d3b9"}, + {file = "aiohttp-3.14.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:498c6c623134f8e09a3c4e60bcd607a0b4590dd7dbf08dd40851b27cbb520ccb"}, + {file = "aiohttp-3.14.3-cp314-cp314t-win32.whl", hash = "sha256:b304db572b4368edd8dda8a2274f73156fe15558fca4a917cb8a09fc47af5963"}, + {file = "aiohttp-3.14.3-cp314-cp314t-win_amd64.whl", hash = "sha256:b20032766aedf6261c7a566585a40867d092ac03a0d81592d5370ef9b054f99b"}, + {file = "aiohttp-3.14.3-cp314-cp314t-win_arm64.whl", hash = "sha256:2e1161602f45a54de2ce0905243a95f58cb42dcd378402f3697f5e0b21e9d2e7"}, + {file = "aiohttp-3.14.3.tar.gz", hash = "sha256:9491196535a88924a60afd5b5f434b5b203b6cc616250878dbdb223a8f7844bc"}, ] [package.dependencies] -aiohappyeyeballs = ">=2.3.0" -aiosignal = ">=1.1.2" +aiohappyeyeballs = ">=2.5.0" +aiosignal = ">=1.4.0" attrs = ">=17.3.0" frozenlist = ">=1.1.1" multidict = ">=4.5,<7.0" propcache = ">=0.2.0" +typing_extensions = {version = ">=4.4", markers = "python_version < \"3.13\""} yarl = ">=1.17.0,<2.0" [package.extras] -speedups = ["Brotli ; platform_python_implementation == \"CPython\"", "aiodns (>=3.2.0) ; sys_platform == \"linux\" or sys_platform == \"darwin\"", "brotlicffi ; platform_python_implementation != \"CPython\""] +speedups = ["Brotli (>=1.2) ; platform_python_implementation == \"CPython\" and sys_platform != \"android\" and sys_platform != \"ios\"", "aiodns (>=3.3.0) ; sys_platform != \"android\" and sys_platform != \"ios\"", "backports.zstd ; platform_python_implementation == \"CPython\" and python_version < \"3.14\" and sys_platform != \"android\" and sys_platform != \"ios\"", "brotlicffi (>=1.2) ; platform_python_implementation != \"CPython\""] [[package]] name = "aiosignal" -version = "1.3.2" +version = "1.4.0" description = "aiosignal: a list of registered asynchronous callbacks" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5"}, - {file = "aiosignal-1.3.2.tar.gz", hash = "sha256:a8c255c66fafb1e499c9351d0bf32ff2d8a0321595ebac3b93713656d2436f54"}, + {file = "aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e"}, + {file = "aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7"}, ] [package.dependencies] frozenlist = ">=1.1.0" +typing-extensions = {version = ">=4.2", markers = "python_version < \"3.13\""} [[package]] name = "argon2-cffi" @@ -184,6 +229,18 @@ cffi = ">=1.0.1" dev = ["cogapp", "pre-commit", "pytest", "wheel"] tests = ["pytest"] +[[package]] +name = "asn1crypto" +version = "1.5.1" +description = "Fast ASN.1 parser and serializer with definitions for private keys, public keys, certificates, CRL, OCSP, CMS, PKCS#3, PKCS#7, PKCS#8, PKCS#12, PKCS#5, X.509 and TSP" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "asn1crypto-1.5.1-py2.py3-none-any.whl", hash = "sha256:db4e40728b728508912cbb3d44f19ce188f218e9eba635821bb4b68564f8fd67"}, + {file = "asn1crypto-1.5.1.tar.gz", hash = "sha256:13ae38502be632115abf8a24cbe5f4da52e3b5231990aff31123c805306ccb9c"}, +] + [[package]] name = "asttokens" version = "3.0.0" @@ -1111,6 +1168,22 @@ files = [ [package.dependencies] ptyprocess = ">=0.5" +[[package]] +name = "pg8000" +version = "1.31.5" +description = "PostgreSQL interface library" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "pg8000-1.31.5-py3-none-any.whl", hash = "sha256:0af2c1926b153307639868d2ee5cef6cd3a7d07448e12736989b10e1d491e201"}, + {file = "pg8000-1.31.5.tar.gz", hash = "sha256:46ebb03be52b7a77c03c725c79da2ca281d6e8f59577ca66b17c9009618cae78"}, +] + +[package.dependencies] +python-dateutil = ">=2.8.2" +scramp = ">=1.4.5" + [[package]] name = "prompt-toolkit" version = "3.0.48" @@ -1218,105 +1291,6 @@ files = [ {file = "propcache-0.2.1.tar.gz", hash = "sha256:3f77ce728b19cb537714499928fe800c3dda29e8d9428778fc7c186da4c09a64"}, ] -[[package]] -name = "psycopg" -version = "3.2.4" -description = "PostgreSQL database adapter for Python" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "psycopg-3.2.4-py3-none-any.whl", hash = "sha256:43665368ccd48180744cab26b74332f46b63b7e06e8ce0775547a3533883d381"}, - {file = "psycopg-3.2.4.tar.gz", hash = "sha256:f26f1346d6bf1ef5f5ef1714dd405c67fb365cfd1c6cea07de1792747b167b92"}, -] - -[package.dependencies] -typing-extensions = {version = ">=4.6", markers = "python_version < \"3.13\""} -tzdata = {version = "*", markers = "sys_platform == \"win32\""} - -[package.extras] -binary = ["psycopg-binary (==3.2.4) ; implementation_name != \"pypy\""] -c = ["psycopg-c (==3.2.4) ; implementation_name != \"pypy\""] -dev = ["ast-comments (>=1.1.2)", "black (>=24.1.0)", "codespell (>=2.2)", "dnspython (>=2.1)", "flake8 (>=4.0)", "mypy (>=1.14)", "pre-commit (>=4.0.1)", "types-setuptools (>=57.4)", "wheel (>=0.37)"] -docs = ["Sphinx (>=5.0)", "furo (==2022.6.21)", "sphinx-autobuild (>=2021.3.14)", "sphinx-autodoc-typehints (>=1.12)"] -pool = ["psycopg-pool"] -test = ["anyio (>=4.0)", "mypy (>=1.14)", "pproxy (>=2.7)", "pytest (>=6.2.5)", "pytest-cov (>=3.0)", "pytest-randomly (>=3.5)"] - -[[package]] -name = "psycopg-binary" -version = "3.2.4" -description = "PostgreSQL database adapter for Python -- C optimisation distribution" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "psycopg_binary-3.2.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c716f75b5c0388fc5283b5124046292c727511dd8c6aa59ca2dc644b9a2ed0cd"}, - {file = "psycopg_binary-3.2.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e2e8050347018f596a63f5dccbb92fb68bca52b13912cb8fc40184b24c0e534f"}, - {file = "psycopg_binary-3.2.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:04171f9af9ab567c0fd339bac06f2c75836db839cebac5bd07824778dafa7f0e"}, - {file = "psycopg_binary-3.2.4-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7ba7b2ff25a6405826f627fb7d0f1e06e5c08ae25ffabc74a5e9ec7b0a63b85"}, - {file = "psycopg_binary-3.2.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e58eeba520d405b2ad72dffaafd04d0b592bef870e718bf37c261e89a75450a"}, - {file = "psycopg_binary-3.2.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb18cfbb1cfc8172786ceefd314f0faa05c40ea93b3db7194d0f6bbbbfedb42a"}, - {file = "psycopg_binary-3.2.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:769804b4f753ddec9403183a6d4577d5b696fc49c2451421013fb06d6fa2f288"}, - {file = "psycopg_binary-3.2.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:7d4f0c9b01eb933ce35bb32a54205f48d7bc36bf455565afe269cabcb7973955"}, - {file = "psycopg_binary-3.2.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:26aed7ff8691ba810de95718d3bc81a43fd48a4036c3641ef711eb5f71fc7106"}, - {file = "psycopg_binary-3.2.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8a4b65eaf44dfed0b47e6ebd392e88cd3cff62ea11652d92db6fefeb2608ed25"}, - {file = "psycopg_binary-3.2.4-cp310-cp310-win_amd64.whl", hash = "sha256:a9fa48a2dc54c4e906d7dd781031d227d1b13966deff7e5ece5b037588643190"}, - {file = "psycopg_binary-3.2.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d092b0aa80b8c3ee0701a7252cbfb0bdb742e1f74aaf0c1a13ef22c05c9266ab"}, - {file = "psycopg_binary-3.2.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3955381dacc6d15f3838d5f25445ee99f80882876a163f8de0c01ffc54aeef4a"}, - {file = "psycopg_binary-3.2.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:04144d1963aa3309247980f1a742b98e15f60d68ea9745143c433f99aaeb70d7"}, - {file = "psycopg_binary-3.2.4-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eac61931bc90c1c6fdc648452894d3a434a005ffefaf12819b4709548c894bf2"}, - {file = "psycopg_binary-3.2.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c09b765960480c4586758a3c16f0ee0db6f7e2f31c88cccb5e7d7024215468cd"}, - {file = "psycopg_binary-3.2.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:220de8efcc276e42ba7cc7ed613145b1274b6b5de321a1396fb6b6ce1758d34c"}, - {file = "psycopg_binary-3.2.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b558d3de315d18819ce477908e27518cbdd3275717c6193b58dde36f0443e167"}, - {file = "psycopg_binary-3.2.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e3b4c9b9a112d43533f7dbdedbb1188107d4ddcd262e2a2af41b4de0caf7d053"}, - {file = "psycopg_binary-3.2.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:870df866f789bb641a350897c1751c293b9420f46be4eb366d190ff5f2f2ffd8"}, - {file = "psycopg_binary-3.2.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:89506e268fb95428fb0f8f7abe48032e66cf47390469e11a4fe989f7407a5d88"}, - {file = "psycopg_binary-3.2.4-cp311-cp311-win_amd64.whl", hash = "sha256:7ddf1494cc3bf60761c01265c44dfc7a7fd63f21308c403c14f5dd91702df84d"}, - {file = "psycopg_binary-3.2.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3ac24b3d421127ebe8662eba2c1e149a12f0f5b6795e66c1811a3f59111456bb"}, - {file = "psycopg_binary-3.2.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f702f36204127984dd212eb57bb328676abdfe8a56f179e408a806d5e520aa11"}, - {file = "psycopg_binary-3.2.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:610cd2013ee0849154fcff34b0cca17f720c91c7430ca094a61f1e5ff1d38e15"}, - {file = "psycopg_binary-3.2.4-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:95da59edd95f6b6488799c9710fafc2d5750e3ec6328ec991f7a9be04efe6886"}, - {file = "psycopg_binary-3.2.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b71e98e3186f08473962e1ea4bfbc4387ecc398644b794cb112ad0a4276e3789"}, - {file = "psycopg_binary-3.2.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ccf4f71c3a0d46bc74207bf7997f010a6586414161dd10f3dd026ec059942ef"}, - {file = "psycopg_binary-3.2.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:244e1dd33b694792b7bc7a3d412a535ba39116218b07d8936b4591567f4121e9"}, - {file = "psycopg_binary-3.2.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:f8dc8f4de5130c6278dd5e34b18ad8324a74658a7adb72d4e67ca97f9aeaaf3c"}, - {file = "psycopg_binary-3.2.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c336e58a48061a9189d3ba8c19f00fe5d9570219e6f7f954b923ad5c33e5bc71"}, - {file = "psycopg_binary-3.2.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9633c5dc6796d11766d2475e62335b67e5f99f119f40ba1675c1d23208d7709d"}, - {file = "psycopg_binary-3.2.4-cp312-cp312-win_amd64.whl", hash = "sha256:295c25e56b430d786a475c5c2cef266b0b27c0a6fcaadf9d83a4cdcfb76f971f"}, - {file = "psycopg_binary-3.2.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:81ab801c0d35830c876bf0d1edc8e7dd2f73aa2b04fe24eb812159c0b054d149"}, - {file = "psycopg_binary-3.2.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c09e02ce1124eb6638b3381df050a8cf88aedfad4522f939945cda49050a990c"}, - {file = "psycopg_binary-3.2.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a249cdc6a5c2b5088a8677acba66b291e5237524739ab3d27498e1ef189312f5"}, - {file = "psycopg_binary-3.2.4-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d2960ba8a5c0ad75e184f6d8bf76bdf023708999efe75fe4e13445136c1cd206"}, - {file = "psycopg_binary-3.2.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3dae2e50b0d3425c167eebbedc3553f7c811dbc0dbfc737b6877f68a03be7daf"}, - {file = "psycopg_binary-3.2.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03bf7ee7e0002c2cce43ecb923ec510358056eb2e44a96afaeb0424518f35206"}, - {file = "psycopg_binary-3.2.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5f5c85eeb63b1a8a6b026eef57f5da36ff215ce9a6a3bb8e20a409670d6cfbda"}, - {file = "psycopg_binary-3.2.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:8c7b95899d4d6d23c5cc46cb3419e8e6ca68d867509432ee1487042564a1ea55"}, - {file = "psycopg_binary-3.2.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:fa4acea9ca20a567c3872a5afab2084751530bb57b8fb6b52820d5c54e7c8c3b"}, - {file = "psycopg_binary-3.2.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5c487f35a1905bb15da927c1fc05f70f3d29f0e21fb4ba21d360a0da9c755f20"}, - {file = "psycopg_binary-3.2.4-cp313-cp313-win_amd64.whl", hash = "sha256:80297c3a9f7b5a6afdb0d8f220661ccd796e5c9128c44b32c41267f7daefd37f"}, - {file = "psycopg_binary-3.2.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:22cf23d037310ae08feceea5e24f727b1ef816867188dbec2edde2e7237b0004"}, - {file = "psycopg_binary-3.2.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3409151b91df85ef99a72d137aba289e1d7b5d4ac7750b37183674421903e04"}, - {file = "psycopg_binary-3.2.4-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1145c3c038e6dbe7127309cc9bbe209bce5743f9f02a2a65c4f9478bd794598e"}, - {file = "psycopg_binary-3.2.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:21d369bac7606157ef2699a0ff65c8d43d274f0178fd03241babb5f86b7586f7"}, - {file = "psycopg_binary-3.2.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:158fa0dbda433e0069bd2b6ffdf357c9fcdb84ec5e3b353fb8206636873b54f9"}, - {file = "psycopg_binary-3.2.4-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:4a56b55072a3e0629e6421a7f6fdd4eecc0eba4e9cedaaf2e7578ac62c336680"}, - {file = "psycopg_binary-3.2.4-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:d993ecfa7f2ac30108d57e7418732d70aa399ccb4a8ca1cf415638679fb32e8b"}, - {file = "psycopg_binary-3.2.4-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:a5b68ba52bdf3ed86a8a1f1ac809ecd775ffd7bb611438d3ab9e1ee572742f95"}, - {file = "psycopg_binary-3.2.4-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:90423ff7a0c1f4001b8d54e6c7866f5bbb778f3f4272a70a7926878fe7d8763c"}, - {file = "psycopg_binary-3.2.4-cp38-cp38-win_amd64.whl", hash = "sha256:5a462bdd427330418fa2a011b6494103edd94cacd4f5b00e598bcbd1c8d20fb9"}, - {file = "psycopg_binary-3.2.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2ddec5deed4c93a1bd73f210bed6dadbabc470ac1f9ebf55fa260e48396fd61f"}, - {file = "psycopg_binary-3.2.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8bd54787d894261ff48d5c4b7f23e281c05c9a5ac67355eff7d29cfbcde640cd"}, - {file = "psycopg_binary-3.2.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1ae8cf8694d01788be5f418f6cada813e2b86cef67efba9c60cb9371cee9eb9"}, - {file = "psycopg_binary-3.2.4-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0958dd3bfffbdef86594a6fa45255d4389ade94d17572bdf5207a900166a3cba"}, - {file = "psycopg_binary-3.2.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6b9558f9d101907e412ea12c355e8989c811d382d893ba6a541c091e6d916164"}, - {file = "psycopg_binary-3.2.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:279faafe9a4cdaeeee7844c19cccb865328bd55a2bf4012fef8d7040223a5245"}, - {file = "psycopg_binary-3.2.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:196d8426a9220d29c118eec6074034648267c176d220cb42c49b3c9c396f0dbc"}, - {file = "psycopg_binary-3.2.4-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:166e68b1e42862b18570d636a7b615630552daeab8b129083aa094f848be64b0"}, - {file = "psycopg_binary-3.2.4-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:b84c3f51969d33266640c218ad5bb5f8487e6a991db7a95b2c3c46fbda37a77c"}, - {file = "psycopg_binary-3.2.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:501113e4d84887c03f83c7d8886c0744fe088fd6b633b919ebf7af4f0f7186be"}, - {file = "psycopg_binary-3.2.4-cp39-cp39-win_amd64.whl", hash = "sha256:e889fe21c578c6c533c8550e1b3ba5d2cc5d151890458fa5fbfc2ca3b2324cfa"}, -] - [[package]] name = "ptyprocess" version = "0.7.0" @@ -1347,30 +1321,30 @@ tests = ["pytest"] [[package]] name = "pyasn1" -version = "0.6.1" +version = "0.6.4" description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" optional = false python-versions = ">=3.8" groups = ["main"] files = [ - {file = "pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629"}, - {file = "pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034"}, + {file = "pyasn1-0.6.4-py3-none-any.whl", hash = "sha256:deda9277cfd454080ec40b207fb6df82206a3a2688735233cdcd8d3d565f088b"}, + {file = "pyasn1-0.6.4.tar.gz", hash = "sha256:9c447d8431c947fe4c8febc4ed9e760bc29011a5b01e5c74b67025bd9fb8ce81"}, ] [[package]] name = "pyasn1-modules" -version = "0.4.1" +version = "0.4.2" description = "A collection of ASN.1-based protocols modules" optional = false python-versions = ">=3.8" groups = ["main"] files = [ - {file = "pyasn1_modules-0.4.1-py3-none-any.whl", hash = "sha256:49bfa96b45a292b711e986f222502c1c9a5e1f4e568fc30e2574a6c7d07838fd"}, - {file = "pyasn1_modules-0.4.1.tar.gz", hash = "sha256:c28e2dbf9c06ad61c71a075c7e0f9fd0f1b0bb2d2ad4377f240d33ac2ab60a7c"}, + {file = "pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a"}, + {file = "pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6"}, ] [package.dependencies] -pyasn1 = ">=0.4.6,<0.7.0" +pyasn1 = ">=0.6.1,<0.7.0" [[package]] name = "pycparser" @@ -1627,6 +1601,21 @@ files = [ [package.dependencies] pyasn1 = ">=0.1.3" +[[package]] +name = "scramp" +version = "1.4.17" +description = "An implementation of the SCRAM protocol." +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "scramp-1.4.17-py3-none-any.whl", hash = "sha256:a4e3fd2e8169461a28a13777a166d3da94274454f0714a7d3023fee124474ac8"}, + {file = "scramp-1.4.17.tar.gz", hash = "sha256:28970f29ebc33df47f9975c805e5e5a360effe5b31045e607d64b3b60370dba1"}, +] + +[package.dependencies] +asn1crypto = ">=1.5.1" + [[package]] name = "six" version = "1.17.0" @@ -1687,19 +1676,6 @@ files = [ {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, ] -[[package]] -name = "tzdata" -version = "2024.2" -description = "Provider of IANA time zone data" -optional = false -python-versions = ">=2" -groups = ["main"] -markers = "sys_platform == \"win32\"" -files = [ - {file = "tzdata-2024.2-py2.py3-none-any.whl", hash = "sha256:a48093786cdcde33cad18c2555e8532f34422074448fbc874186f0abd79565cd"}, - {file = "tzdata-2024.2.tar.gz", hash = "sha256:7d85cc416e9382e69095b7bdf4afd9e3880418a2413feec7069d533d6b4e31cc"}, -] - [[package]] name = "urllib3" version = "2.3.0" @@ -1847,4 +1823,4 @@ propcache = ">=0.2.0" [metadata] lock-version = "2.1" python-versions = "^3.12" -content-hash = "a02951a9f022362b76646aceeecc5ae110cf27bd011db6daf1ae7c55ee8ded39" +content-hash = "8cb6f0a836076505fcea7061c8bd7d6d1e29351b8dbbdcea855e9d45ed2fdd26" diff --git a/pyproject.toml b/pyproject.toml index 813f791d..8cbaa529 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,8 +33,7 @@ flatdict = "^4.1.0" croniter = "^1.3.5" minio = "^7.1.13" backoff = "^2.2.1" -psycopg-binary = "^3.1.18" -psycopg = "^3.1.18" +pg8000 = "^1.31.2" redis = "^5.0.4" bcrypt = "^4.2.1" diff --git a/setup.sh b/setup.sh new file mode 100755 index 00000000..2394801c --- /dev/null +++ b/setup.sh @@ -0,0 +1,338 @@ +#!/bin/bash +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +# Install the prerequisites required to run the operator tests. +# +# Supported platforms: macOS (Homebrew) and Ubuntu Linux / WSL2 with Ubuntu (apt). +# +# Usage: +# ./setup.sh install what is missing +# ./setup.sh --check only report what is missing, install nothing +# +set -euo pipefail + +# versions - keep aligned with the Dockerfile and .github/workflows +KUSTOMIZE_VERSION="v4.5.7" # Dockerfile +KIND_VERSION="v0.22.0" # .github/workflows/check.yml +KUBECTL_VERSION="v1.23.0" # Dockerfile +POETRY_VERSION="2.3.2" # Dockerfile ARG POETRY_VERSION +PYTHON_VERSION="3.12" # Dockerfile / pyproject.toml +TASK_VERSION="3.0.0" # minimum - Taskfile.yml uses schema version 3 + +CHECK_ONLY="false" +if [ "${1:-}" = "--check" ]; then CHECK_ONLY="true"; fi + +MISSING=0 +INSTALLED=0 + +say() { echo "==> $*"; } +ok() { echo " OK $*"; } +miss() { echo " MISSING $*"; } + +#----------------------------------------------------------------------------- +# platform detection +#----------------------------------------------------------------------------- +detect_platform() { + case "$(uname -s)" in + Darwin) echo "mac" ;; + Linux) + if [ -f /etc/os-release ] && grep -qi '^ID=ubuntu' /etc/os-release; then + echo "ubuntu" + else + echo "unsupported-linux" + fi + ;; + *) echo "unsupported" ;; + esac +} + +PLATFORM="$(detect_platform)" + +case "$PLATFORM" in + mac) + say "Detected macOS" + if ! command -v brew >/dev/null 2>&1; then + echo + echo "ERROR: Homebrew is required on macOS but was not found." + echo "Install it from https://brew.sh then run this script again:" + echo ' /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"' + exit 1 + fi + ;; + ubuntu) + say "Detected Ubuntu Linux" + ;; + unsupported-linux) + echo + echo "ERROR: this Linux distribution is not supported." + echo "The tests are supported on Ubuntu. Please use Ubuntu, or a container/VM running Ubuntu." + exit 1 + ;; + *) + echo + echo "ERROR: unsupported operating system: $(uname -s)" + echo + echo "Running the tests requires one of:" + echo " - macOS (with Homebrew)" + echo " - Ubuntu Linux (with apt)" + echo " - Windows via WSL2 with an Ubuntu distribution" + echo + echo "On Windows, install WSL2 with Ubuntu and run this script inside it:" + echo " wsl --install -d Ubuntu" + exit 1 + ;; +esac + +# WSL is Ubuntu underneath - just let the user know where they are +if [ "$PLATFORM" = "ubuntu" ] && grep -qi microsoft /proc/version 2>/dev/null; then + say "Running under WSL - make sure Docker Desktop has WSL integration enabled" +fi + +ARCH_RAW="$(uname -m)" +case "$ARCH_RAW" in + x86_64|amd64) ARCH="amd64" ;; + arm64|aarch64) ARCH="arm64" ;; + *) echo "ERROR: unsupported architecture: $ARCH_RAW"; exit 1 ;; +esac + +if [ "$PLATFORM" = "mac" ]; then OS="darwin"; else OS="linux"; fi + +mkdir -p "$HOME/.local/bin" + +#----------------------------------------------------------------------------- +# helpers +#----------------------------------------------------------------------------- +# need ; returns 0 if it must be installed +need() { + if command -v "$1" >/dev/null 2>&1; then + ok "$2 ($(command -v "$1"))" + return 1 + fi + miss "$2" + MISSING=$((MISSING + 1)) + [ "$CHECK_ONLY" = "true" ] && return 1 + return 0 +} + +# like need, but stays quiet when present so the caller can report the version +need_quiet() { + command -v "$1" >/dev/null 2>&1 && return 1 + miss "$2" + MISSING=$((MISSING + 1)) + [ "$CHECK_ONLY" = "true" ] && return 1 + return 0 +} + +installed() { INSTALLED=$((INSTALLED + 1)); echo " ...installed $1"; } + +WARNINGS=0 +warn() { WARNINGS=$((WARNINGS + 1)); echo " WARN $*"; } + +# strip a leading v and keep only the leading numeric dotted version +normver() { echo "$1" | sed 's/^v//' | grep -oE '^[0-9]+(\.[0-9]+)*' || echo "0"; } + +# vercmp A B -> prints "lt", "eq" or "gt" for A relative to B +vercmp() { + local a b first + a="$(normver "$1")"; b="$(normver "$2")" + [ "$a" = "$b" ] && { echo "eq"; return; } + first="$(printf '%s\n%s\n' "$a" "$b" | sort -V | head -1)" + if [ "$first" = "$a" ]; then echo "lt"; else echo "gt"; fi +} + +# check_version [min] +# accepts a newer version but warns; refuses an older one. +# pass "min" as the 4th argument when the expected version is only a minimum, +# in which case anything newer is fine and no warning is emitted. +check_version() { + local name="$1" found="$2" want="$3" kind="${4:-pinned}" + if [ -z "$found" ]; then + warn "$name: could not determine the version (expected $want)" + return + fi + case "$(vercmp "$found" "$want")" in + eq) ok "$name $found" ;; + gt) ok "$name $found" + if [ "$kind" != "min" ]; then + warn "$name $found is newer than the expected $want - it should work, but the CI and the Dockerfile use $want" + fi ;; + lt) if [ "$kind" = "min" ] + then warn "$name $found is older than the minimum required $want - please upgrade it" + else warn "$name $found is OLDER than the required $want - please upgrade it" + fi + MISSING=$((MISSING + 1)) ;; + esac +} + +apt_install() { + sudo apt-get update -qq + sudo apt-get install -y -qq "$@" +} + +#----------------------------------------------------------------------------- +say "Checking prerequisites" +#----------------------------------------------------------------------------- + +# --- base tools ------------------------------------------------------------- +if need curl "curl"; then + if [ "$PLATFORM" = "mac" ]; then brew install curl; else apt_install curl; fi + installed curl +fi + +if need git "git"; then + if [ "$PLATFORM" = "mac" ]; then brew install git; else apt_install git; fi + installed git +fi + +# --- task (the build tool: every other step below is run through it) --------- +if need_quiet task "task"; then + if [ "$PLATFORM" = "mac" ]; then + brew install go-task/tap/go-task + else + curl -sL https://taskfile.dev/install.sh | sh -s -- -d -b "$HOME/.local/bin" + fi + installed task + hash -r 2>/dev/null || true + if ! command -v task >/dev/null 2>&1; then + warn "task was installed in $HOME/.local/bin but is not on your PATH yet" + fi +else + check_version "task" "$(task --version 2>/dev/null | grep -oE 'v?[0-9]+(\.[0-9]+)+' | head -1)" "$TASK_VERSION" min +fi + +# --- docker (not installed automatically: it needs a desktop app / daemon) --- +if command -v docker >/dev/null 2>&1; then + ok "docker ($(command -v docker))" +else + miss "docker" + MISSING=$((MISSING + 1)) + if [ "$PLATFORM" = "mac" ]; then + echo " install Docker Desktop: https://docs.docker.com/desktop/install/mac-install/" + else + echo " install with: curl -fsSL https://get.docker.com | sh" + echo " then: sudo usermod -aG docker \$USER (log out and back in)" + fi +fi + +# --- python 3.12 ------------------------------------------------------------ +if command -v python3 >/dev/null 2>&1 || command -v "python$PYTHON_VERSION" >/dev/null 2>&1; then + PY_BIN="$(command -v "python$PYTHON_VERSION" || command -v python3)" + check_version "python" "$("$PY_BIN" -c 'import sys; print("%d.%d"%sys.version_info[:2])')" "$PYTHON_VERSION" +else + miss "python$PYTHON_VERSION" + MISSING=$((MISSING + 1)) + if [ "$CHECK_ONLY" = "false" ]; then + if [ "$PLATFORM" = "mac" ]; then + brew install "python@$PYTHON_VERSION" + else + apt_install software-properties-common + sudo add-apt-repository -y ppa:deadsnakes/ppa + apt_install "python$PYTHON_VERSION" "python$PYTHON_VERSION-venv" "python$PYTHON_VERSION-dev" + fi + installed "python$PYTHON_VERSION" + fi +fi + +# --- poetry (the python package manager used by this project) --------------- +if need_quiet poetry "poetry"; then + if [ "$PLATFORM" = "mac" ]; then + brew install poetry + else + curl -sSL https://install.python-poetry.org | "python$PYTHON_VERSION" - + echo " note: ensure \$HOME/.local/bin is in your PATH" + fi + installed poetry +else + command -v poetry >/dev/null 2>&1 && \ + check_version "poetry" "$(poetry --version 2>/dev/null | grep -oE '[0-9]+(\.[0-9]+)+' | head -1)" "$POETRY_VERSION" +fi + +# --- kubectl ---------------------------------------------------------------- +if need_quiet kubectl "kubectl"; then + if [ "$PLATFORM" = "mac" ]; then + brew install kubectl + else + curl -sL "https://dl.k8s.io/release/$KUBECTL_VERSION/bin/$OS/$ARCH/kubectl" \ + -o "$HOME/.local/bin/kubectl" + chmod +x "$HOME/.local/bin/kubectl" + fi + installed kubectl +else + check_version "kubectl" "$(kubectl version --client 2>/dev/null | grep -oE 'v?[0-9]+(\.[0-9]+)+' | head -1)" "$KUBECTL_VERSION" +fi + +# --- kustomize -------------------------------------------------------------- +if need_quiet kustomize "kustomize"; then + if [ "$PLATFORM" = "mac" ]; then + brew install kustomize + else + URL="https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize%2F$KUSTOMIZE_VERSION/kustomize_${KUSTOMIZE_VERSION}_${OS}_${ARCH}.tar.gz" + curl -sL "$URL" | tar xzf - -C "$HOME/.local/bin" + chmod +x "$HOME/.local/bin/kustomize" + fi + installed kustomize +else + check_version "kustomize" "$(kustomize version 2>/dev/null | grep -oE 'v?[0-9]+(\.[0-9]+)+' | head -1)" "$KUSTOMIZE_VERSION" +fi + +# --- kind ------------------------------------------------------------------- +if need_quiet kind "kind"; then + if [ "$PLATFORM" = "mac" ]; then + brew install kind + else + curl -sLo "$HOME/.local/bin/kind" \ + "https://kind.sigs.k8s.io/dl/$KIND_VERSION/kind-${OS}-${ARCH}" + chmod +x "$HOME/.local/bin/kind" + fi + installed kind +else + check_version "kind" "$(kind version 2>/dev/null | grep -oE 'v?[0-9]+(\.[0-9]+)+' | head -1)" "$KIND_VERSION" +fi + +#----------------------------------------------------------------------------- +# summary +#----------------------------------------------------------------------------- +echo +if [ "$CHECK_ONLY" = "true" ]; then + if [ "$MISSING" -eq 0 ]; then + if [ "$WARNINGS" -gt 0 ] + then say "All prerequisites are installed ($WARNINGS version warning(s))." + else say "All prerequisites are installed." + fi + else + say "$MISSING prerequisite(s) missing - run ./setup.sh to install them." + exit 1 + fi + exit 0 +fi + +case ":$PATH:" in + *":$HOME/.local/bin:"*) ;; + *) echo "NOTE: add \$HOME/.local/bin to your PATH:" + echo " echo 'export PATH=\$HOME/.local/bin:\$PATH' >> ~/.bashrc" ;; +esac + +if [ "$WARNINGS" -gt 0 ]; then + say "Setup complete ($INSTALLED installed, $WARNINGS warning(s))." +else + say "Setup complete ($INSTALLED installed)." +fi +echo +echo "Next steps:" +echo " task setup install the python dependencies" +echo " task utest run the unit tests" diff --git a/tests/kind/whisk-slim.yaml b/tests/kind/whisk-slim.yaml index 565be5c8..352eafcc 100644 --- a/tests/kind/whisk-slim.yaml +++ b/tests/kind/whisk-slim.yaml @@ -67,8 +67,6 @@ spec: slim: true affinity: false tolerations: false - preload: - only-apache: true couchdb: host: couchdb port: 5984