diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 8e9462525bd..f712edc7fe9 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,8 +1,9 @@ **What this PR does / why we need it**: diff --git a/.github/actions/kserve-dep-setup/action.yml b/.github/actions/kserve-dep-setup/action.yml index 37a08dd830f..e01c200a837 100644 --- a/.github/actions/kserve-dep-setup/action.yml +++ b/.github/actions/kserve-dep-setup/action.yml @@ -17,22 +17,14 @@ runs: run: | shopt -s nocasematch - if [[ "${{ inputs.network-layer }}" == "istio" ]]; then - echo "Selected network layer ${{ inputs.network-layer }}" - ./test/scripts/gh-actions/setup-deps.sh ${{ inputs.deployment-mode }} - - kubectl get pods -n istio-system - kubectl describe svc -n istio-system istio-ingressgateway - elif [[ "${{ inputs.network-layer }}" == "kourier" ]]; then - echo "Selected network layer ${{ inputs.network-layer }}" - ./test/scripts/gh-actions/setup-kourier.sh + if [[ "${{ inputs.network-layer }}" == "kourier" ]]; then + echo "Selected network layer ${{ inputs.network-layer }}" + ./test/scripts/gh-actions/setup-kourier.sh else - echo "Unsupported network layer" + echo "Selected network layer ${{ inputs.network-layer }}" + ./test/scripts/gh-actions/setup-deps.sh ${{ inputs.deployment-mode }} "${{ inputs.network-layer }}" fi - kubectl get pods -n knative-serving - kubectl get pods -n cert-manager - - name: Update test overlays shell: bash run: | diff --git a/.github/actions/minikube-setup/action.yml b/.github/actions/minikube-setup/action.yml index 5788a4f022a..188375b251e 100644 --- a/.github/actions/minikube-setup/action.yml +++ b/.github/actions/minikube-setup/action.yml @@ -1,22 +1,38 @@ name: 'Minikube setup action' description: 'Sets up minikube on the github runner' +inputs: + nodes: + description: 'Number of nodes to start minikube with' + required: false + default: '1' + driver: + description: 'Driver to use for minikube' + required: false + default: 'none' + start-args: + description: 'Additional arguments to pass to minikube start' + required: false + default: '' + runs: using: "composite" steps: - name: Install kubectl uses: azure/setup-kubectl@v4.0.0 with: - version: 'v1.29.7' + version: 'v1.30.7' - name: Setup Minikube uses: medyagh/setup-minikube@latest with: minikube-version: '1.33.1' - kubernetes-version: 'v1.29.7' - driver: 'none' + kubernetes-version: 'v1.30.7' + driver: ${{ inputs.driver }} wait: 'all' - start-args: --wait-timeout=6m0s + cpus: 'max' + memory: 'max' + start-args: --wait-timeout=6m0s --nodes=${{ inputs.nodes }} ${{ inputs.start-args }} - name: Check Kubernetes pods shell: bash diff --git a/.github/workflows/artexplainer-docker-publish.yml b/.github/workflows/artexplainer-docker-publish.yml index 544a16c6ba5..3fdf272ba77 100644 --- a/.github/workflows/artexplainer-docker-publish.yml +++ b/.github/workflows/artexplainer-docker-publish.yml @@ -39,7 +39,7 @@ jobs: - name: Run tests uses: docker/build-push-action@v5 with: - platforms: linux/amd64, linux/arm64/v8 + platforms: linux/amd64 context: python file: python/artexplainer.Dockerfile push: false @@ -93,7 +93,7 @@ jobs: - name: Build and push uses: docker/build-push-action@v5 with: - platforms: linux/amd64, linux/arm64/v8 + platforms: linux/amd64 context: python file: python/artexplainer.Dockerfile push: true diff --git a/.github/workflows/automated-release.yml b/.github/workflows/automated-release.yml index b5f76a9c617..7de513f7025 100644 --- a/.github/workflows/automated-release.yml +++ b/.github/workflows/automated-release.yml @@ -3,27 +3,27 @@ on: workflow_dispatch: inputs: releaseBranch: - description: 'The existing branch name to release from, e.g. release-0.12' + description: "The existing branch name to release from, e.g. release-0.12" required: true releaseTag: - description: 'The release tag, e.g. v0.12.0-rc1' + description: "The release tag, e.g. v0.12.0-rc1" required: true jobs: prepare-release: runs-on: ubuntu-latest steps: - - name: Setup Go - uses: actions/setup-go@v5 - with: - go-version: 1.22 - - name: Checkout source code uses: actions/checkout@v4 with: fetch-depth: 0 ref: ${{ inputs.releaseBranch }} + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + - name: Install dependencies run: | go mod download @@ -39,12 +39,12 @@ jobs: cd $KSERVE_PATH export RELEASE_BRANCH=${{ inputs.releaseBranch }} export RELEASE_TAG=${{ inputs.releaseTag }} - + # Bump Versions make bump-version ./hack/generate-install.sh $RELEASE_TAG ./hack/python-release.sh - + # Update Release Branch and Push Tag git diff git config --global user.email "terrytangyuan@gmail.com" diff --git a/.github/workflows/e2e-test.yml b/.github/workflows/e2e-test.yml index 855eb7ea3be..7c1a3fdccb6 100644 --- a/.github/workflows/e2e-test.yml +++ b/.github/workflows/e2e-test.yml @@ -24,6 +24,7 @@ env: # Controller images CONTROLLER_IMG: "kserve-controller" LOCALMODEL_CONTROLLER_IMG: "kserve-localmodel-controller" + LOCALMODEL_AGENT_IMG: "kserve-localmodelnode-agent" STORAGE_INIT_IMG: "storage-initializer" AGENT_IMG: "agent" ROUTER_IMG: "router" @@ -78,7 +79,7 @@ jobs: path: ${{ env.DOCKER_IMAGES_PATH }}/${{ env.CONTROLLER_IMG }}-${{ github.sha }} compression-level: 0 if-no-files-found: error - + - name: Upload localmodel controller image uses: actions/upload-artifact@v4 with: @@ -87,6 +88,14 @@ jobs: compression-level: 0 if-no-files-found: error + - name: Upload localmodel agent image + uses: actions/upload-artifact@v4 + with: + name: ${{ env.BASE_ARTIFACT_PREFIX }}-${{ env.LOCALMODEL_AGENT_IMG }}-${{ github.sha }} + path: ${{ env.DOCKER_IMAGES_PATH }}/${{ env.LOCALMODEL_AGENT_IMG }}-${{ github.sha }} + compression-level: 0 + if-no-files-found: error + - name: Upload agent image uses: actions/upload-artifact@v4 with: @@ -286,11 +295,7 @@ jobs: test-predictor: runs-on: ubuntu-22.04 - needs: - [ - kserve-image-build, - predictor-runtime-build, - ] + needs: [kserve-image-build, predictor-runtime-build] steps: - name: Checkout uses: actions/checkout@v4 @@ -301,7 +306,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: "1.22" + go-version-file: go.mod - name: Setup Python uses: actions/setup-python@v5 @@ -363,8 +368,8 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: "1.22" - + go-version-file: go.mod + - name: Setup Python uses: actions/setup-python@v5 with: @@ -415,7 +420,7 @@ jobs: - name: Install KServe run: | ./test/scripts/gh-actions/setup-kserve.sh - + kubectl get pods -n kserve kubectl describe pods -n kserve @@ -432,11 +437,7 @@ jobs: test-graph: runs-on: ubuntu-22.04 needs: - [ - kserve-image-build, - predictor-runtime-build, - graph-tests-images-build, - ] + [kserve-image-build, predictor-runtime-build, graph-tests-images-build] steps: - name: Checkout uses: actions/checkout@v4 @@ -444,7 +445,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: "1.22" + go-version-file: go.mod - name: Setup Python uses: actions/setup-python@v5 @@ -507,15 +508,10 @@ jobs: run: | ./test/scripts/gh-actions/status-check.sh - test-path-based-routing: runs-on: ubuntu-22.04 needs: - [ - kserve-image-build, - predictor-runtime-build, - explainer-runtime-build - ] + [kserve-image-build, predictor-runtime-build, explainer-runtime-build] steps: - name: Checkout uses: actions/checkout@v4 @@ -523,7 +519,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: "1.22" + go-version-file: go.mod - name: Setup Python uses: actions/setup-python@v5 @@ -589,7 +585,7 @@ jobs: - name: Patch inferenceservice config run: | - kubectl patch configmaps -n kserve inferenceservice-config --patch-file config/overlays/test/configmap/inferenceservice-ingress.yaml + kubectl patch configmaps -n kserve inferenceservice-config --patch-file config/overlays/test/configmap/inferenceservice-path-template.yaml kubectl describe configmaps -n kserve inferenceservice-config - name: Run E2E tests with path-based routing @@ -615,7 +611,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: "1.22" + go-version-file: go.mod - name: Setup Python uses: actions/setup-python@v5 with: @@ -669,8 +665,7 @@ jobs: test-with-helm: runs-on: ubuntu-22.04 - needs: - [kserve-image-build] + needs: [kserve-image-build] steps: - name: Checkout source uses: actions/checkout@v4 @@ -678,7 +673,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: "1.22" + go-version-file: go.mod - name: Setup Python uses: actions/setup-python@v5 @@ -702,7 +697,6 @@ jobs: - name: Install Kserve from helm run: | - ./test/scripts/gh-actions/setup-modelmesh-dep.sh ./test/scripts/gh-actions/setup-kserve-helm.sh kubectl get pods -n kserve kubectl describe pods -n kserve @@ -720,8 +714,13 @@ jobs: test-raw: runs-on: ubuntu-22.04 + strategy: + fail-fast: false + matrix: + # Test with Ingress and Gateway API + network-layer: ["istio-ingress", "envoy-gatewayapi", "istio-gatewayapi"] needs: - [kserve-image-build, predictor-runtime-build] + [kserve-image-build, predictor-runtime-build, explainer-runtime-build] steps: - name: Checkout source uses: actions/checkout@v4 @@ -732,7 +731,8 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: "1.22" + go-version-file: go.mod + - name: Setup Python uses: actions/setup-python@v5 with: @@ -745,6 +745,7 @@ jobs: uses: ./.github/actions/kserve-dep-setup with: deployment-mode: "raw" + network-layer: ${{ matrix.network-layer }} - name: Download base images uses: ./.github/actions/base-download @@ -767,11 +768,12 @@ jobs: name: ${{ env.PREDICTOR_ARTIFACT_PREFIX }}-${{ env.CUSTOM_MODEL_GRPC_IMG }}-${{ github.sha }} path: ./tmp - - name: Download transformer image + - name: Download transformer and explainer artifacts uses: actions/download-artifact@v4 with: - name: ${{ env.TRANSFORMER_ARTIFACT_PREFIX }}-${{ env.IMAGE_TRANSFORMER_IMG }}-${{ github.sha }} path: ./tmp + pattern: +(${{ env.TRANSFORMER_ARTIFACT_PREFIX }}|${{ env.EXPLAINER_ARTIFACT_PREFIX }})-* + merge-multiple: true - name: Load docker images uses: ./.github/actions/load-docker-images @@ -783,7 +785,7 @@ jobs: - name: Install KServe run: | - ./test/scripts/gh-actions/setup-kserve.sh "raw" + ./test/scripts/gh-actions/setup-kserve.sh "raw" ${{ matrix.network-layer }} kubectl get pods -n kserve kubectl describe pods -n kserve @@ -792,10 +794,27 @@ jobs: run: | kubectl describe configmaps -n kserve inferenceservice-config + - name: Enable Gateway API + if: matrix.network-layer == 'envoy-gatewayapi' || matrix.network-layer == 'istio-gatewayapi' + run: | + kubectl patch configmaps -n kserve inferenceservice-config --patch-file config/overlays/test/configmap/inferenceservice-enable-gateway-api.yaml + - name: Run E2E tests timeout-minutes: 30 run: | - ./test/scripts/gh-actions/run-e2e-tests.sh "raw" "6" + ./test/scripts/gh-actions/run-e2e-tests.sh "raw" "6" ${{ matrix.network-layer }} + + - name: Patch inferenceservice config for path based routing + if: matrix.network-layer == 'envoy-gatewayapi' || matrix.network-layer == 'istio-gatewayapi' + run: | + kubectl patch configmaps -n kserve inferenceservice-config --patch-file config/overlays/test/configmap/inferenceservice-path-template.yaml + kubectl describe configmaps -n kserve inferenceservice-config + + - name: Run E2E tests with path based routing + if: matrix.network-layer == 'envoy-gatewayapi' || matrix.network-layer == 'istio-gatewayapi' + timeout-minutes: 30 + run: | + ./test/scripts/gh-actions/run-e2e-tests.sh "raw" "6" ${{ matrix.network-layer }} - name: Patch inferenceservice config for cluster ip none run: | @@ -805,7 +824,7 @@ jobs: - name: Run E2E tests - cluster ip none timeout-minutes: 30 run: | - ./test/scripts/gh-actions/run-e2e-tests.sh "rawcipn" "1" + ./test/scripts/gh-actions/run-e2e-tests.sh "rawcipn" "1" ${{ matrix.network-layer }} - name: Check system status if: always() @@ -826,7 +845,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: "1.22" + go-version-file: go.mod - name: Setup Python uses: actions/setup-python@v5 @@ -899,8 +918,7 @@ jobs: test-llm: runs-on: ubuntu-22.04 - needs: - [ kserve-image-build, predictor-runtime-build] + needs: [kserve-image-build, predictor-runtime-build] steps: - name: Checkout source uses: actions/checkout@v4 @@ -911,7 +929,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: "1.22" + go-version-file: go.mod - name: Setup Python uses: actions/setup-python@v5 with: @@ -959,8 +977,7 @@ jobs: test-huggingface-server-vllm: runs-on: ubuntu-22.04 - needs: - [ kserve-image-build, predictor-runtime-build] + needs: [kserve-image-build, predictor-runtime-build] steps: - name: Checkout source uses: actions/checkout@v4 @@ -971,7 +988,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: "1.22" + go-version-file: go.mod - name: Setup Python uses: actions/setup-python@v5 with: @@ -1015,4 +1032,128 @@ jobs: - name: Check system status if: always() run: | - ./test/scripts/gh-actions/status-check.sh \ No newline at end of file + ./test/scripts/gh-actions/status-check.sh + + test-modelcache: + runs-on: ubuntu-22.04 + needs: [kserve-image-build, predictor-runtime-build] + steps: + - name: Checkout source + uses: actions/checkout@v4 + + - name: Free-up disk space + uses: ./.github/actions/free-up-disk-space + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: "3.9" + + - name: Create tmp-images directory + run: | + sudo mkdir -p /tmp-images + sudo chown -R $USER /tmp-images + + - name: Setup Minikube + uses: ./.github/actions/minikube-setup + with: + nodes: 3 + driver: "docker" + start-args: "--mount --mount-string=/tmp-images:/tmp-images" + + - name: Create minikube tunnel + run: | + nohup minikube tunnel > minikube-tunnel.log 2>&1 & + + - name: KServe dependency setup + uses: ./.github/actions/kserve-dep-setup + + - name: Download base images + uses: actions/download-artifact@v4 + with: + path: /tmp-images + pattern: ${{ env.BASE_ARTIFACT_PREFIX }}-* + merge-multiple: true + + - name: Load base docker images + run: | + ls -l /tmp-images + minikube ssh -n minikube -- ls -l /tmp-images + files=$(find /tmp-images -maxdepth 1 -type f) + for file in ${files[@]};do + echo "Loading image $(basename ${file})" + minikube ssh -n minikube -- docker image load -i ${file} + minikube ssh -n minikube-m02 -- docker image load -i ${file} + minikube ssh -n minikube-m03 -- docker image load -i ${file} + done + sudo rm -rf /tmp-images/* + minikube ssh -n minikube -- docker image ls + minikube ssh -n minikube-m02 -- docker image ls + minikube ssh -n minikube-m03 -- docker image ls + + - name: Download huggingface server image + uses: actions/download-artifact@v4 + with: + name: ${{ env.HUGGINGFACE_IMG }}-${{ github.sha }} + path: /tmp-images + + - name: Load runtime docker images + run: | + files=$(find /tmp-images -maxdepth 1 -type f) + for file in ${files[@]};do + echo "Loading image $(basename ${file})" + minikube ssh -n minikube-m02 -- docker image load -i ${file} + done + sudo rm -rf tmp-images/* + minikube ssh -n minikube-m02 -- docker image ls + + - name: Create model root directory + run: | + minikube ssh -n minikube-m02 -- sudo mkdir -p -m=777 /models + minikube ssh -n minikube-m03 -- sudo mkdir -p -m=777 /models + + - name: Install Poetry and version plugin + run: ./test/scripts/gh-actions/setup-poetry.sh + + - name: Install KServe + run: | + ./test/scripts/gh-actions/setup-kserve.sh + + kubectl get pods -n kserve + kubectl describe pods -n kserve + + - name: Enable modelcache + run: | + sed -i -e "s/latest/${GITHUB_SHA}/g" config/overlays/test/configmap/inferenceservice-enable-modelcache.yaml + kubectl patch configmaps -n kserve inferenceservice-config --patch-file config/overlays/test/configmap/inferenceservice-enable-modelcache.yaml + kubectl describe configmaps -n kserve inferenceservice-config + + - name: Create localmodel job namespace + run: | + kubectl create ns kserve-localmodel-jobs + + - name: Label worker nodes for modelcache + run: | + kubectl label nodes -l '!node-role.kubernetes.io/control-plane' kserve/localmodel=worker + + - name: Enable nodeselector in knative + run: | + kubectl patch configmaps -n knative-serving config-features --patch '{"data": {"kubernetes.podspec-nodeselector": "enabled"}}' + + - name: Run E2E tests + timeout-minutes: 15 + run: | + ./test/scripts/gh-actions/run-e2e-tests.sh "modelcache" "1" + + - name: Check system status + if: always() + run: | + echo "::group::Minikube tunnel logs" + cat minikube-tunnel.log + echo "::endgroup::" + ./test/scripts/gh-actions/status-check.sh diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 1d86539f087..c196bd486a7 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -16,15 +16,15 @@ jobs: name: Build runs-on: ubuntu-latest steps: + - name: Check out code into the Go module directory + uses: actions/checkout@v4 + - name: Set up Go 1.x uses: actions/setup-go@v5 with: - go-version: "1.22" + go-version-file: go.mod id: go - - name: Check out code into the Go module directory - uses: actions/checkout@v4 - - name: Get dependencies run: | go get -v -t -d ./... @@ -53,4 +53,4 @@ jobs: filename: coverage.json label: coverage message: ${{ steps.test.outputs.coverage }} - color: green \ No newline at end of file + color: green diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index f44d41359b8..d602d02e2ad 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -18,11 +18,47 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: - go-version: '1.22' + go-version-file: go.mod cache: false - name: golangci-lint - uses: golangci/golangci-lint-action@v4 + uses: golangci/golangci-lint-action@v6 with: - version: v1.56 + version: v1.63 args: --out-format=line-number + verify-go-mod: + runs-on: ubuntu-latest + steps: + - name: Checkout source code + uses: actions/checkout@v4 + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + cache: false + + - name: Run go mod tidy + run: | + go mod tidy + + - name: Verify + shell: bash + run: | + # From: https://backreference.org/2009/12/23/how-to-match-newlines-in-sed/ + # This is to leverage this workaround: + # https://github.com/actions/toolkit/issues/193#issuecomment-605394935 + urlencode() { + sed ':begin;$!N;s/\n/%0A/;tbegin' + } + + if [ -z "$(git status --porcelain)" ]; then + echo "${{ github.repository }} up to date." + else + echo "Found diffs in: $(git diff-index --name-only HEAD)" + for x in $(git diff-index --name-only HEAD); do + echo "::error file=$x::Please run 'go mod tidy'.%0A$(git diff $x | urlencode)" + done + echo "${{ github.repository }} is out of date. Please run 'go mod tidy'" + exit 1 + fi diff --git a/.github/workflows/huggingface-vllm-docker-publish-manual.yml b/.github/workflows/huggingface-vllm-docker-publish-manual.yml new file mode 100644 index 00000000000..2255e5a0bd6 --- /dev/null +++ b/.github/workflows/huggingface-vllm-docker-publish-manual.yml @@ -0,0 +1,70 @@ +name: Huggingface vLLM Docker Publisher + +on: + workflow_dispatch: + inputs: + version: + description: 'Huggingface vLLM image version to publish' + required: true + +env: + IMAGE_NAME: huggingfaceserver + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + push: + strategy: + fail-fast: false + matrix: + image: + - version: ${{ inputs.version }} + path: 'python/huggingface_server_cpu_openvino.Dockerfile' + - version: ${{ inputs.version }}-gpu + path: 'python/huggingface_server.Dockerfile' + runs-on: ubuntu-latest + steps: + - name: Checkout source + uses: actions/checkout@v4 + + - name: Free-up disk space + uses: ./.github/actions/free-up-disk-space + + - name: Setup Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to DockerHub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USER }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Export image id and version variable + run: | + IMAGE_ID=kserve/$IMAGE_NAME + + # Change all uppercase to lowercase + IMAGE_ID=$(echo $IMAGE_ID | tr '[A-Z]' '[a-z]') + + # Add prefix v to version if it doesn't start with v + if [[ ${{ matrix.image.version }} != v* ]]; then + VERSION="v${{ matrix.image.version }}" + else + VERSION="${{ matrix.image.version }}" + fi + + echo IMAGE_ID=$IMAGE_ID >> $GITHUB_ENV + echo VERSION=$VERSION >> $GITHUB_ENV + + - name: Build and push + uses: docker/build-push-action@v5 + with: + platforms: linux/amd64 + context: python + file: ${{ matrix.image.path }} + push: true + tags: ${{ env.IMAGE_ID }}:${{ env.VERSION }} + # https://github.com/docker/buildx/issues/1533 + provenance: false diff --git a/.github/workflows/kserve-localmodel-agent-docker-publish.yml b/.github/workflows/kserve-localmodel-agent-docker-publish.yml new file mode 100644 index 00000000000..9b09350efdb --- /dev/null +++ b/.github/workflows/kserve-localmodel-agent-docker-publish.yml @@ -0,0 +1,95 @@ +name: Kserve localmodel agent Docker Publisher + +on: + push: + # Publish `master` as Docker `latest` image. + branches: + - master + + # Publish `v1.2.3` tags as releases. + tags: + - v* + + # Run tests for any PRs. + pull_request: + +env: + IMAGE_NAME: kserve-localmodelnode-agent + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + # Run tests. + # See also https://docs.docker.com/docker-hub/builds/automated-testing/ + test: + runs-on: ubuntu-latest + + steps: + - name: Checkout source + uses: actions/checkout@v4 + + - name: Run tests + run: | + if [ -f docker-compose.test.yml ]; then + docker-compose --file docker-compose.test.yml build + docker-compose --file docker-compose.test.yml run sut + else + docker buildx build . --file localmodel-agent.Dockerfile + fi + + # Push image to GitHub Packages. + # See also https://docs.docker.com/docker-hub/builds/ + push: + # Ensure test job passes before pushing image. + needs: test + + runs-on: ubuntu-latest + if: github.event_name == 'push' + + steps: + - name: Checkout source + uses: actions/checkout@v4 + + - name: Setup QEMU + uses: docker/setup-qemu-action@v3 + + - name: Setup Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to DockerHub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USER }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: export version variable + run: | + IMAGE_ID=kserve/$IMAGE_NAME + + # Change all uppercase to lowercase + IMAGE_ID=$(echo $IMAGE_ID | tr '[A-Z]' '[a-z]') + + # Strip git ref prefix from version + VERSION=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,') + + # Strip "v" prefix from tag name + # [[ "${{ github.ref }}" == "refs/tags/"* ]] && VERSION=$(echo $VERSION | sed -e 's/^v//') + + # Use Docker `latest` tag convention + [ "$VERSION" == "master" ] && VERSION=latest + + echo VERSION=$VERSION >> $GITHUB_ENV + echo IMAGE_ID=$IMAGE_ID >> $GITHUB_ENV + + - name: Build and push + uses: docker/build-push-action@v5 + with: + platforms: linux/amd64,linux/arm/v7,linux/arm64/v8,linux/ppc64le,linux/s390x + context: . + file: localmodel-agent.Dockerfile + push: true + tags: ${{ env.IMAGE_ID }}:${{ env.VERSION }} + # https://github.com/docker/buildx/issues/1533 + provenance: false diff --git a/.github/workflows/lightgbm-docker-publish.yml b/.github/workflows/lightgbm-docker-publish.yml index c5061d8ab13..6e23265c82b 100644 --- a/.github/workflows/lightgbm-docker-publish.yml +++ b/.github/workflows/lightgbm-docker-publish.yml @@ -39,7 +39,7 @@ jobs: - name: Run tests uses: docker/build-push-action@v5 with: - platforms: linux/amd64, linux/arm64/v8 + platforms: linux/amd64 context: python file: python/lgb.Dockerfile push: false @@ -93,7 +93,7 @@ jobs: - name: Build and push uses: docker/build-push-action@v5 with: - platforms: linux/amd64, linux/arm64/v8 + platforms: linux/amd64 context: python file: python/lgb.Dockerfile push: true diff --git a/.github/workflows/paddle-docker-publish.yml b/.github/workflows/paddle-docker-publish.yml index 767570c9017..3bee2ae633b 100644 --- a/.github/workflows/paddle-docker-publish.yml +++ b/.github/workflows/paddle-docker-publish.yml @@ -39,7 +39,7 @@ jobs: - name: Run tests uses: docker/build-push-action@v5 with: - platforms: linux/amd64, linux/arm64/v8 + platforms: linux/amd64 context: python file: python/paddle.Dockerfile push: false @@ -93,7 +93,7 @@ jobs: - name: Build and push uses: docker/build-push-action@v5 with: - platforms: linux/amd64, linux/arm64/v8 + platforms: linux/amd64 context: python file: python/paddle.Dockerfile push: true diff --git a/.github/workflows/pmml-docker-publish.yml b/.github/workflows/pmml-docker-publish.yml index 2ba04764b7d..983a88344ce 100644 --- a/.github/workflows/pmml-docker-publish.yml +++ b/.github/workflows/pmml-docker-publish.yml @@ -39,7 +39,7 @@ jobs: - name: Run tests uses: docker/build-push-action@v5 with: - platforms: linux/amd64, linux/arm64/v8 + platforms: linux/amd64 context: python file: python/pmml.Dockerfile push: false @@ -93,7 +93,7 @@ jobs: - name: Build and push uses: docker/build-push-action@v5 with: - platforms: linux/amd64, linux/arm64/v8 + platforms: linux/amd64 context: python file: python/pmml.Dockerfile push: true diff --git a/.github/workflows/scheduled-image-scan.yml b/.github/workflows/scheduled-image-scan.yml index 721baecd63d..83385d4a5ce 100644 --- a/.github/workflows/scheduled-image-scan.yml +++ b/.github/workflows/scheduled-image-scan.yml @@ -19,12 +19,10 @@ jobs: [ { name: kserve-controller, file: Dockerfile }, { name: agent, file: agent.Dockerfile }, - { - name: storage-initializer, - file: python/storage-initializer.Dockerfile, - }, + { name: storage-initializer, file: python/storage-initializer.Dockerfile }, { name: router, file: router.Dockerfile }, { name: kserve-localmodel-controller, file: localmodel.Dockerfile }, + { name: kserve-localmodelnode-agent, file: localmodel-agent.Dockerfile }, ] steps: @@ -112,10 +110,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - image: - [ - { name: art-explainer, file: python/artexplainer.Dockerfile }, - ] + image: [{ name: art-explainer, file: python/artexplainer.Dockerfile }] steps: - name: Checkout diff --git a/.github/workflows/sklearnserver-docker-publish.yml b/.github/workflows/sklearnserver-docker-publish.yml index 46797c4e3c8..ff7d0ccf366 100644 --- a/.github/workflows/sklearnserver-docker-publish.yml +++ b/.github/workflows/sklearnserver-docker-publish.yml @@ -39,7 +39,7 @@ jobs: - name: Run tests uses: docker/build-push-action@v5 with: - platforms: linux/amd64, linux/arm64/v8 + platforms: linux/amd64 context: python file: python/sklearn.Dockerfile push: false @@ -93,7 +93,7 @@ jobs: - name: Build and push uses: docker/build-push-action@v5 with: - platforms: linux/amd64, linux/arm64/v8 + platforms: linux/amd64 context: python file: python/sklearn.Dockerfile push: true diff --git a/.github/workflows/storage-initializer-docker-publisher.yml b/.github/workflows/storage-initializer-docker-publisher.yml index d9d01ce156b..8a136e664f1 100644 --- a/.github/workflows/storage-initializer-docker-publisher.yml +++ b/.github/workflows/storage-initializer-docker-publisher.yml @@ -39,7 +39,7 @@ jobs: - name: Run tests uses: docker/build-push-action@v5 with: - platforms: linux/amd64, linux/arm64/v8 + platforms: linux/amd64 context: python file: python/storage-initializer.Dockerfile push: false @@ -93,7 +93,7 @@ jobs: - name: Build and push uses: docker/build-push-action@v5 with: - platforms: linux/amd64, linux/arm64/v8 + platforms: linux/amd64 context: python file: python/storage-initializer.Dockerfile push: true diff --git a/.github/workflows/verify-codegen.yml b/.github/workflows/verify-codegen.yml index 4845ebb2df6..2625975a062 100644 --- a/.github/workflows/verify-codegen.yml +++ b/.github/workflows/verify-codegen.yml @@ -17,12 +17,12 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: 1.22 + go-version-file: go.mod - name: Install dependencies run: | go mod download - + - name: Update Codegen shell: bash run: | @@ -38,19 +38,19 @@ jobs: - name: Verify shell: bash run: | - cd $KSERVE_PATH - + cd $KSERVE_PATH + # From: https://backreference.org/2009/12/23/how-to-match-newlines-in-sed/ # This is to leverage this workaround: # https://github.com/actions/toolkit/issues/193#issuecomment-605394935 urlencode() { sed ':begin;$!N;s/\n/%0A/;tbegin' } - + # When running make generate, it introduces some lint errors which is fixed manually. # Reset these files to avoid false triggers due to those changes. git checkout -- python/kserve/test/__init__.py python/kserve/kserve/api_client.py - + if [ -z "$(git status --porcelain ./pkg ./python ./config ./charts)" ]; then echo "${{ github.repository }} up to date." else diff --git a/.github/workflows/xgbserver-docker-publisher.yml b/.github/workflows/xgbserver-docker-publisher.yml index 0e55ffb4330..c467f6af956 100644 --- a/.github/workflows/xgbserver-docker-publisher.yml +++ b/.github/workflows/xgbserver-docker-publisher.yml @@ -39,7 +39,7 @@ jobs: - name: Run tests uses: docker/build-push-action@v5 with: - platforms: linux/amd64, linux/arm64/v8 + platforms: linux/amd64 context: python file: python/xgb.Dockerfile push: false @@ -93,7 +93,7 @@ jobs: - name: Build and push uses: docker/build-push-action@v5 with: - platforms: linux/amd64, linux/arm64/v8 + platforms: linux/amd64 context: python file: python/xgb.Dockerfile push: true diff --git a/.golangci.yml b/.golangci.yml index 28a9ef790b5..22cf21cafbc 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,22 +1,24 @@ run: timeout: 6m - go: "1.22" - tests: false - skip-dirs: # skip auto gen folders - - "client/" - - "clientv1alpha1/" - - "tools/tf2openapi/generated/" + go: "1.23" + tests: true issues: max-same-issues: 0 max-issues-per-linter: 0 - + uniq-by-line: true + exclude-dirs: # skip auto gen folders + - "client/*" + - "tools/tf2openapi/generated/*" + - "openapi/" + exclude-files: + - "zz_generated.*.go" output: - format: "github-actions,line-number" + formats: + - format: "colored-line-number" print-issued-lines: true print-linter-name: true - uniq-by-line: true path-prefix: "" sort-results: true @@ -24,32 +26,175 @@ output: linters: disable-all: true enable: - - errorlint + # Check for pass []any as any in variadic func(...any). + - asasalint + + # Dangerous unicode characters + - bidichk + + # Checks whether HTTP response body is closed successfully. - bodyclose + + # Canonicalheader checks whether net/http.Header uses canonical header. + - canonicalheader + + # Containedctx is a linter that detects struct contained context.Context + # field. + - containedctx + + # Check whether the function uses a non-inherited context. + - contextcheck + + # Copyloopvar is a linter detects places where loop variables are copied. + - copyloopvar + + # Check declaration order of types, consts, vars and funcs. + - decorder + + # Checks assignments with too many blank identifiers (e.g. x, , , _, := f()). - dogsled - - exportloopref + + # Checks for duplicate words in the source code. + - dupword + + # Check for two durations multiplied together. + - durationcheck + + # Checks that sentinel errors are prefixed with the Err- and error types + # are suffixed with the -Error. + - errname + + # Errorlint is a linter for that can be used to find code that will cause + # problems with the error wrapping scheme introduced in Go 1.13. + - errorlint + + # Detects functions from golang.org/x/exp/ that can be replaced by std functions. + - exptostd + + # Detects nested contexts in loops. https://gabnotes.org/fat-contexts/ + - fatcontext + + # Enforces standards of using ginkgo and gomega. + - ginkgolinter + + # Provides diagnostics that check for bugs, performance and style issues. + # Extensible without recompilation through dynamic rules. + # Dynamic rules are written declaratively with AST patterns, filters, + # report message and optional suggestion. - gocritic + + # Gofmt checks whether code was gofmt-ed. By default this tool runs + # with -s option to check for code simplification. + - gofmt + + # Gofumpt checks whether code was gofumpt-ed. + - gofumpt + + # Check import statements are formatted according to the 'goimport' + # command. Reformat imports in autofix mode. - goimports + + # Inspects source code for security problems. - gosec + + # Linter that specializes in simplifying code. - gosimple + + # Vet examines Go source code and reports suspicious constructs. + # It is roughly the same as 'go vet' and uses its passes. - govet + + # Enforces consistent import aliases. + - importas + + # Detects when assignments to existing variables are not used. - ineffassign + + # Intrange is a linter to find places where for loops could make use of + # an integer range. + - intrange + + # Checks key value pairs for common logger libraries (kitlog,klog,logr,zap). + - loggercheck + + # Finds slice declarations with non-zero initial length. + - makezero + + # Reports use of unnecessary []byte/string conversion calls + - mirror + + # Finds commonly misspelled English words. - misspell + + # Checks that functions with naked returns are not longer than a maximum size (can be zero). - nakedret + + # Finds the code that returns nil even if it checks that the error is not nil. + - nilerr + + # Reports constructs that checks for err != nil, but returns a different nil value error. + # Powered by nilness and nilerr. + - nilnesserr + + # Reports ill-formed or insufficient nolint directives. + - nolintlint + + # Checks that fmt.Sprintf can be replaced with a faster alternative. + - perfsprint + + # Finds slice declarations that could potentially be pre-allocated. + - prealloc + + # Reports direct reads from proto message fields when getters should be used. + - protogetter + + # Checks that package variables are not reassigned. + - reassign + + # Checks for receiver type consistency. + - recvcheck + + # Fast, configurable, extensible, flexible, and beautiful linter for Go. Drop-in replacement of golint. # - revive - - staticcheck + + # Checks for mistakes with OpenTelemetry/Census spans. + - spancheck + + # Stylecheck is a replacement for golint. # - stylecheck + + # It's a set of rules from staticcheck. It's not the same thing as the staticcheck binary. + - staticcheck + + # Tenv is analyzer that detects using os.Setenv instead of t.Setenv + # since Go1.17. + - tenv + + # Checks usage of github.com/stretchr/testify. + - testifylint + - typecheck + + # Remove unnecessary type conversions. - unconvert - - unused - - whitespace - # - prealloc - - gofmt + # Reports unused function parameters and results in your code. + - unparam + + # A linter that detect the possibility to use variables/constants from the + # Go standard library. + - usestdlibvars + + # Finds wasted assignment statements. + - wastedassign + + # Whitespace is a linter that checks for unnecessary newlines at the start + # and end of functions, if, for, etc. + - whitespace linters-settings: goimports: - local-prefixes: "bbgithub.dev.bloomberg.com/cncr-inference/inferno" + local-prefixes: "github.com/kserve/kserve" gosec: # specify configuration of gosec rules: https://github.com/securego/gosec#available-rules config: @@ -57,4 +202,18 @@ linters-settings: # default: "0600" G302: "0640" # maximum allowed permissions for os.WriteFile and ioutil.WriteFile - G306: "0640" \ No newline at end of file + G306: "0640" + importas: + alias: + - pkg: "github.com/kserve/kserve/pkg/apis/serving/v1beta1" + alias: "" + - pkg: "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" + alias: "" + - pkg: "k8s.io/api/core/v1" + alias: "corev1" + - pkg: "k8s.io/apimachinery/pkg/apis/meta/v1" + alias: "metav1" + - pkg: "sigs.k8s.io/controller-runtime" + alias: "ctrl" + - pkg: "sigs.k8s.io/controller-runtime/runtime" + alias: "runtime" diff --git a/Dockerfile b/Dockerfile index 955c1b2bc26..7e7a4d1fa84 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # Build the manager binary -FROM golang:1.22 AS builder +FROM golang:1.23 AS builder # Copy in the go src WORKDIR /go/src/github.com/kserve/kserve diff --git a/Makefile b/Makefile index 9e117a98a73..81ccadbe230 100644 --- a/Makefile +++ b/Makefile @@ -17,6 +17,7 @@ CUSTOM_MODEL_GRPC_IMG ?= custom-model-grpc CUSTOM_TRANSFORMER_IMG ?= image-transformer CUSTOM_TRANSFORMER_GRPC_IMG ?= custom-image-transformer-grpc HUGGINGFACE_SERVER_IMG ?= huggingfaceserver +HUGGINGFACE_SERVER_CPU_IMG ?= huggingfaceserver-cpu-openvino AIF_IMG ?= aiffairness ART_IMG ?= art-explainer STORAGE_INIT_IMG ?= storage-initializer @@ -28,6 +29,12 @@ ENVTEST_K8S_VERSION = 1.29 SUCCESS_200_ISVC_IMG ?= success-200-isvc ERROR_404_ISVC_IMG ?= error-404-isvc +ENGINE ?= docker +# Empty string for local build when using podman, it allows to build different architectures +# to use do: ENGINE=podman ARCH="--arch x86_64" make docker-build-something +ARCH ?= + + ## Location to install dependencies to LOCALBIN ?= $(shell pwd)/bin $(LOCALBIN): @@ -91,7 +98,7 @@ deploy-dev: manifests if [ ${KSERVE_ENABLE_SELF_SIGNED_CA} != false ]; then ./hack/self-signed-ca.sh; fi; # TODO: Add runtimes as part of default deployment kubectl wait --for=condition=ready pod -l control-plane=kserve-controller-manager -n kserve --timeout=300s - kubectl apply --server-side=true -k config/clusterresources + kubectl apply --server-side=true --force-conflicts -k config/clusterresources git checkout HEAD -- config/certmanager/certificate.yaml deploy-dev-sklearn: docker-push-sklearn @@ -124,7 +131,7 @@ deploy-ci: manifests deploy-helm: manifests helm install kserve-crd charts/kserve-crd/ --wait --timeout 180s - helm install kserve charts/kserve-resources/ --wait --timeout 180s + helm install kserve charts/kserve-resources/ --wait --timeout 180s -n kserve --create-namespace undeploy: kubectl delete -k config/default @@ -207,8 +214,8 @@ bump-version: @hack/prepare-for-release.sh $(PRIOR_VERSION) $(NEW_VERSION) # Build the docker image -docker-build: test - docker buildx build . -t ${IMG} +docker-build: + ${ENGINE} buildx build ${ARCH} . -t ${IMG} @echo "updating kustomize image patch file for manager resource" # Use perl instead of sed to avoid OSX/Linux compatibility issue: @@ -220,115 +227,121 @@ docker-push: docker push ${IMG} docker-build-agent: - docker buildx build -f agent.Dockerfile . -t ${KO_DOCKER_REPO}/${AGENT_IMG} + ${ENGINE} buildx build ${ARCH} -f agent.Dockerfile . -t ${KO_DOCKER_REPO}/${AGENT_IMG} docker-build-router: - docker buildx build -f router.Dockerfile . -t ${KO_DOCKER_REPO}/${ROUTER_IMG} + ${ENGINE} buildx build ${ARCH} -f router.Dockerfile . -t ${KO_DOCKER_REPO}/${ROUTER_IMG} docker-push-agent: - docker push ${KO_DOCKER_REPO}/${AGENT_IMG} + ${ENGINE} push ${KO_DOCKER_REPO}/${AGENT_IMG} docker-push-router: - docker push ${KO_DOCKER_REPO}/${ROUTER_IMG} + ${ENGINE} push ${KO_DOCKER_REPO}/${ROUTER_IMG} docker-build-sklearn: - cd python && docker buildx build --build-arg BASE_IMAGE=${BASE_IMG} -t ${KO_DOCKER_REPO}/${SKLEARN_IMG} -f sklearn.Dockerfile . + cd python && ${ENGINE} buildx build ${ARCH} --build-arg BASE_IMAGE=${BASE_IMG} -t ${KO_DOCKER_REPO}/${SKLEARN_IMG} -f sklearn.Dockerfile . docker-push-sklearn: docker-build-sklearn - docker push ${KO_DOCKER_REPO}/${SKLEARN_IMG} + ${ENGINE} push ${KO_DOCKER_REPO}/${SKLEARN_IMG} docker-build-xgb: - cd python && docker buildx build --build-arg BASE_IMAGE=${BASE_IMG} -t ${KO_DOCKER_REPO}/${XGB_IMG} -f xgb.Dockerfile . + cd python && ${ENGINE} buildx build ${ARCH} --build-arg BASE_IMAGE=${BASE_IMG} -t ${KO_DOCKER_REPO}/${XGB_IMG} -f xgb.Dockerfile . docker-push-xgb: docker-build-xgb - docker push ${KO_DOCKER_REPO}/${XGB_IMG} + ${ENGINE} push ${KO_DOCKER_REPO}/${XGB_IMG} docker-build-lgb: - cd python && docker buildx build --build-arg BASE_IMAGE=${BASE_IMG} -t ${KO_DOCKER_REPO}/${LGB_IMG} -f lgb.Dockerfile . + cd python && ${ENGINE} buildx build ${ARCH} --build-arg BASE_IMAGE=${BASE_IMG} -t ${KO_DOCKER_REPO}/${LGB_IMG} -f lgb.Dockerfile . docker-push-lgb: docker-build-lgb - docker push ${KO_DOCKER_REPO}/${LGB_IMG} + ${ENGINE} push ${KO_DOCKER_REPO}/${LGB_IMG} docker-build-pmml: - cd python && docker buildx build --build-arg BASE_IMAGE=${PMML_BASE_IMG} -t ${KO_DOCKER_REPO}/${PMML_IMG} -f pmml.Dockerfile . + cd python && ${ENGINE} buildx build ${ARCH} --build-arg BASE_IMAGE=${PMML_BASE_IMG} -t ${KO_DOCKER_REPO}/${PMML_IMG} -f pmml.Dockerfile . docker-push-pmml: docker-build-pmml - docker push ${KO_DOCKER_REPO}/${PMML_IMG} + ${ENGINE} push ${KO_DOCKER_REPO}/${PMML_IMG} docker-build-paddle: - cd python && docker buildx build --build-arg BASE_IMAGE=${BASE_IMG} -t ${KO_DOCKER_REPO}/${PADDLE_IMG} -f paddle.Dockerfile . + cd python && ${ENGINE} buildx build ${ARCH} --build-arg BASE_IMAGE=${BASE_IMG} -t ${KO_DOCKER_REPO}/${PADDLE_IMG} -f paddle.Dockerfile . docker-push-paddle: docker-build-paddle - docker push ${KO_DOCKER_REPO}/${PADDLE_IMG} + ${ENGINE} push ${KO_DOCKER_REPO}/${PADDLE_IMG} docker-build-custom-model: - cd python && docker buildx build -t ${KO_DOCKER_REPO}/${CUSTOM_MODEL_IMG} -f custom_model.Dockerfile . + cd python && ${ENGINE} buildx build ${ARCH} -t ${KO_DOCKER_REPO}/${CUSTOM_MODEL_IMG} -f custom_model.Dockerfile . docker-push-custom-model: docker-build-custom-model docker push ${KO_DOCKER_REPO}/${CUSTOM_MODEL_IMG} docker-build-custom-model-grpc: - cd python && docker buildx build -t ${KO_DOCKER_REPO}/${CUSTOM_MODEL_GRPC_IMG} -f custom_model_grpc.Dockerfile . + cd python && ${ENGINE} buildx build ${ARCH} -t ${KO_DOCKER_REPO}/${CUSTOM_MODEL_GRPC_IMG} -f custom_model_grpc.Dockerfile . docker-push-custom-model-grpc: docker-build-custom-model-grpc - docker push ${KO_DOCKER_REPO}/${CUSTOM_MODEL_GRPC_IMG} + ${ENGINE} push ${KO_DOCKER_REPO}/${CUSTOM_MODEL_GRPC_IMG} docker-build-custom-transformer: - cd python && docker buildx build -t ${KO_DOCKER_REPO}/${CUSTOM_TRANSFORMER_IMG} -f custom_transformer.Dockerfile . + cd python && ${ENGINE} buildx build ${ARCH} -t ${KO_DOCKER_REPO}/${CUSTOM_TRANSFORMER_IMG} -f custom_transformer.Dockerfile . docker-push-custom-transformer: docker-build-custom-transformer - docker push ${KO_DOCKER_REPO}/${CUSTOM_TRANSFORMER_IMG} + ${ENGINE} push ${KO_DOCKER_REPO}/${CUSTOM_TRANSFORMER_IMG} docker-build-custom-transformer-grpc: - cd python && docker buildx build -t ${KO_DOCKER_REPO}/${CUSTOM_TRANSFORMER_GRPC_IMG} -f custom_transformer_grpc.Dockerfile . + cd python && ${ENGINE} buildx build ${ARCH} -t ${KO_DOCKER_REPO}/${CUSTOM_TRANSFORMER_GRPC_IMG} -f custom_transformer_grpc.Dockerfile . docker-push-custom-transformer-grpc: docker-build-custom-transformer-grpc - docker push ${KO_DOCKER_REPO}/${CUSTOM_TRANSFORMER_GRPC_IMG} + ${ENGINE} push ${KO_DOCKER_REPO}/${CUSTOM_TRANSFORMER_GRPC_IMG} docker-build-aif: - cd python && docker buildx build -t ${KO_DOCKER_REPO}/${AIF_IMG} -f aiffairness.Dockerfile . + cd python && ${ENGINE} buildx build ${ARCH} -t ${KO_DOCKER_REPO}/${AIF_IMG} -f aiffairness.Dockerfile . docker-push-aif: docker-build-aif - docker push ${KO_DOCKER_REPO}/${AIF_IMG} + ${ENGINE} push ${KO_DOCKER_REPO}/${AIF_IMG} docker-build-art: - cd python && docker buildx build -t ${KO_DOCKER_REPO}/${ART_IMG} -f artexplainer.Dockerfile . + cd python && ${ENGINE} buildx build ${ARCH} -t ${KO_DOCKER_REPO}/${ART_IMG} -f artexplainer.Dockerfile . docker-push-art: docker-build-art - docker push ${KO_DOCKER_REPO}/${ART_IMG} + ${ENGINE} push ${KO_DOCKER_REPO}/${ART_IMG} docker-build-storageInitializer: - cd python && docker buildx build --build-arg BASE_IMAGE=${BASE_IMG} -t ${KO_DOCKER_REPO}/${STORAGE_INIT_IMG} -f storage-initializer.Dockerfile . + cd python && ${ENGINE} buildx build ${ARCH} --build-arg BASE_IMAGE=${BASE_IMG} -t ${KO_DOCKER_REPO}/${STORAGE_INIT_IMG} -f storage-initializer.Dockerfile . docker-push-storageInitializer: docker-build-storageInitializer - docker push ${KO_DOCKER_REPO}/${STORAGE_INIT_IMG} + ${ENGINE} push ${KO_DOCKER_REPO}/${STORAGE_INIT_IMG} docker-build-qpext: - docker buildx build -t ${KO_DOCKER_REPO}/${QPEXT_IMG} -f qpext/qpext.Dockerfile . + ${ENGINE} buildx build ${ARCH} -t ${KO_DOCKER_REPO}/${QPEXT_IMG} -f qpext/qpext.Dockerfile . docker-build-push-qpext: docker-build-qpext - docker push ${KO_DOCKER_REPO}/${QPEXT_IMG} + ${ENGINE} push ${KO_DOCKER_REPO}/${QPEXT_IMG} deploy-dev-qpext: docker-build-push-qpext kubectl patch cm config-deployment -n knative-serving --type merge --patch '{"data": {"queue-sidecar-image": "${KO_DOCKER_REPO}/${QPEXT_IMG}"}}' docker-build-success-200-isvc: - cd python && docker buildx build -t ${KO_DOCKER_REPO}/${SUCCESS_200_ISVC_IMG} -f success_200_isvc.Dockerfile . + cd python && ${ENGINE} buildx build ${ARCH} -t ${KO_DOCKER_REPO}/${SUCCESS_200_ISVC_IMG} -f success_200_isvc.Dockerfile . docker-push-success-200-isvc: docker-build-success-200-isvc - docker push ${KO_DOCKER_REPO}/${SUCCESS_200_ISVC_IMG} + ${ENGINE} push ${KO_DOCKER_REPO}/${SUCCESS_200_ISVC_IMG} docker-build-error-node-404: - cd python && docker buildx build -t ${KO_DOCKER_REPO}/${ERROR_404_ISVC_IMG} -f error_404_isvc.Dockerfile . + cd python && ${ENGINE} buildx build ${ARCH} -t ${KO_DOCKER_REPO}/${ERROR_404_ISVC_IMG} -f error_404_isvc.Dockerfile . docker-push-error-node-404: docker-build-error-node-404 - docker push ${KO_DOCKER_REPO}/${ERROR_404_ISVC_IMG} + ${ENGINE} push ${KO_DOCKER_REPO}/${ERROR_404_ISVC_IMG} docker-build-huggingface: - cd python && docker buildx build -t ${KO_DOCKER_REPO}/${HUGGINGFACE_SERVER_IMG} -f huggingface_server.Dockerfile . + cd python && ${ENGINE} buildx build ${ARCH} -t ${KO_DOCKER_REPO}/${HUGGINGFACE_SERVER_IMG} -f huggingface_server.Dockerfile . docker-push-huggingface: docker-build-huggingface - docker push ${KO_DOCKER_REPO}/${HUGGINGFACE_SERVER_IMG} + ${ENGINE} push ${KO_DOCKER_REPO}/${HUGGINGFACE_SERVER_IMG} + +docker-build-huggingface-cpu-openvino: + cd python && ${ENGINE} buildx build ${ARCH} -t ${KO_DOCKER_REPO}/${HUGGINGFACE_SERVER_CPU_IMG} -f huggingface_server_cpu_openvino.Dockerfile . + +docker-push-huggingface-cpu-openvino: docker-build-huggingface-cpu-openvino + ${ENGINE} push ${KO_DOCKER_REPO}/${HUGGINGFACE_SERVER_CPU_IMG} test-qpext: cd qpext && go test -v ./... -cover @@ -342,8 +355,8 @@ $(ENVTEST): $(LOCALBIN) test -s $(LOCALBIN)/setup-envtest || GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-runtime/tools/setup-envtest@latest apidocs: - docker buildx build -f docs/apis/Dockerfile --rm -t apidocs-gen . && \ - docker run -it --rm -v $(CURDIR)/pkg/apis:/go/src/github.com/kserve/kserve/pkg/apis -v ${PWD}/docs/apis:/go/gen-crd-api-reference-docs/apidocs apidocs-gen + ${ENGINE} buildx build ${ARCH} -f docs/apis/Dockerfile --rm -t apidocs-gen . && \ + ${ENGINE} run -it --rm -v $(CURDIR)/pkg/apis:/go/src/github.com/kserve/kserve/pkg/apis -v ${PWD}/docs/apis:/go/gen-crd-api-reference-docs/apidocs apidocs-gen .PHONY: check-doc-links check-doc-links: diff --git a/agent.Dockerfile b/agent.Dockerfile index 7600e8254ca..b2d22ea945b 100644 --- a/agent.Dockerfile +++ b/agent.Dockerfile @@ -1,5 +1,5 @@ # Build the inference-agent binary -FROM golang:1.22 AS builder +FROM golang:1.23 AS builder # Copy in the go src WORKDIR /go/src/github.com/kserve/kserve diff --git a/charts/kserve-crd-minimal/Chart.yaml b/charts/kserve-crd-minimal/Chart.yaml index b37614f6cd0..5f9396b8642 100644 --- a/charts/kserve-crd-minimal/Chart.yaml +++ b/charts/kserve-crd-minimal/Chart.yaml @@ -1,6 +1,6 @@ apiVersion: v1 name: kserve-crd-minimal -version: v0.14.0 +version: v0.15.0-rc0 description: Helm chart for deploying minimal kserve crds without validation keywords: - kserve diff --git a/charts/kserve-crd-minimal/README.md b/charts/kserve-crd-minimal/README.md index 23304d071bf..0319f65affc 100644 --- a/charts/kserve-crd-minimal/README.md +++ b/charts/kserve-crd-minimal/README.md @@ -2,12 +2,12 @@ Helm chart for deploying minimal kserve crds without validation -![Version: v0.14.0](https://img.shields.io/badge/Version-v0.14.0-informational?style=flat-square) +![Version: v0.15.0-rc0](https://img.shields.io/badge/Version-v0.15.0--rc0-informational?style=flat-square) ## Installing the Chart To install the chart, run the following: ```console -$ helm install kserve-crd-minimal oci://ghcr.io/kserve/charts/kserve-crd-minimal --version v0.14.0 +$ helm install kserve-crd-minimal oci://ghcr.io/kserve/charts/kserve-crd-minimal --version v0.15.0-rc0 ``` diff --git a/charts/kserve-crd-minimal/templates/serving.kserve.io_predictor.yaml b/charts/kserve-crd-minimal/templates/serving.kserve.io_predictor.yaml deleted file mode 100644 index a9c4ba7c877..00000000000 --- a/charts/kserve-crd-minimal/templates/serving.kserve.io_predictor.yaml +++ /dev/null @@ -1,270 +0,0 @@ -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.7.0 - creationTimestamp: null - labels: - app.kubernetes.io/instance: modelmesh-controller - app.kubernetes.io/managed-by: modelmesh-controller - app.kubernetes.io/name: modelmesh-controller - name: predictors.serving.kserve.io -spec: - group: serving.kserve.io - names: - kind: Predictor - listKind: PredictorList - plural: predictors - singular: predictor - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .spec.modelType.name - name: Type - type: string - - jsonPath: .status.available - name: Available - type: boolean - - jsonPath: .status.activeModelState - name: ActiveModel - type: string - - jsonPath: .status.targetModelState - name: TargetModel - type: string - - jsonPath: .status.transitionStatus - name: Transition - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha1 - schema: - openAPIV3Schema: - description: Predictor is the Schema for the predictors API - properties: - apiVersion: - description: - "APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources" - type: string - kind: - description: - "Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" - type: string - metadata: - type: object - spec: - description: PredictorSpec defines the desired state of Predictor - properties: - gpu: - description: May be absent, "preferred" or "required" - enum: - - required - - preferred - type: string - modelType: - properties: - name: - type: string - version: - type: string - required: - - name - type: object - path: - description: (DEPRECATED) The path to the model files within the storage - type: string - protocolVersion: - description: - Protocol version to be exposed by the predictor (i.e. - v1 or v2 or grpc-v1 or grpc-v2) - type: string - runtime: - description: - If omitted a compatible runtime is selected based on - the model type (if available) - properties: - name: - type: string - required: - - name - type: object - schemaPath: - description: (DEPRECATED) The path to the schema file within the storage - type: string - serviceAccountName: - description: NOT YET SUPPORTED - type: string - storage: - properties: - key: - description: The Storage Key in the secret for this model. - type: string - parameters: - additionalProperties: - type: string - description: - Parameters to override the default storage credentials - and config. - type: object - path: - description: - The path to the model object in the storage. It cannot - co-exist with the storageURI. - type: string - persistentVolumeClaim: - description: - (DEPRECATED) PersistentVolmueClaim was never supported - this way and will be removed - properties: - claimName: - description: - "ClaimName is the name of a PersistentVolumeClaim - in the same namespace as the pod using this volume. More - info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims" - type: string - readOnly: - description: - Will force the ReadOnly setting in VolumeMounts. - Default false. - type: boolean - required: - - claimName - type: object - s3: - description: - (DEPRECATED) S3 has configuration to connect to an - S3 instance. It is now deprecated, use fields from Spec.Storage - instead. - properties: - bucket: - type: string - secretKey: - type: string - required: - - secretKey - type: object - schemaPath: - description: The path to the model schema file in the storage. - type: string - type: object - required: - - modelType - type: object - status: - default: - activeModelState: Pending - available: false - failedCopies: 0 - targetModelState: "" - totalCopies: 0 - transitionStatus: UpToDate - description: PredictorStatus defines the observed state of Predictor - properties: - activeModelState: - default: Pending - description: - "High level state string: Pending, Standby, Loading, - Loaded, FailedToLoad" - enum: - - "" - - Pending - - Standby - - Loading - - Loaded - - FailedToLoad - type: string - available: - description: Whether the predictor endpoint is available - type: boolean - failedCopies: - default: 0 - description: - How many copies of this predictor's models failed to - load recently - type: integer - grpcEndpoint: - type: string - httpEndpoint: - description: - Addressable endpoint for the deployed trained model This - will be "static" and will not change when the model is mutated - type: string - lastFailureInfo: - description: - Details of last failure, when load of target model is - failed or blocked - properties: - location: - description: - Name of component to which the failure relates (usually - Pod name) - type: string - message: - description: Detailed error message - type: string - modelId: - description: Internal ID of model, tied to specific Spec contents - type: string - reason: - description: High level class of failure - enum: - - ModelLoadFailed - - RuntimeUnhealthy - - NoSupportingRuntime - - RuntimeNotRecognized - - InvalidPredictorSpec - type: string - time: - description: Time failure occurred or was discovered - format: date-time - type: string - type: object - targetModelState: - default: "" - description: ModelState enum - enum: - - "" - - Pending - - Standby - - Loading - - Loaded - - FailedToLoad - type: string - totalCopies: - default: 0 - description: Total number of copies of this predictor's models - type: integer - transitionStatus: - default: UpToDate - description: - Whether the available predictor endpoint reflects the - current Spec or is in transition - enum: - - UpToDate - - InProgress - - BlockedByFailedLoad - - InvalidSpec - type: string - required: - - activeModelState - - available - - failedCopies - - targetModelState - - totalCopies - - transitionStatus - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/charts/kserve-crd/Chart.yaml b/charts/kserve-crd/Chart.yaml index 71d9775854e..9649e3a3064 100644 --- a/charts/kserve-crd/Chart.yaml +++ b/charts/kserve-crd/Chart.yaml @@ -1,6 +1,6 @@ apiVersion: v1 name: kserve-crd -version: v0.14.0 +version: v0.15.0-rc0 description: Helm chart for deploying kserve crds keywords: - kserve diff --git a/charts/kserve-crd/README.md b/charts/kserve-crd/README.md index e5d31378d0a..4d52dde8e22 100644 --- a/charts/kserve-crd/README.md +++ b/charts/kserve-crd/README.md @@ -2,12 +2,12 @@ Helm chart for deploying kserve crds -![Version: v0.14.0](https://img.shields.io/badge/Version-v0.14.0-informational?style=flat-square) +![Version: v0.15.0-rc0](https://img.shields.io/badge/Version-v0.15.0--rc0-informational?style=flat-square) ## Installing the Chart To install the chart, run the following: ```console -$ helm install kserve-crd oci://ghcr.io/kserve/charts/kserve-crd --version v0.14.0 +$ helm install kserve-crd oci://ghcr.io/kserve/charts/kserve-crd --version v0.15.0-rc0 ``` diff --git a/charts/kserve-crd/templates/serving.kserve.io_clusterservingruntimes.yaml b/charts/kserve-crd/templates/serving.kserve.io_clusterservingruntimes.yaml index 4a9079784b4..743e43f67ea 100644 --- a/charts/kserve-crd/templates/serving.kserve.io_clusterservingruntimes.yaml +++ b/charts/kserve-crd/templates/serving.kserve.io_clusterservingruntimes.yaml @@ -1001,6 +1001,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -1335,10 +1337,12 @@ spec: diskURI: type: string fsType: + default: ext4 type: string kind: type: string readOnly: + default: false type: boolean required: - diskName @@ -1698,6 +1702,13 @@ spec: required: - path type: object + image: + properties: + pullPolicy: + type: string + reference: + type: string + type: object iscsi: properties: chapAuthDiscovery: @@ -1711,6 +1722,7 @@ spec: iqn: type: string iscsiInterface: + default: default type: string lun: format: int32 @@ -1959,6 +1971,7 @@ spec: image: type: string keyring: + default: /etc/ceph/keyring type: string monitors: items: @@ -1966,6 +1979,7 @@ spec: type: array x-kubernetes-list-type: atomic pool: + default: rbd type: string readOnly: type: boolean @@ -1977,6 +1991,7 @@ spec: type: object x-kubernetes-map-type: atomic user: + default: admin type: string required: - image @@ -1985,6 +2000,7 @@ spec: scaleIO: properties: fsType: + default: xfs type: string gateway: type: string @@ -2002,6 +2018,7 @@ spec: sslEnabled: type: boolean storageMode: + default: ThinProvisioned type: string storagePool: type: string @@ -2963,6 +2980,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -3217,7 +3236,9 @@ spec: additionalProperties: type: string type: object - size: + pipelineParallelSize: + type: integer + tensorParallelSize: type: integer tolerations: items: @@ -3261,10 +3282,12 @@ spec: diskURI: type: string fsType: + default: ext4 type: string kind: type: string readOnly: + default: false type: boolean required: - diskName @@ -3624,6 +3647,13 @@ spec: required: - path type: object + image: + properties: + pullPolicy: + type: string + reference: + type: string + type: object iscsi: properties: chapAuthDiscovery: @@ -3637,6 +3667,7 @@ spec: iqn: type: string iscsiInterface: + default: default type: string lun: format: int32 @@ -3885,6 +3916,7 @@ spec: image: type: string keyring: + default: /etc/ceph/keyring type: string monitors: items: @@ -3892,6 +3924,7 @@ spec: type: array x-kubernetes-list-type: atomic pool: + default: rbd type: string readOnly: type: boolean @@ -3903,6 +3936,7 @@ spec: type: object x-kubernetes-map-type: atomic user: + default: admin type: string required: - image @@ -3911,6 +3945,7 @@ spec: scaleIO: properties: fsType: + default: xfs type: string gateway: type: string @@ -3928,6 +3963,7 @@ spec: sslEnabled: type: boolean storageMode: + default: ThinProvisioned type: string storagePool: type: string diff --git a/charts/kserve-crd/templates/serving.kserve.io_clusterstoragecontainers.yaml b/charts/kserve-crd/templates/serving.kserve.io_clusterstoragecontainers.yaml index 0afee25e8c3..944ed597fce 100644 --- a/charts/kserve-crd/templates/serving.kserve.io_clusterstoragecontainers.yaml +++ b/charts/kserve-crd/templates/serving.kserve.io_clusterstoragecontainers.yaml @@ -469,6 +469,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object diff --git a/charts/kserve-crd/templates/serving.kserve.io_inferencegraphs.yaml b/charts/kserve-crd/templates/serving.kserve.io_inferencegraphs.yaml index d14444c8486..b83542ae233 100644 --- a/charts/kserve-crd/templates/serving.kserve.io_inferencegraphs.yaml +++ b/charts/kserve-crd/templates/serving.kserve.io_inferencegraphs.yaml @@ -526,6 +526,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object diff --git a/charts/kserve-crd/templates/serving.kserve.io_inferenceservices.yaml b/charts/kserve-crd/templates/serving.kserve.io_inferenceservices.yaml index 644b697b62b..7fe150d3352 100644 --- a/charts/kserve-crd/templates/serving.kserve.io_inferenceservices.yaml +++ b/charts/kserve-crd/templates/serving.kserve.io_inferenceservices.yaml @@ -936,6 +936,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -1643,6 +1645,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -2399,6 +2403,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -2698,13 +2704,10 @@ spec: properties: name: type: string - source: - properties: - resourceClaimName: - type: string - resourceClaimTemplateName: - type: string - type: object + resourceClaimName: + type: string + resourceClaimTemplateName: + type: string required: - name type: object @@ -2789,6 +2792,8 @@ spec: type: integer type: array x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + type: string sysctls: items: properties: @@ -2930,10 +2935,12 @@ spec: diskURI: type: string fsType: + default: ext4 type: string kind: type: string readOnly: + default: false type: boolean required: - diskName @@ -3293,6 +3300,13 @@ spec: required: - path type: object + image: + properties: + pullPolicy: + type: string + reference: + type: string + type: object iscsi: properties: chapAuthDiscovery: @@ -3306,6 +3320,7 @@ spec: iqn: type: string iscsiInterface: + default: default type: string lun: format: int32 @@ -3554,6 +3569,7 @@ spec: image: type: string keyring: + default: /etc/ceph/keyring type: string monitors: items: @@ -3561,6 +3577,7 @@ spec: type: array x-kubernetes-list-type: atomic pool: + default: rbd type: string readOnly: type: boolean @@ -3572,6 +3589,7 @@ spec: type: object x-kubernetes-map-type: atomic user: + default: admin type: string required: - image @@ -3580,6 +3598,7 @@ spec: scaleIO: properties: fsType: + default: xfs type: string gateway: type: string @@ -3597,6 +3616,7 @@ spec: sslEnabled: type: boolean storageMode: + default: ThinProvisioned type: string storagePool: type: string @@ -4575,6 +4595,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -5315,6 +5337,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -6016,6 +6040,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -6691,6 +6717,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -7404,6 +7432,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -8098,6 +8128,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -8796,6 +8828,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -9481,6 +9515,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -10173,6 +10209,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -10436,13 +10474,10 @@ spec: properties: name: type: string - source: - properties: - resourceClaimName: - type: string - resourceClaimTemplateName: - type: string - type: object + resourceClaimName: + type: string + resourceClaimTemplateName: + type: string required: - name type: object @@ -10527,6 +10562,8 @@ spec: type: integer type: array x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + type: string sysctls: items: properties: @@ -10996,6 +11033,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -11683,6 +11722,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -12448,6 +12489,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -12723,10 +12766,12 @@ spec: diskURI: type: string fsType: + default: ext4 type: string kind: type: string readOnly: + default: false type: boolean required: - diskName @@ -13086,6 +13131,13 @@ spec: required: - path type: object + image: + properties: + pullPolicy: + type: string + reference: + type: string + type: object iscsi: properties: chapAuthDiscovery: @@ -13099,6 +13151,7 @@ spec: iqn: type: string iscsiInterface: + default: default type: string lun: format: int32 @@ -13347,6 +13400,7 @@ spec: image: type: string keyring: + default: /etc/ceph/keyring type: string monitors: items: @@ -13354,6 +13408,7 @@ spec: type: array x-kubernetes-list-type: atomic pool: + default: rbd type: string readOnly: type: boolean @@ -13365,6 +13420,7 @@ spec: type: object x-kubernetes-map-type: atomic user: + default: admin type: string required: - image @@ -13373,6 +13429,7 @@ spec: scaleIO: properties: fsType: + default: xfs type: string gateway: type: string @@ -13390,6 +13447,7 @@ spec: sslEnabled: type: boolean storageMode: + default: ThinProvisioned type: string storagePool: type: string @@ -14352,6 +14410,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -15057,6 +15117,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -15770,6 +15832,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -16027,6 +16091,8 @@ spec: pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true type: object + pipelineParallelSize: + type: integer preemptionPolicy: type: string priority: @@ -16048,13 +16114,10 @@ spec: properties: name: type: string - source: - properties: - resourceClaimName: - type: string - resourceClaimTemplateName: - type: string - type: object + resourceClaimName: + type: string + resourceClaimTemplateName: + type: string required: - name type: object @@ -16130,6 +16193,8 @@ spec: type: integer type: array x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + type: string sysctls: items: properties: @@ -16163,10 +16228,10 @@ spec: type: boolean shareProcessNamespace: type: boolean - size: - type: integer subdomain: type: string + tensorParallelSize: + type: integer terminationGracePeriodSeconds: format: int64 type: integer @@ -16270,10 +16335,12 @@ spec: diskURI: type: string fsType: + default: ext4 type: string kind: type: string readOnly: + default: false type: boolean required: - diskName @@ -16633,6 +16700,13 @@ spec: required: - path type: object + image: + properties: + pullPolicy: + type: string + reference: + type: string + type: object iscsi: properties: chapAuthDiscovery: @@ -16646,6 +16720,7 @@ spec: iqn: type: string iscsiInterface: + default: default type: string lun: format: int32 @@ -16894,6 +16969,7 @@ spec: image: type: string keyring: + default: /etc/ceph/keyring type: string monitors: items: @@ -16901,6 +16977,7 @@ spec: type: array x-kubernetes-list-type: atomic pool: + default: rbd type: string readOnly: type: boolean @@ -16912,6 +16989,7 @@ spec: type: object x-kubernetes-map-type: atomic user: + default: admin type: string required: - image @@ -16920,6 +16998,7 @@ spec: scaleIO: properties: fsType: + default: xfs type: string gateway: type: string @@ -16937,6 +17016,7 @@ spec: sslEnabled: type: boolean storageMode: + default: ThinProvisioned type: string storagePool: type: string @@ -17447,6 +17527,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -18601,6 +18683,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -19357,6 +19441,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -19656,13 +19742,10 @@ spec: properties: name: type: string - source: - properties: - resourceClaimName: - type: string - resourceClaimTemplateName: - type: string - type: object + resourceClaimName: + type: string + resourceClaimTemplateName: + type: string required: - name type: object @@ -19747,6 +19830,8 @@ spec: type: integer type: array x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + type: string sysctls: items: properties: @@ -19888,10 +19973,12 @@ spec: diskURI: type: string fsType: + default: ext4 type: string kind: type: string readOnly: + default: false type: boolean required: - diskName @@ -20251,6 +20338,13 @@ spec: required: - path type: object + image: + properties: + pullPolicy: + type: string + reference: + type: string + type: object iscsi: properties: chapAuthDiscovery: @@ -20264,6 +20358,7 @@ spec: iqn: type: string iscsiInterface: + default: default type: string lun: format: int32 @@ -20512,6 +20607,7 @@ spec: image: type: string keyring: + default: /etc/ceph/keyring type: string monitors: items: @@ -20519,6 +20615,7 @@ spec: type: array x-kubernetes-list-type: atomic pool: + default: rbd type: string readOnly: type: boolean @@ -20530,6 +20627,7 @@ spec: type: object x-kubernetes-map-type: atomic user: + default: admin type: string required: - image @@ -20538,6 +20636,7 @@ spec: scaleIO: properties: fsType: + default: xfs type: string gateway: type: string @@ -20555,6 +20654,7 @@ spec: sslEnabled: type: boolean storageMode: + default: ThinProvisioned type: string storagePool: type: string diff --git a/charts/kserve-crd/templates/serving.kserve.io_localmodelcaches.yaml b/charts/kserve-crd/templates/serving.kserve.io_localmodelcaches.yaml index e40ba50ac29..4606113c955 100644 --- a/charts/kserve-crd/templates/serving.kserve.io_localmodelcaches.yaml +++ b/charts/kserve-crd/templates/serving.kserve.io_localmodelcaches.yaml @@ -1,4 +1,3 @@ ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: @@ -32,8 +31,12 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - nodeGroup: - type: string + nodeGroups: + items: + type: string + maxItems: 1 + minItems: 1 + type: array sourceModelUri: type: string x-kubernetes-validations: @@ -41,7 +44,7 @@ spec: rule: self == oldSelf required: - modelSize - - nodeGroup + - nodeGroups - sourceModelUri type: object status: diff --git a/charts/kserve-crd/templates/serving.kserve.io_localmodelnodegroups.yaml b/charts/kserve-crd/templates/serving.kserve.io_localmodelnodegroups.yaml index 262d2130153..0ebefe9cc0c 100644 --- a/charts/kserve-crd/templates/serving.kserve.io_localmodelnodegroups.yaml +++ b/charts/kserve-crd/templates/serving.kserve.io_localmodelnodegroups.yaml @@ -143,10 +143,12 @@ spec: diskURI: type: string fsType: + default: ext4 type: string kind: type: string readOnly: + default: false type: boolean required: - diskName @@ -395,6 +397,7 @@ spec: iqn: type: string iscsiInterface: + default: default type: string lun: format: int32 @@ -546,6 +549,7 @@ spec: image: type: string keyring: + default: /etc/ceph/keyring type: string monitors: items: @@ -553,6 +557,7 @@ spec: type: array x-kubernetes-list-type: atomic pool: + default: rbd type: string readOnly: type: boolean @@ -565,6 +570,7 @@ spec: type: object x-kubernetes-map-type: atomic user: + default: admin type: string required: - image @@ -573,6 +579,7 @@ spec: scaleIO: properties: fsType: + default: xfs type: string gateway: type: string @@ -591,6 +598,7 @@ spec: sslEnabled: type: boolean storageMode: + default: ThinProvisioned type: string storagePool: type: string diff --git a/charts/kserve-crd/templates/serving.kserve.io_localmodelnodes.yaml b/charts/kserve-crd/templates/serving.kserve.io_localmodelnodes.yaml new file mode 100644 index 00000000000..f130f6e7a3b --- /dev/null +++ b/charts/kserve-crd/templates/serving.kserve.io_localmodelnodes.yaml @@ -0,0 +1,61 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.2 + name: localmodelnodes.serving.kserve.io +spec: + group: serving.kserve.io + names: + kind: LocalModelNode + listKind: LocalModelNodeList + plural: localmodelnodes + singular: localmodelnode + scope: Cluster + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + localModels: + items: + properties: + modelName: + type: string + sourceModelUri: + type: string + required: + - modelName + - sourceModelUri + type: object + type: array + required: + - localModels + type: object + status: + properties: + modelStatus: + additionalProperties: + enum: + - "" + - ModelDownloadPending + - ModelDownloading + - ModelDownloaded + - ModelDownloadError + type: string + type: object + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/charts/kserve-crd/templates/serving.kserve.io_predictor.yaml b/charts/kserve-crd/templates/serving.kserve.io_predictor.yaml deleted file mode 100644 index 6c345312e10..00000000000 --- a/charts/kserve-crd/templates/serving.kserve.io_predictor.yaml +++ /dev/null @@ -1,266 +0,0 @@ -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.7.0 - creationTimestamp: null - name: predictors.serving.kserve.io -spec: - group: serving.kserve.io - names: - kind: Predictor - listKind: PredictorList - plural: predictors - singular: predictor - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .spec.modelType.name - name: Type - type: string - - jsonPath: .status.available - name: Available - type: boolean - - jsonPath: .status.activeModelState - name: ActiveModel - type: string - - jsonPath: .status.targetModelState - name: TargetModel - type: string - - jsonPath: .status.transitionStatus - name: Transition - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha1 - schema: - openAPIV3Schema: - description: Predictor is the Schema for the predictors API - properties: - apiVersion: - description: - "APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources" - type: string - kind: - description: - "Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" - type: string - metadata: - type: object - spec: - description: PredictorSpec defines the desired state of Predictor - properties: - gpu: - description: May be absent, "preferred" or "required" - enum: - - required - - preferred - type: string - modelType: - properties: - name: - type: string - version: - type: string - required: - - name - type: object - path: - description: (DEPRECATED) The path to the model files within the storage - type: string - protocolVersion: - description: - Protocol version to be exposed by the predictor (i.e. - v1 or v2 or grpc-v1 or grpc-v2) - type: string - runtime: - description: - If omitted a compatible runtime is selected based on - the model type (if available) - properties: - name: - type: string - required: - - name - type: object - schemaPath: - description: (DEPRECATED) The path to the schema file within the storage - type: string - serviceAccountName: - description: NOT YET SUPPORTED - type: string - storage: - properties: - key: - description: The Storage Key in the secret for this model. - type: string - parameters: - additionalProperties: - type: string - description: - Parameters to override the default storage credentials - and config. - type: object - path: - description: - The path to the model object in the storage. It cannot - co-exist with the storageURI. - type: string - persistentVolumeClaim: - description: - (DEPRECATED) PersistentVolmueClaim was never supported - this way and will be removed - properties: - claimName: - description: - "ClaimName is the name of a PersistentVolumeClaim - in the same namespace as the pod using this volume. More - info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims" - type: string - readOnly: - description: - Will force the ReadOnly setting in VolumeMounts. - Default false. - type: boolean - required: - - claimName - type: object - s3: - description: - (DEPRECATED) S3 has configuration to connect to an - S3 instance. It is now deprecated, use fields from Spec.Storage - instead. - properties: - bucket: - type: string - secretKey: - type: string - required: - - secretKey - type: object - schemaPath: - description: The path to the model schema file in the storage. - type: string - type: object - required: - - modelType - type: object - status: - default: - activeModelState: Pending - available: false - failedCopies: 0 - targetModelState: "" - totalCopies: 0 - transitionStatus: UpToDate - description: PredictorStatus defines the observed state of Predictor - properties: - activeModelState: - default: Pending - description: - "High level state string: Pending, Standby, Loading, - Loaded, FailedToLoad" - enum: - - "" - - Pending - - Standby - - Loading - - Loaded - - FailedToLoad - type: string - available: - description: Whether the predictor endpoint is available - type: boolean - failedCopies: - default: 0 - description: - How many copies of this predictor's models failed to - load recently - type: integer - grpcEndpoint: - type: string - httpEndpoint: - description: - Addressable endpoint for the deployed trained model This - will be "static" and will not change when the model is mutated - type: string - lastFailureInfo: - description: - Details of last failure, when load of target model is - failed or blocked - properties: - location: - description: - Name of component to which the failure relates (usually - Pod name) - type: string - message: - description: Detailed error message - type: string - modelId: - description: Internal ID of model, tied to specific Spec contents - type: string - reason: - description: High level class of failure - enum: - - ModelLoadFailed - - RuntimeUnhealthy - - NoSupportingRuntime - - RuntimeNotRecognized - - InvalidPredictorSpec - type: string - time: - description: Time failure occurred or was discovered - format: date-time - type: string - type: object - targetModelState: - default: "" - description: ModelState enum - enum: - - "" - - Pending - - Standby - - Loading - - Loaded - - FailedToLoad - type: string - totalCopies: - default: 0 - description: Total number of copies of this predictor's models - type: integer - transitionStatus: - default: UpToDate - description: - Whether the available predictor endpoint reflects the - current Spec or is in transition - enum: - - UpToDate - - InProgress - - BlockedByFailedLoad - - InvalidSpec - type: string - required: - - activeModelState - - available - - failedCopies - - targetModelState - - totalCopies - - transitionStatus - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/charts/kserve-crd/templates/serving.kserve.io_servingruntimes.yaml b/charts/kserve-crd/templates/serving.kserve.io_servingruntimes.yaml index eb0137bf377..f174ebc7a05 100644 --- a/charts/kserve-crd/templates/serving.kserve.io_servingruntimes.yaml +++ b/charts/kserve-crd/templates/serving.kserve.io_servingruntimes.yaml @@ -1001,6 +1001,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -1335,10 +1337,12 @@ spec: diskURI: type: string fsType: + default: ext4 type: string kind: type: string readOnly: + default: false type: boolean required: - diskName @@ -1698,6 +1702,13 @@ spec: required: - path type: object + image: + properties: + pullPolicy: + type: string + reference: + type: string + type: object iscsi: properties: chapAuthDiscovery: @@ -1711,6 +1722,7 @@ spec: iqn: type: string iscsiInterface: + default: default type: string lun: format: int32 @@ -1959,6 +1971,7 @@ spec: image: type: string keyring: + default: /etc/ceph/keyring type: string monitors: items: @@ -1966,6 +1979,7 @@ spec: type: array x-kubernetes-list-type: atomic pool: + default: rbd type: string readOnly: type: boolean @@ -1977,6 +1991,7 @@ spec: type: object x-kubernetes-map-type: atomic user: + default: admin type: string required: - image @@ -1985,6 +2000,7 @@ spec: scaleIO: properties: fsType: + default: xfs type: string gateway: type: string @@ -2002,6 +2018,7 @@ spec: sslEnabled: type: boolean storageMode: + default: ThinProvisioned type: string storagePool: type: string @@ -2963,6 +2980,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -3217,7 +3236,9 @@ spec: additionalProperties: type: string type: object - size: + pipelineParallelSize: + type: integer + tensorParallelSize: type: integer tolerations: items: @@ -3261,10 +3282,12 @@ spec: diskURI: type: string fsType: + default: ext4 type: string kind: type: string readOnly: + default: false type: boolean required: - diskName @@ -3624,6 +3647,13 @@ spec: required: - path type: object + image: + properties: + pullPolicy: + type: string + reference: + type: string + type: object iscsi: properties: chapAuthDiscovery: @@ -3637,6 +3667,7 @@ spec: iqn: type: string iscsiInterface: + default: default type: string lun: format: int32 @@ -3885,6 +3916,7 @@ spec: image: type: string keyring: + default: /etc/ceph/keyring type: string monitors: items: @@ -3892,6 +3924,7 @@ spec: type: array x-kubernetes-list-type: atomic pool: + default: rbd type: string readOnly: type: boolean @@ -3903,6 +3936,7 @@ spec: type: object x-kubernetes-map-type: atomic user: + default: admin type: string required: - image @@ -3911,6 +3945,7 @@ spec: scaleIO: properties: fsType: + default: xfs type: string gateway: type: string @@ -3928,6 +3963,7 @@ spec: sslEnabled: type: boolean storageMode: + default: ThinProvisioned type: string storagePool: type: string diff --git a/charts/kserve-resources/Chart.yaml b/charts/kserve-resources/Chart.yaml index f5e812813ae..e1bb2824c54 100644 --- a/charts/kserve-resources/Chart.yaml +++ b/charts/kserve-resources/Chart.yaml @@ -1,6 +1,6 @@ apiVersion: v1 name: kserve -version: v0.14.0 +version: v0.15.0-rc0 description: Helm chart for deploying kserve resources keywords: - kserve diff --git a/charts/kserve-resources/README.md b/charts/kserve-resources/README.md index b71aec64642..fccad66cce1 100644 --- a/charts/kserve-resources/README.md +++ b/charts/kserve-resources/README.md @@ -2,14 +2,14 @@ Helm chart for deploying kserve resources -![Version: v0.14.0](https://img.shields.io/badge/Version-v0.14.0-informational?style=flat-square) +![Version: v0.15.0-rc0](https://img.shields.io/badge/Version-v0.15.0--rc0-informational?style=flat-square) ## Installing the Chart To install the chart, run the following: ```console -$ helm install kserve oci://ghcr.io/kserve/charts/kserve --version v0.14.0 +$ helm install kserve oci://ghcr.io/kserve/charts/kserve --version v0.15.0-rc0 ``` ## Values @@ -17,7 +17,7 @@ $ helm install kserve oci://ghcr.io/kserve/charts/kserve --version v0.14.0 | Key | Type | Default | Description | |-----|------|---------|-------------| | kserve.agent.image | string | `"kserve/agent"` | | -| kserve.agent.tag | string | `"v0.14.0"` | | +| kserve.agent.tag | string | `"v0.15.0-rc0"` | | | kserve.controller.affinity | object | `{}` | A Kubernetes Affinity, if required. For more information, see [Affinity v1 core](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#affinity-v1-core). For example: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: foo.bar.com/role operator: In values: - master | | kserve.controller.annotations | object | `{}` | Optional additional annotations to add to the controller deployment. | | kserve.controller.containerSecurityContext | object | `{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]},"privileged":false,"readOnlyRootFilesystem":true,"runAsNonRoot":true}` | Container Security Context to be set on the controller component container. For more information, see [Configure a Security Context for a Pod or Container](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/). | @@ -27,8 +27,12 @@ $ helm install kserve oci://ghcr.io/kserve/charts/kserve --version v0.14.0 | kserve.controller.gateway.disableIstioVirtualHost | bool | `false` | DisableIstioVirtualHost controls whether to use istio as network layer for top level component routing or path based routing. This configuration is only applicable for Serverless mode, when disabled Istio is no longer required. | | kserve.controller.gateway.domain | string | `"example.com"` | Ingress domain for RawDeployment mode, for Serverless it is configured in Knative. | | kserve.controller.gateway.domainTemplate | string | `"{{ .Name }}-{{ .Namespace }}.{{ .IngressDomain }}"` | Ingress domain template for RawDeployment mode, for Serverless mode it is configured in Knative. | -| kserve.controller.gateway.ingressGateway.className | string | `"istio"` | | -| kserve.controller.gateway.ingressGateway.gateway | string | `"knative-serving/knative-ingress-gateway"` | | +| kserve.controller.gateway.ingressGateway | object | `{"className":"istio","createGateway":false,"enableGatewayApi":false,"gateway":"knative-serving/knative-ingress-gateway","kserveGateway":"kserve/kserve-ingress-gateway"}` | ingressGateway specifies the gateway which handles the network traffic from outside the cluster. | +| kserve.controller.gateway.ingressGateway.className | string | `"istio"` | class specifies the ingress class name. If Gateway API is enabled, this will not affect the ingress routing. | +| kserve.controller.gateway.ingressGateway.createGateway | bool | `false` | createGateway controls whether to create the default Gateway resource for ingress routing as part of the installation. This is only used when Gateway API is enabled. | +| kserve.controller.gateway.ingressGateway.enableGatewayApi | bool | `false` | enableGatewayApi controls whether to use the Gateway API for ingress routing instead of kuberetes Ingress. | +| kserve.controller.gateway.ingressGateway.gateway | string | `"knative-serving/knative-ingress-gateway"` | gateway specifies the name and namespace of the Knative's ingress gateway. | +| kserve.controller.gateway.ingressGateway.kserveGateway | string | `"kserve/kserve-ingress-gateway"` | kserveGateway specifies the name and namespace of the Gateway which handles the network traffic from outside the cluster. This is only used when Gateway API is enabled. The gateway should be specified in format / | | kserve.controller.gateway.localGateway.gateway | string | `"knative-serving/knative-local-gateway"` | localGateway specifies the gateway which handles the network traffic within the cluster. | | kserve.controller.gateway.localGateway.gatewayService | string | `"knative-local-gateway.istio-system.svc.cluster.local"` | localGatewayService specifies the hostname of the local gateway service. | | kserve.controller.gateway.localGateway.knativeGatewayService | string | `""` | knativeLocalGatewayService specifies the hostname of the Knative's local gateway service. When unset, the value of "localGatewayService" will be used. When enabling strict mTLS in Istio, KServe local gateway should be created and pointed to the Knative local gateway. | @@ -55,47 +59,37 @@ $ helm install kserve oci://ghcr.io/kserve/charts/kserve --version v0.14.0 | kserve.controller.resources | object | `{"limits":{"cpu":"100m","memory":"300Mi"},"requests":{"cpu":"100m","memory":"300Mi"}}` | Resources to provide to the kserve controller pod. For example: requests: cpu: 10m memory: 32Mi For more information, see [Resource Management for Pods and Containers](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/). | | kserve.controller.securityContext | object | `{"runAsNonRoot":true}` | Pod Security Context. For more information, see [Configure a Security Context for a Pod or Container](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/). | | kserve.controller.serviceAnnotations | object | `{}` | Optional additional annotations to add to the controller service. | -| kserve.controller.tag | string | `"v0.14.0"` | KServe controller contrainer image tag. | +| kserve.controller.tag | string | `"v0.15.0-rc0"` | KServe controller contrainer image tag. | | kserve.controller.tolerations | list | `[]` | A list of Kubernetes Tolerations, if required. For more information, see [Toleration v1 core](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#toleration-v1-core). For example: tolerations: - key: foo.bar.com/role operator: Equal value: master effect: NoSchedule | | kserve.controller.topologySpreadConstraints | list | `[]` | A list of Kubernetes TopologySpreadConstraints, if required. For more information, see [Topology spread constraint v1 core](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#topologyspreadconstraint-v1-core For example: topologySpreadConstraints: - maxSkew: 2 topologyKey: topology.kubernetes.io/zone whenUnsatisfiable: ScheduleAnyway labelSelector: matchLabels: app.kubernetes.io/instance: kserve-controller-manager app.kubernetes.io/component: controller | | kserve.controller.webhookServiceAnnotations | object | `{}` | Optional additional annotations to add to the webhook service. | +| kserve.inferenceservice.resources.limits.cpu | string | `"1"` | | +| kserve.inferenceservice.resources.limits.memory | string | `"2Gi"` | | +| kserve.inferenceservice.resources.requests.cpu | string | `"1"` | | +| kserve.inferenceservice.resources.requests.memory | string | `"2Gi"` | | +| kserve.localmodel.agent.affinity | object | `{}` | | | kserve.localmodel.agent.hostPath | string | `"/mnt/models"` | | | kserve.localmodel.agent.image | string | `"kserve/kserve-localmodelnode-agent"` | | | kserve.localmodel.agent.nodeSelector | object | `{}` | | | kserve.localmodel.agent.reconcilationFrequencyInSecs | int | `60` | | -| kserve.localmodel.agent.tag | string | `"v0.14.0"` | | +| kserve.localmodel.agent.securityContext.runAsNonRoot | bool | `true` | | +| kserve.localmodel.agent.securityContext.runAsUser | int | `1000` | | +| kserve.localmodel.agent.tag | string | `"v0.15.0-rc0"` | | +| kserve.localmodel.agent.tolerations | list | `[]` | | | kserve.localmodel.controller.image | string | `"kserve/kserve-localmodel-controller"` | | -| kserve.localmodel.controller.tag | string | `"v0.14.0"` | | +| kserve.localmodel.controller.tag | string | `"v0.15.0-rc0"` | | +| kserve.localmodel.disableVolumeManagement | bool | `false` | | | kserve.localmodel.enabled | bool | `false` | | | kserve.localmodel.jobNamespace | string | `"kserve-localmodel-jobs"` | | | kserve.localmodel.jobTTLSecondsAfterFinished | int | `3600` | | | kserve.localmodel.securityContext.fsGroup | int | `1000` | | | kserve.metricsaggregator.enableMetricAggregation | string | `"false"` | configures metric aggregation annotation. This adds the annotation serving.kserve.io/enable-metric-aggregation to every service with the specified boolean value. If true enables metric aggregation in queue-proxy by setting env vars in the queue proxy container to configure scraping ports. | | kserve.metricsaggregator.enablePrometheusScraping | string | `"false"` | If true, prometheus annotations are added to the pod to scrape the metrics. If serving.kserve.io/enable-metric-aggregation is false, the prometheus port is set with the default prometheus scraping port 9090, otherwise the prometheus port annotation is set with the metric aggregation port. | -| kserve.modelmesh.config.modelmeshImage | string | `"kserve/modelmesh"` | | -| kserve.modelmesh.config.modelmeshImageTag | string | `"v0.12.0"` | | -| kserve.modelmesh.config.modelmeshRuntimeAdapterImage | string | `"kserve/modelmesh-runtime-adapter"` | | -| kserve.modelmesh.config.modelmeshRuntimeAdapterImageTag | string | `"v0.12.0"` | | -| kserve.modelmesh.config.podsPerRuntime | int | `2` | | -| kserve.modelmesh.config.restProxyImage | string | `"kserve/rest-proxy"` | | -| kserve.modelmesh.config.restProxyImageTag | string | `"v0.12.0"` | | -| kserve.modelmesh.controller.affinity.podAntiAffinity.preferredDuringSchedulingIgnoredDuringExecution[0].podAffinityTerm.labelSelector.matchExpressions[0].key | string | `"control-plane"` | | -| kserve.modelmesh.controller.affinity.podAntiAffinity.preferredDuringSchedulingIgnoredDuringExecution[0].podAffinityTerm.labelSelector.matchExpressions[0].operator | string | `"In"` | | -| kserve.modelmesh.controller.affinity.podAntiAffinity.preferredDuringSchedulingIgnoredDuringExecution[0].podAffinityTerm.labelSelector.matchExpressions[0].values[0] | string | `"modelmesh-controller"` | | -| kserve.modelmesh.controller.affinity.podAntiAffinity.preferredDuringSchedulingIgnoredDuringExecution[0].podAffinityTerm.topologyKey | string | `"topology.kubernetes.io/zone"` | | -| kserve.modelmesh.controller.affinity.podAntiAffinity.preferredDuringSchedulingIgnoredDuringExecution[0].weight | int | `100` | | -| kserve.modelmesh.controller.image | string | `"kserve/modelmesh-controller"` | | -| kserve.modelmesh.controller.nodeSelector | object | `{}` | | -| kserve.modelmesh.controller.tag | string | `"v0.12.0"` | | -| kserve.modelmesh.controller.tolerations | list | `[]` | | -| kserve.modelmesh.controller.topologySpreadConstraints | list | `[]` | | -| kserve.modelmesh.enabled | bool | `true` | | -| kserve.modelmeshVersion | string | `"v0.12.0"` | | | kserve.router.image | string | `"kserve/router"` | | -| kserve.router.tag | string | `"v0.14.0"` | | +| kserve.router.tag | string | `"v0.15.0-rc0"` | | | kserve.security.autoMountServiceAccountToken | bool | `true` | | | kserve.service.serviceClusterIPNone | bool | `false` | | -| kserve.servingruntime.art.defaultVersion | string | `"v0.14.0"` | | +| kserve.servingruntime.art.defaultVersion | string | `"v0.15.0-rc0"` | | | kserve.servingruntime.art.image | string | `"kserve/art-explainer"` | | | kserve.servingruntime.art.imagePullSecrets | list | `[]` | | | kserve.servingruntime.huggingfaceserver.devShm.enabled | bool | `false` | | @@ -107,14 +101,14 @@ $ helm install kserve oci://ghcr.io/kserve/charts/kserve --version v0.14.0 | kserve.servingruntime.huggingfaceserver.securityContext.capabilities.drop[0] | string | `"ALL"` | | | kserve.servingruntime.huggingfaceserver.securityContext.privileged | bool | `false` | | | kserve.servingruntime.huggingfaceserver.securityContext.runAsNonRoot | bool | `true` | | -| kserve.servingruntime.huggingfaceserver.tag | string | `"v0.14.0"` | | +| kserve.servingruntime.huggingfaceserver.tag | string | `"v0.15.0-rc0"` | | | kserve.servingruntime.lgbserver.image | string | `"kserve/lgbserver"` | | | kserve.servingruntime.lgbserver.imagePullSecrets | list | `[]` | | | kserve.servingruntime.lgbserver.securityContext.allowPrivilegeEscalation | bool | `false` | | | kserve.servingruntime.lgbserver.securityContext.capabilities.drop[0] | string | `"ALL"` | | | kserve.servingruntime.lgbserver.securityContext.privileged | bool | `false` | | | kserve.servingruntime.lgbserver.securityContext.runAsNonRoot | bool | `true` | | -| kserve.servingruntime.lgbserver.tag | string | `"v0.14.0"` | | +| kserve.servingruntime.lgbserver.tag | string | `"v0.15.0-rc0"` | | | kserve.servingruntime.mlserver.image | string | `"docker.io/seldonio/mlserver"` | | | kserve.servingruntime.mlserver.imagePullSecrets | list | `[]` | | | kserve.servingruntime.mlserver.modelClassPlaceholder | string | `"{{.Labels.modelClass}}"` | | @@ -130,21 +124,21 @@ $ helm install kserve oci://ghcr.io/kserve/charts/kserve --version v0.14.0 | kserve.servingruntime.paddleserver.securityContext.capabilities.drop[0] | string | `"ALL"` | | | kserve.servingruntime.paddleserver.securityContext.privileged | bool | `false` | | | kserve.servingruntime.paddleserver.securityContext.runAsNonRoot | bool | `true` | | -| kserve.servingruntime.paddleserver.tag | string | `"v0.14.0"` | | +| kserve.servingruntime.paddleserver.tag | string | `"v0.15.0-rc0"` | | | kserve.servingruntime.pmmlserver.image | string | `"kserve/pmmlserver"` | | | kserve.servingruntime.pmmlserver.imagePullSecrets | list | `[]` | | | kserve.servingruntime.pmmlserver.securityContext.allowPrivilegeEscalation | bool | `false` | | | kserve.servingruntime.pmmlserver.securityContext.capabilities.drop[0] | string | `"ALL"` | | | kserve.servingruntime.pmmlserver.securityContext.privileged | bool | `false` | | | kserve.servingruntime.pmmlserver.securityContext.runAsNonRoot | bool | `true` | | -| kserve.servingruntime.pmmlserver.tag | string | `"v0.14.0"` | | +| kserve.servingruntime.pmmlserver.tag | string | `"v0.15.0-rc0"` | | | kserve.servingruntime.sklearnserver.image | string | `"kserve/sklearnserver"` | | | kserve.servingruntime.sklearnserver.imagePullSecrets | list | `[]` | | | kserve.servingruntime.sklearnserver.securityContext.allowPrivilegeEscalation | bool | `false` | | | kserve.servingruntime.sklearnserver.securityContext.capabilities.drop[0] | string | `"ALL"` | | | kserve.servingruntime.sklearnserver.securityContext.privileged | bool | `false` | | | kserve.servingruntime.sklearnserver.securityContext.runAsNonRoot | bool | `true` | | -| kserve.servingruntime.sklearnserver.tag | string | `"v0.14.0"` | | +| kserve.servingruntime.sklearnserver.tag | string | `"v0.15.0-rc0"` | | | kserve.servingruntime.tensorflow.image | string | `"tensorflow/serving"` | | | kserve.servingruntime.tensorflow.imagePullSecrets | list | `[]` | | | kserve.servingruntime.tensorflow.securityContext.allowPrivilegeEscalation | bool | `false` | | @@ -176,7 +170,7 @@ $ helm install kserve oci://ghcr.io/kserve/charts/kserve --version v0.14.0 | kserve.servingruntime.xgbserver.securityContext.capabilities.drop[0] | string | `"ALL"` | | | kserve.servingruntime.xgbserver.securityContext.privileged | bool | `false` | | | kserve.servingruntime.xgbserver.securityContext.runAsNonRoot | bool | `true` | | -| kserve.servingruntime.xgbserver.tag | string | `"v0.14.0"` | | +| kserve.servingruntime.xgbserver.tag | string | `"v0.15.0-rc0"` | | | kserve.storage.caBundleConfigMapName | string | `""` | Mounted CA bundle config map name for storage initializer. | | kserve.storage.caBundleVolumeMountPath | string | `"/etc/ssl/custom-certs"` | Mounted path for CA bundle config map. | | kserve.storage.containerSecurityContext.allowPrivilegeEscalation | bool | `false` | | @@ -199,5 +193,5 @@ $ helm install kserve oci://ghcr.io/kserve/charts/kserve --version v0.14.0 | kserve.storage.s3.verifySSL | string | `""` | Whether to verify the tls/ssl certificate, default to true. | | kserve.storage.storageSecretNameAnnotation | string | `"serving.kserve.io/secretName"` | Storage secret name reference for storage initializer. | | kserve.storage.storageSpecSecretName | string | `"storage-config"` | Storage spec secret name. | -| kserve.storage.tag | string | `"v0.14.0"` | | -| kserve.version | string | `"v0.14.0"` | | +| kserve.storage.tag | string | `"v0.15.0-rc0"` | | +| kserve.version | string | `"v0.15.0-rc0"` | | diff --git a/charts/kserve-resources/templates/clusterrole.yaml b/charts/kserve-resources/templates/clusterrole.yaml index 46c76a4117e..ddfb156d5cb 100644 --- a/charts/kserve-resources/templates/clusterrole.yaml +++ b/charts/kserve-resources/templates/clusterrole.yaml @@ -91,6 +91,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - httproutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - networking.istio.io resources: diff --git a/charts/kserve-resources/templates/configmap.yaml b/charts/kserve-resources/templates/configmap.yaml index 34c7bcc6a1c..b4c2d1d34fa 100644 --- a/charts/kserve-resources/templates/configmap.yaml +++ b/charts/kserve-resources/templates/configmap.yaml @@ -179,7 +179,9 @@ data: # ====================================== INGRESS CONFIGURATION ====================================== # Example ingress: |- - { + { + "enableGatewayApi": false, + "kserveIngressGateway" : "kserve/kserve-ingress-gateway", "ingressGateway" : "knative-serving/knative-ingress-gateway", "localGateway" : "knative-serving/knative-local-gateway", "localGatewayService" : "knative-local-gateway.istio-system.svc.cluster.local", @@ -192,7 +194,17 @@ data: "disableIngressCreation": false } ingress: |- - { + { + # enableGatewayApi specifies whether to use Gateway API instead of Ingress for serving external traffic. + "enableGatewayApi": false, + + # KServe implements [Gateway API](https://gateway-api.sigs.k8s.io/) to serve external traffic. + # By default, KServe configures a default gateway to serve external traffic. + # But, KServe can be configured to use a custom gateway by modifying this configuration. + # The gateway should be specified in format / + # NOTE: This configuration only applicable for raw deployment. + "kserveIngressGateway": "kserve/kserve-ingress-gateway", + # ingressGateway specifies the ingress gateway to serve external traffic. # The gateway should be specified in format / # NOTE: This configuration only applicable for serverless deployment with Istio configured as network layer. @@ -470,6 +482,8 @@ data: # The frequency at which the local model agent reconciles the local models # This is to detect if models are missing from local disk "reconcilationFrequencyInSecs": {{ .Values.kserve.localmodel.agent.reconcilationFrequencyInSecs }} + # This is to disable localmodel pv and pvc management for namespaces without isvcs + "disableVolumeManagement": false } agent: |- @@ -533,7 +547,9 @@ data: } } ingress: |- - { + { + "enableGatewayApi": {{ .Values.kserve.controller.gateway.ingressGateway.enableGatewayApi }}, + "kserveIngressGateway" : "{{ .Values.kserve.controller.gateway.ingressGateway.kserveGateway }}", "ingressGateway" : "{{ .Values.kserve.controller.gateway.ingressGateway.gateway }}", "knativeLocalGatewayService" : "{{ .Values.kserve.controller.gateway.localGateway.knativeGatewayService }}", "localGateway" : "{{ .Values.kserve.controller.gateway.localGateway.gateway }}", @@ -585,9 +601,20 @@ data: "jobTTLSecondsAfterFinished": {{ .Values.kserve.localmodel.jobTTLSecondsAfterFinished }}, "defaultJobImage": "kserve/storage-initializer:latest", "fsGroup": {{ .Values.kserve.localmodel.securityContext.fsGroup }}, - "reconcilationFrequencyInSecs": {{ .Values.kserve.localmodel.agent.reconcilationFrequencyInSecs }} + "reconcilationFrequencyInSecs": {{ .Values.kserve.localmodel.agent.reconcilationFrequencyInSecs }}, + "disableVolumeManagement": {{ .Values.kserve.localmodel.disableVolumeManagement }} } security: |- { "autoMountServiceAccountToken": {{ .Values.kserve.security.autoMountServiceAccountToken }} } + + inferenceService: |- + { + "resource": { + "cpuLimit": "{{ .Values.kserve.inferenceservice.resources.limits.cpu }}", + "cpuRequest": "{{ .Values.kserve.inferenceservice.resources.requests.cpu }}", + "memoryLimit": "{{ .Values.kserve.inferenceservice.resources.limits.memory }}", + "memoryRequest": "{{ .Values.kserve.inferenceservice.resources.requests.memory }}" + } + } diff --git a/charts/kserve-resources/templates/deployment.yaml b/charts/kserve-resources/templates/deployment.yaml index 350f015e25f..8acb084cffd 100644 --- a/charts/kserve-resources/templates/deployment.yaml +++ b/charts/kserve-resources/templates/deployment.yaml @@ -97,14 +97,14 @@ spec: value: kserve-webhook-server-cert livenessProbe: failureThreshold: 5 - initialDelaySeconds: 10 + initialDelaySeconds: 30 httpGet: path: /healthz port: 8081 timeoutSeconds: 5 readinessProbe: - initialDelaySeconds: 10 - failureThreshold: 10 + initialDelaySeconds: 30 + failureThreshold: 5 periodSeconds: 5 httpGet: path: /readyz diff --git a/charts/kserve-resources/templates/ingress_gateway.yaml b/charts/kserve-resources/templates/ingress_gateway.yaml new file mode 100644 index 00000000000..82fdc9135e0 --- /dev/null +++ b/charts/kserve-resources/templates/ingress_gateway.yaml @@ -0,0 +1,19 @@ +{{- if and .Values.kserve.controller.gateway.ingressGateway.enableGatewayApi .Values.kserve.controller.gateway.ingressGateway.createGateway }} +apiVersion: gateway.networking.k8s.io/v1 +kind: Gateway +metadata: + name: kserve-ingress-gateway + namespace: {{ .Release.Namespace }} +spec: + gatewayClassName: envoy + listeners: + - name: http + port: 80 + protocol: HTTP + allowedRoutes: + namespaces: + from: All + infrastructure: + labels: + serving.kserve.io/gateway: kserve-ingress-gateway +{{- end }} diff --git a/charts/kserve-resources/templates/localmodelnode/daemonset.yaml b/charts/kserve-resources/templates/localmodelnode/daemonset.yaml index e5512a15be4..b254b8c3329 100644 --- a/charts/kserve-resources/templates/localmodelnode/daemonset.yaml +++ b/charts/kserve-resources/templates/localmodelnode/daemonset.yaml @@ -27,6 +27,14 @@ spec: nodeSelector: {{- toYaml . | nindent 8 }} {{- end }} + {{- with .Values.kserve.localmodel.agent.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.kserve.localmodel.agent.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} serviceAccountName: kserve-localmodelnode-agent securityContext: runAsNonRoot: true @@ -43,7 +51,8 @@ spec: - ALL privileged: false readOnlyRootFilesystem: true - runAsNonRoot: true + runAsNonRoot: {{ .Values.kserve.localmodel.agent.securityContext.runAsNonRoot }} + runAsUser: {{ .Values.kserve.localmodel.agent.securityContext.runAsUser }} env: - name: POD_NAMESPACE valueFrom: diff --git a/charts/kserve-resources/templates/localmodelnode/namespace.yaml b/charts/kserve-resources/templates/localmodelnode/namespace.yaml deleted file mode 100644 index 5b60f0b8c39..00000000000 --- a/charts/kserve-resources/templates/localmodelnode/namespace.yaml +++ /dev/null @@ -1,7 +0,0 @@ -{{- if .Values.kserve.localmodel.enabled }} ---- -apiVersion: v1 -kind: Namespace -metadata: - name: {{.Values.kserve.localmodel.jobNamespace}} -{{- end }} diff --git a/charts/kserve-resources/templates/modelmesh/certificate.yaml b/charts/kserve-resources/templates/modelmesh/certificate.yaml deleted file mode 100644 index 9d5beedecdf..00000000000 --- a/charts/kserve-resources/templates/modelmesh/certificate.yaml +++ /dev/null @@ -1,17 +0,0 @@ -{{- if .Values.kserve.modelmesh.enabled }} ---- -apiVersion: cert-manager.io/v1 -kind: Certificate -metadata: - name: modelmesh-webhook-server-cert - namespace: {{ .Release.Namespace }} -spec: - commonName: modelmesh-webhook-server-service.{{ .Release.Namespace }}.svc - dnsNames: - - modelmesh-webhook-server-service.{{ .Release.Namespace }}.svc - - modelmesh-webhook-server-service.{{ .Release.Namespace }}.svc.cluster.local - issuerRef: - kind: Issuer - name: selfsigned-issuer - secretName: modelmesh-webhook-server-cert -{{- end }} diff --git a/charts/kserve-resources/templates/modelmesh/clusterrole.yaml b/charts/kserve-resources/templates/modelmesh/clusterrole.yaml deleted file mode 100644 index ada0cf6ea89..00000000000 --- a/charts/kserve-resources/templates/modelmesh/clusterrole.yaml +++ /dev/null @@ -1,204 +0,0 @@ -{{- if .Values.kserve.modelmesh.enabled }} ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: modelmesh-controller-role - labels: - app.kubernetes.io/instance: modelmesh-controller - app.kubernetes.io/managed-by: modelmesh-controller - app.kubernetes.io/name: modelmesh-controller -rules: - - apiGroups: - - "" - resources: - - configmaps - verbs: - - create - - delete - - get - - list - - patch - - update - - watch - - apiGroups: - - "" - resources: - - endpoints - - persistentvolumeclaims - verbs: - - get - - list - - watch - - apiGroups: - - "" - resources: - - namespaces - - namespaces/finalizers - verbs: - - get - - list - - patch - - update - - watch - - apiGroups: - - "" - resources: - - secrets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch - - apiGroups: - - "" - resources: - - services - - services/finalizers - verbs: - - create - - delete - - get - - list - - patch - - update - - watch - - apiGroups: - - apps - resources: - - deployments - - deployments/finalizers - verbs: - - create - - delete - - get - - list - - patch - - update - - watch - - apiGroups: - - monitoring.coreos.com - resources: - - servicemonitors - verbs: - - create - - delete - - get - - list - - patch - - update - - watch - - apiGroups: - - serving.kserve.io - resources: - - inferenceservices - verbs: - - create - - delete - - get - - list - - patch - - update - - watch - - apiGroups: - - serving.kserve.io - resources: - - inferenceservices/finalizers - verbs: - - get - - patch - - update - - apiGroups: - - serving.kserve.io - resources: - - inferenceservices/status - verbs: - - get - - patch - - update - - apiGroups: - - serving.kserve.io - resources: - - predictors - verbs: - - create - - delete - - get - - list - - patch - - update - - watch - - apiGroups: - - serving.kserve.io - resources: - - predictors/finalizers - verbs: - - get - - patch - - update - - apiGroups: - - serving.kserve.io - resources: - - predictors/status - verbs: - - get - - patch - - update - - apiGroups: - - serving.kserve.io - resources: - - servingruntimes - - servingruntimes/finalizers - verbs: - - create - - delete - - get - - list - - patch - - update - - watch - - apiGroups: - - serving.kserve.io - resources: - - servingruntimes/status - verbs: - - get - - patch - - update - - apiGroups: - - serving.kserve.io - resources: - - clusterservingruntimes - - clusterservingruntimes/finalizers - verbs: - - create - - delete - - get - - list - - patch - - update - - watch - - apiGroups: - - serving.kserve.io - resources: - - clusterservingruntimes/status - verbs: - - get - - patch - - update - - apiGroups: - - autoscaling - resources: - - horizontalpodautoscalers - - horizontalpodautoscalers/status - verbs: - - get - - list - - watch - - create - - delete - - update -{{- end }} diff --git a/charts/kserve-resources/templates/modelmesh/clusterrolebinding.yaml b/charts/kserve-resources/templates/modelmesh/clusterrolebinding.yaml deleted file mode 100644 index 32a912b9768..00000000000 --- a/charts/kserve-resources/templates/modelmesh/clusterrolebinding.yaml +++ /dev/null @@ -1,19 +0,0 @@ -{{- if .Values.kserve.modelmesh.enabled }} ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - labels: - app.kubernetes.io/instance: modelmesh-controller - app.kubernetes.io/managed-by: modelmesh-controller - app.kubernetes.io/name: modelmesh-controller - name: modelmesh-controller-rolebinding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: modelmesh-controller-role -subjects: -- kind: ServiceAccount - name: modelmesh-controller - namespace: {{ .Release.Namespace }} -{{- end }} diff --git a/charts/kserve-resources/templates/modelmesh/clusterservingruntimes.yaml b/charts/kserve-resources/templates/modelmesh/clusterservingruntimes.yaml deleted file mode 100644 index 88a10688197..00000000000 --- a/charts/kserve-resources/templates/modelmesh/clusterservingruntimes.yaml +++ /dev/null @@ -1,229 +0,0 @@ -{{- if .Values.kserve.modelmesh.enabled }} ---- -apiVersion: serving.kserve.io/v1alpha1 -kind: ClusterServingRuntime -metadata: - labels: - app.kubernetes.io/instance: modelmesh-controller - app.kubernetes.io/managed-by: modelmesh-controller - app.kubernetes.io/name: modelmesh-controller - name: modelmesh-serving-mlserver-1.x-SR - name: mlserver-1.x -spec: - builtInAdapter: - serverType: mlserver - runtimeManagementPort: 8001 - memBufferBytes: 134217728 - modelLoadingTimeoutMillis: 90000 - containers: - - env: - - name: MLSERVER_MODELS_DIR - value: /models/_mlserver_models/ - - name: MLSERVER_GRPC_PORT - value: "8001" - - name: MLSERVER_HTTP_PORT - value: "8002" - - name: MLSERVER_LOAD_MODELS_AT_STARTUP - value: "false" - - name: MLSERVER_MODEL_NAME - value: dummy-model-fixme - - name: MLSERVER_HOST - value: 127.0.0.1 - - name: MLSERVER_GRPC_MAX_MESSAGE_LENGTH - value: "-1" - image: seldonio/mlserver:1.3.2 - name: mlserver - resources: - requests: - cpu: 500m - memory: 1Gi - limits: - cpu: "5" - memory: 1Gi - grpcDataEndpoint: port:8001 - grpcEndpoint: port:8085 - multiModel: true - protocolVersions: - - grpc-v2 - supportedModelFormats: - - name: sklearn - version: "0" - autoSelect: true - - name: xgboost - version: "1" - autoSelect: true - - name: lightgbm - version: "3" - autoSelect: true - ---- -apiVersion: serving.kserve.io/v1alpha1 -kind: ClusterServingRuntime -metadata: - labels: - app.kubernetes.io/instance: modelmesh-controller - app.kubernetes.io/managed-by: modelmesh-controller - app.kubernetes.io/name: modelmesh-controller - name: modelmesh-serving-ovms-1.x-SR - name: ovms-1.x -spec: - builtInAdapter: - memBufferBytes: 134217728 - modelLoadingTimeoutMillis: 90000 - runtimeManagementPort: 8888 - serverType: ovms - containers: - - args: - - --port=8001 - - --rest_port=8888 - - --config_path=/models/model_config_list.json - - --file_system_poll_wait_seconds=0 - - --grpc_bind_address=127.0.0.1 - - --rest_bind_address=127.0.0.1 - image: openvino/model_server:2022.2 - name: ovms - resources: - limits: - cpu: 5 - memory: 1Gi - requests: - cpu: 500m - memory: 1Gi - grpcDataEndpoint: port:8001 - grpcEndpoint: port:8085 - multiModel: true - protocolVersions: - - grpc-v1 - supportedModelFormats: - - name: openvino_ir - version: opset1 - autoSelect: true - - name: onnx - version: "1" - ---- -apiVersion: serving.kserve.io/v1alpha1 -kind: ClusterServingRuntime -metadata: - annotations: - maxLoadingConcurrency: "2" - labels: - app.kubernetes.io/instance: modelmesh-controller - app.kubernetes.io/managed-by: modelmesh-controller - app.kubernetes.io/name: modelmesh-controller - name: modelmesh-serving-triton-2.x-SR - name: triton-2.x -spec: - builtInAdapter: - memBufferBytes: 134217728 - modelLoadingTimeoutMillis: 90000 - runtimeManagementPort: 8001 - serverType: triton - containers: - - command: - - /bin/sh - args: - - -c - - > - mkdir -p /models/_triton_models; - chmod 777 /models/_triton_models; - exec tritonserver - --model-repository=/models/_triton_models - --model-control-mode=explicit - --strict-model-config=false - --strict-readiness=false - --allow-http=true - --allow-sagemaker=false - image: nvcr.io/nvidia/tritonserver:23.04-py3 - livenessProbe: - exec: - command: - - curl - - --fail - - --silent - - --show-error - - --max-time - - "9" - - http://localhost:8000/v2/health/live - initialDelaySeconds: 5 - periodSeconds: 30 - timeoutSeconds: 10 - name: triton - resources: - limits: - cpu: "5" - memory: 1Gi - requests: - cpu: 500m - memory: 1Gi - grpcDataEndpoint: port:8001 - grpcEndpoint: port:8085 - multiModel: true - protocolVersions: - - grpc-v2 - supportedModelFormats: - - name: keras - version: "2" - autoSelect: true - - name: onnx - version: "1" - autoSelect: true - - name: pytorch - version: "1" - autoSelect: true - - name: tensorflow - version: "1" - autoSelect: true - - name: tensorflow - version: "2" - autoSelect: true - - name: tensorrt - version: "7" - autoSelect: true - ---- -apiVersion: serving.kserve.io/v1alpha1 -kind: ClusterServingRuntime -metadata: - annotations: - maxLoadingConcurrency: "2" - labels: - app.kubernetes.io/instance: modelmesh-controller - app.kubernetes.io/managed-by: modelmesh-controller - app.kubernetes.io/name: modelmesh-controller - name: modelmesh-serving-torchserve-0.x-SR - name: torchserve-0.x -spec: - supportedModelFormats: - - name: pytorch-mar - version: "0" - autoSelect: true - multiModel: true - grpcEndpoint: "port:8085" - grpcDataEndpoint: "port:7070" - containers: - - name: torchserve - image: pytorch/torchserve:0.7.1-cpu - args: - # Adapter creates the config file; wait for it to exist before starting - - while [ ! -e "$TS_CONFIG_FILE" ]; do echo "waiting for config file..."; sleep 1; done; - - exec - - torchserve - - --start - - --foreground - env: - - name: TS_CONFIG_FILE - value: /models/_torchserve_models/mmconfig.properties - resources: - requests: - cpu: 500m - memory: 1Gi - limits: - cpu: "5" - memory: 1Gi - builtInAdapter: - serverType: torchserve - runtimeManagementPort: 7071 - memBufferBytes: 134217728 - modelLoadingTimeoutMillis: 90000 -{{- end }} diff --git a/charts/kserve-resources/templates/modelmesh/configmap.yaml b/charts/kserve-resources/templates/modelmesh/configmap.yaml deleted file mode 100644 index 195f1ae14b9..00000000000 --- a/charts/kserve-resources/templates/modelmesh/configmap.yaml +++ /dev/null @@ -1,57 +0,0 @@ -{{- if .Values.kserve.modelmesh.enabled }} ---- -apiVersion: v1 -kind: ConfigMap -metadata: - labels: - app.kubernetes.io/instance: modelmesh-controller - app.kubernetes.io/managed-by: modelmesh-controller - app.kubernetes.io/name: modelmesh-controller - name: model-serving-config-defaults -data: - config-defaults.yaml: | - podsPerRuntime: {{ .Values.kserve.modelmesh.config.podPerRuntime }} - headlessService: true - modelMeshImage: - name: {{ .Values.kserve.modelmesh.config.modelmeshImage }} - tag: {{ .Values.kserve.modelmesh.config.modelmeshImageTag }} - modelMeshResources: - requests: - cpu: "300m" - memory: "448Mi" - limits: - cpu: "3" - memory: "448Mi" - restProxy: - enabled: true - port: 8008 - image: - name: {{ .Values.kserve.modelmesh.config.restProxyImage }} - tag: {{ .Values.kserve.modelmesh.config.restProxyImageTag }} - resources: - requests: - cpu: "50m" - memory: "96Mi" - limits: - cpu: "1" - memory: "512Mi" - storageHelperImage: - name: {{ .Values.kserve.modelmesh.config.modelmeshRuntimeAdapterImage }} - tag: {{ .Values.kserve.modelmesh.config.modelmeshRuntimeAdapterImageTag }} - command: ["/opt/app/puller"] - storageHelperResources: - requests: - cpu: "50m" - memory: "96Mi" - limits: - cpu: "2" - memory: "512Mi" - serviceAccountName: "" - metrics: - enabled: true - builtInServerTypes: - - triton - - mlserver - - ovms - - torchserve -{{- end }} diff --git a/charts/kserve-resources/templates/modelmesh/deployment.yaml b/charts/kserve-resources/templates/modelmesh/deployment.yaml deleted file mode 100644 index dc161c30caa..00000000000 --- a/charts/kserve-resources/templates/modelmesh/deployment.yaml +++ /dev/null @@ -1,103 +0,0 @@ -{{- if .Values.kserve.modelmesh.enabled }} ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - app.kubernetes.io/instance: modelmesh-controller - app.kubernetes.io/managed-by: modelmesh-controller - app.kubernetes.io/name: modelmesh-controller - control-plane: modelmesh-controller - name: modelmesh-controller - namespace: {{ .Release.Namespace }} -spec: - replicas: 1 - selector: - matchLabels: - control-plane: modelmesh-controller - template: - metadata: - labels: - app.kubernetes.io/instance: modelmesh-controller - app.kubernetes.io/managed-by: modelmesh-controller - app.kubernetes.io/name: modelmesh-controller - control-plane: modelmesh-controller - spec: - {{- with .Values.kserve.modelmesh.controller.nodeSelector }} - nodeSelector: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.kserve.modelmesh.controller.affinity }} - affinity: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.kserve.modelmesh.controller.tolerations }} - tolerations: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.kserve.modelmesh.controller.topologySpreadConstraints }} - topologySpreadConstraints: - {{- toYaml . | nindent 8 }} - {{- end }} - containers: - - args: - - --enable-leader-election - command: - - /manager - env: - - name: NAMESPACE_SCOPE - value: "false" - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: ETCD_SECRET_NAME - value: model-serving-etcd - image: {{ .Values.kserve.modelmesh.controller.image }}:{{ .Values.kserve.modelmesh.controller.tag }} - livenessProbe: - httpGet: - path: /healthz - port: 8081 - initialDelaySeconds: 15 - periodSeconds: 10 - name: manager - ports: - - containerPort: 9443 - name: webhook - protocol: TCP - readinessProbe: - httpGet: - path: /readyz - port: 8081 - initialDelaySeconds: 10 - periodSeconds: 5 - resources: - limits: - cpu: "1" - memory: 512Mi - requests: - cpu: 50m - memory: 96Mi - securityContext: - capabilities: - drop: - - ALL - volumeMounts: - - mountPath: /etc/model-serving/config/default - name: config-defaults - readOnly: true - - mountPath: /tmp/k8s-webhook-server/serving-certs - name: modelmesh-webhook-server-cert - readOnly: true - serviceAccountName: modelmesh-controller - terminationGracePeriodSeconds: 10 - volumes: - - name: config-defaults - configMap: - defaultMode: 420 - name: model-serving-config-defaults - - name: modelmesh-webhook-server-cert - secret: - defaultMode: 420 - secretName: modelmesh-webhook-server-cert -{{- end }} diff --git a/charts/kserve-resources/templates/modelmesh/networkpolicy.yaml b/charts/kserve-resources/templates/modelmesh/networkpolicy.yaml deleted file mode 100644 index 6d0991d771e..00000000000 --- a/charts/kserve-resources/templates/modelmesh/networkpolicy.yaml +++ /dev/null @@ -1,82 +0,0 @@ -{{- if .Values.kserve.modelmesh.enabled }} ---- -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - labels: - app.kubernetes.io/instance: modelmesh-controller - app.kubernetes.io/managed-by: modelmesh-controller - app.kubernetes.io/name: modelmesh-controller - name: modelmesh-controller -spec: - ingress: - - ports: - - port: 8443 - protocol: TCP - podSelector: - matchLabels: - app.kubernetes.io/managed-by: modelmesh-controller - control-plane: modelmesh-controller - policyTypes: - - Ingress - ---- -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - labels: - app.kubernetes.io/instance: modelmesh-controller - app.kubernetes.io/managed-by: modelmesh-controller - app.kubernetes.io/name: modelmesh-controller - name: modelmesh-runtimes -spec: - ingress: - - from: - - podSelector: - matchLabels: - app.kubernetes.io/managed-by: modelmesh-controller - ports: - - port: 8033 - protocol: TCP - - port: 8080 - protocol: TCP - - ports: - - port: 8033 - protocol: TCP - - port: 8008 - protocol: TCP - - ports: - - port: 2112 - protocol: TCP - podSelector: - matchExpressions: - - key: modelmesh-service - operator: Exists - matchLabels: - app.kubernetes.io/managed-by: modelmesh-controller - policyTypes: - - Ingress - ---- -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - labels: - app.kubernetes.io/instance: modelmesh-controller - app.kubernetes.io/managed-by: modelmesh-controller - app.kubernetes.io/name: modelmesh-controller - name: modelmesh-webhook -spec: - podSelector: - matchLabels: - app.kubernetes.io/managed-by: modelmesh-controller - control-plane: modelmesh-controller - ingress: - # exposed for webhook - - ports: - - port: 9443 - protocol: TCP - policyTypes: - - Ingress - -{{- end }} diff --git a/charts/kserve-resources/templates/modelmesh/role.yaml b/charts/kserve-resources/templates/modelmesh/role.yaml deleted file mode 100644 index 2dfb5f60234..00000000000 --- a/charts/kserve-resources/templates/modelmesh/role.yaml +++ /dev/null @@ -1,58 +0,0 @@ -{{- if .Values.kserve.modelmesh.enabled }} ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - labels: - app.kubernetes.io/instance: modelmesh-controller - app.kubernetes.io/managed-by: modelmesh-controller - app.kubernetes.io/name: modelmesh-controller - name: modelmesh-controller-leader-election-role -rules: -- apiGroups: - - coordination.k8s.io - resources: - - leases - verbs: - - create - - get - - list - - update -- apiGroups: - - "" - resources: - - configmaps - verbs: - - get - - list - - watch - - create - - update - - patch - - delete -- apiGroups: - - "" - resources: - - events - verbs: - - create - ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - labels: - app.kubernetes.io/instance: modelmesh-controller - app.kubernetes.io/managed-by: modelmesh-controller - app.kubernetes.io/name: modelmesh-controller - name: modelmesh-controller-restricted-scc -rules: -- apiGroups: - - security.openshift.io - resourceNames: - - restricted - resources: - - securitycontextconstraints - verbs: - - use -{{- end }} diff --git a/charts/kserve-resources/templates/modelmesh/rolebinding.yaml b/charts/kserve-resources/templates/modelmesh/rolebinding.yaml deleted file mode 100644 index b3676efec96..00000000000 --- a/charts/kserve-resources/templates/modelmesh/rolebinding.yaml +++ /dev/null @@ -1,35 +0,0 @@ -{{- if .Values.kserve.modelmesh.enabled }} ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - labels: - app.kubernetes.io/instance: modelmesh-controller - app.kubernetes.io/managed-by: modelmesh-controller - app.kubernetes.io/name: modelmesh-controller - name: modelmesh-controller-leader-election-rolebinding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: modelmesh-controller-leader-election-role -subjects: -- kind: ServiceAccount - name: modelmesh-controller - ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - labels: - app.kubernetes.io/instance: modelmesh-controller - app.kubernetes.io/managed-by: modelmesh-controller - app.kubernetes.io/name: modelmesh-controller - name: modelmesh-controller-restricted-scc -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: modelmesh-controller-restricted-scc -subjects: -- kind: ServiceAccount - name: modelmesh-controller -{{- end }} diff --git a/charts/kserve-resources/templates/modelmesh/service.yaml b/charts/kserve-resources/templates/modelmesh/service.yaml deleted file mode 100644 index 6da6b84f7cb..00000000000 --- a/charts/kserve-resources/templates/modelmesh/service.yaml +++ /dev/null @@ -1,15 +0,0 @@ -{{- if .Values.kserve.modelmesh.enabled }} ---- -apiVersion: v1 -kind: Service -metadata: - name: modelmesh-webhook-server-service - namespace: {{ .Release.Namespace }} -spec: - ports: - - port: 9443 - protocol: TCP - targetPort: webhook - selector: - control-plane: modelmesh-controller -{{- end }} diff --git a/charts/kserve-resources/templates/modelmesh/serviceaccount.yaml b/charts/kserve-resources/templates/modelmesh/serviceaccount.yaml deleted file mode 100644 index a3c78b4c58c..00000000000 --- a/charts/kserve-resources/templates/modelmesh/serviceaccount.yaml +++ /dev/null @@ -1,21 +0,0 @@ -{{- if .Values.kserve.modelmesh.enabled }} ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - labels: - app.kubernetes.io/instance: modelmesh-controller - app.kubernetes.io/managed-by: modelmesh-controller - app.kubernetes.io/name: modelmesh-controller - name: modelmesh - ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - labels: - app.kubernetes.io/instance: modelmesh-controller - app.kubernetes.io/managed-by: modelmesh-controller - app.kubernetes.io/name: modelmesh-controller - name: modelmesh-controller -{{- end }} diff --git a/charts/kserve-resources/templates/modelmesh/webhookconfiguration.yaml b/charts/kserve-resources/templates/modelmesh/webhookconfiguration.yaml deleted file mode 100644 index e33e8301c99..00000000000 --- a/charts/kserve-resources/templates/modelmesh/webhookconfiguration.yaml +++ /dev/null @@ -1,32 +0,0 @@ -{{- if .Values.kserve.modelmesh.enabled }} ---- -apiVersion: admissionregistration.k8s.io/v1 -kind: ValidatingWebhookConfiguration -metadata: - name: modelmesh-servingruntime.serving.kserve.io - annotations: - cert-manager.io/inject-ca-from: {{ .Release.Namespace }}/modelmesh-webhook-server-cert -webhooks: - - admissionReviewVersions: - - v1 - clientConfig: - caBundle: Cg== - service: - name: modelmesh-webhook-server-service - namespace: {{ .Release.Namespace }} - path: /validate-serving-modelmesh-io-v1alpha1-servingruntime - port: 9443 - failurePolicy: Fail - name: servingruntime.modelmesh-webhook-server.default - rules: - - apiGroups: - - serving.kserve.io - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - clusterservingruntimes - sideEffects: None -{{- end }} diff --git a/charts/kserve-resources/values.yaml b/charts/kserve-resources/values.yaml index dfe52b9a548..1be22b6ceda 100644 --- a/charts/kserve-resources/values.yaml +++ b/charts/kserve-resources/values.yaml @@ -1,6 +1,5 @@ kserve: - version: &defaultVersion v0.14.0 - modelmeshVersion: &defaultModelMeshVersion v0.12.0 + version: &defaultVersion v0.15.0-rc0 agent: image: kserve/agent tag: *defaultVersion @@ -175,8 +174,18 @@ kserve: # -- knativeLocalGatewayService specifies the hostname of the Knative's local gateway service. # When unset, the value of "localGatewayService" will be used. When enabling strict mTLS in Istio, KServe local gateway should be created and pointed to the Knative local gateway. knativeGatewayService: "" + # -- ingressGateway specifies the gateway which handles the network traffic from outside the cluster. ingressGateway: + # -- enableGatewayApi controls whether to use the Gateway API for ingress routing instead of kuberetes Ingress. + enableGatewayApi: false + # -- kserveGateway specifies the name and namespace of the Gateway which handles the network traffic from outside the cluster. + # This is only used when Gateway API is enabled. The gateway should be specified in format / + kserveGateway: kserve/kserve-ingress-gateway + # -- createGateway controls whether to create the default Gateway resource for ingress routing as part of the installation. This is only used when Gateway API is enabled. + createGateway: false + # -- gateway specifies the name and namespace of the Knative's ingress gateway. gateway: knative-serving/knative-ingress-gateway + # -- class specifies the ingress class name. If Gateway API is enabled, this will not affect the ingress routing. className: istio # -- The nodeSelector on Pods tells Kubernetes to schedule Pods on the nodes with matching labels. @@ -254,35 +263,6 @@ kserve: # This ClusterRole grants the necessary permissions for the Knative's DomainMapping reconciler to resolve InferenceService addressables. knativeAddressableResolver: enabled: false - modelmesh: - enabled: true - controller: - nodeSelector: {} - tolerations: [] - topologySpreadConstraints: [] - affinity: - podAntiAffinity: - preferredDuringSchedulingIgnoredDuringExecution: - - weight: 100 - podAffinityTerm: - labelSelector: - matchExpressions: - - key: control-plane - operator: In - values: - - modelmesh-controller - topologyKey: topology.kubernetes.io/zone - image: kserve/modelmesh-controller - tag: *defaultModelMeshVersion - config: - modelmeshImage: kserve/modelmesh - modelmeshImageTag: *defaultModelMeshVersion - modelmeshRuntimeAdapterImage: kserve/modelmesh-runtime-adapter - modelmeshRuntimeAdapterImageTag: *defaultModelMeshVersion - restProxyImage: kserve/rest-proxy - restProxyImageTag: *defaultModelMeshVersion - podsPerRuntime: 2 - servingruntime: modelNamePlaceholder: "{{.Name}}" tensorflow: @@ -418,11 +398,25 @@ kserve: jobTTLSecondsAfterFinished: 3600 securityContext: fsGroup: 1000 + disableVolumeManagement: false agent: nodeSelector: {} + affinity: {} + tolerations: [] hostPath: /mnt/models image: kserve/kserve-localmodelnode-agent tag: *defaultVersion reconcilationFrequencyInSecs: 60 + securityContext: + runAsUser: 1000 + runAsNonRoot: true security: autoMountServiceAccountToken: true + inferenceservice: + resources: + limits: + cpu: "1" + memory: "2Gi" + requests: + cpu: "1" + memory: "2Gi" diff --git a/cmd/agent/main.go b/cmd/agent/main.go index 6943a2654ea..1b3092480ca 100644 --- a/cmd/agent/main.go +++ b/cmd/agent/main.go @@ -160,7 +160,7 @@ func main() { } logger.Info("Starting agent http server...") ctx := signals.NewContext() - mainServer, drain := buildServer(ctx, *port, *componentPort, loggerArgs, batcherArgs, probe, logger) + mainServer, drain := buildServer(*port, *componentPort, loggerArgs, batcherArgs, probe, logger) servers := map[string]*http.Server{ "main": mainServer, } @@ -222,7 +222,7 @@ func main() { logger.Errorw("Failed to bring up agent, shutting down.", zap.Error(err)) // This extra flush is needed because defers are not handled via os.Exit calls. if err := logger.Sync(); err != nil { - logger.Errorw("Error syncing logger: %v", err) + logger.Errorf("Error syncing logger: %v", err) } os.Stdout.Sync() os.Stderr.Sync() @@ -331,8 +331,9 @@ func buildProbe(logger *zap.SugaredLogger, probeJSON string, autodetectHTTP2 boo return newProbe } -func buildServer(ctx context.Context, port string, userPort int, loggerArgs *loggerArgs, batcherArgs *batcherArgs, // nolint unparam - probeContainer func() bool, logging *zap.SugaredLogger) (server *http.Server, drain func()) { +func buildServer(port string, userPort int, loggerArgs *loggerArgs, batcherArgs *batcherArgs, + probeContainer func() bool, logging *zap.SugaredLogger, +) (server *http.Server, drain func()) { logging.Infof("Building server user port %d port %s", userPort, port) target := &url.URL{ Scheme: "http", diff --git a/cmd/localmodel/main.go b/cmd/localmodel/main.go index 5fb089ef7f5..3fb45f9d580 100644 --- a/cmd/localmodel/main.go +++ b/cmd/localmodel/main.go @@ -20,8 +20,7 @@ import ( "flag" "os" - v1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/runtime" + corev1 "k8s.io/api/core/v1" "k8s.io/client-go/kubernetes" typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1" _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" @@ -39,10 +38,7 @@ import ( localmodelcontroller "github.com/kserve/kserve/pkg/controller/v1alpha1/localmodel" ) -var ( - scheme = runtime.NewScheme() //nolint: unused - setupLog = ctrl.Log.WithName("setup") -) +var setupLog = ctrl.Log.WithName("setup") const ( LeaderLockName = "kserve-local-model-manager-leader-lock" @@ -102,9 +98,11 @@ func main() { setupLog.Info("Setting up manager") mgr, err := manager.New(cfg, manager.Options{ Metrics: metricsserver.Options{ - BindAddress: options.metricsAddr}, + BindAddress: options.metricsAddr, + }, WebhookServer: webhook.NewServer(webhook.Options{ - Port: options.webhookPort}), + Port: options.webhookPort, + }), LeaderElection: options.enableLeaderElection, LeaderElectionID: LeaderLockName, HealthProbeBindAddress: options.probeAddr, @@ -129,7 +127,7 @@ func main() { } setupLog.Info("Setting up core scheme") - if err := v1.AddToScheme(mgr.GetScheme()); err != nil { + if err := corev1.AddToScheme(mgr.GetScheme()); err != nil { setupLog.Error(err, "unable to add Core APIs to scheme") os.Exit(1) } diff --git a/cmd/localmodelnode/main.go b/cmd/localmodelnode/main.go index 1a400439fdd..e918985ff9d 100644 --- a/cmd/localmodelnode/main.go +++ b/cmd/localmodelnode/main.go @@ -20,8 +20,7 @@ import ( "flag" "os" - v1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/runtime" + corev1 "k8s.io/api/core/v1" "k8s.io/client-go/kubernetes" typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1" _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" @@ -39,10 +38,7 @@ import ( localmodelnodecontroller "github.com/kserve/kserve/pkg/controller/v1alpha1/localmodelnode" ) -var ( - scheme = runtime.NewScheme() //nolint: unused - setupLog = ctrl.Log.WithName("setup") -) +var setupLog = ctrl.Log.WithName("setup") const ( LeaderLockName = "kserve-local-model-node-manager-leader-lock" @@ -102,9 +98,11 @@ func main() { setupLog.Info("Setting up manager") mgr, err := manager.New(cfg, manager.Options{ Metrics: metricsserver.Options{ - BindAddress: options.metricsAddr}, + BindAddress: options.metricsAddr, + }, WebhookServer: webhook.NewServer(webhook.Options{ - Port: options.webhookPort}), + Port: options.webhookPort, + }), LeaderElection: options.enableLeaderElection, LeaderElectionID: LeaderLockName, HealthProbeBindAddress: options.probeAddr, @@ -129,7 +127,7 @@ func main() { } setupLog.Info("Setting up core scheme") - if err := v1.AddToScheme(mgr.GetScheme()); err != nil { + if err := corev1.AddToScheme(mgr.GetScheme()); err != nil { setupLog.Error(err, "unable to add Core APIs to scheme") os.Exit(1) } diff --git a/cmd/manager/main.go b/cmd/manager/main.go index 3d087968d6a..a058661a541 100644 --- a/cmd/manager/main.go +++ b/cmd/manager/main.go @@ -17,18 +17,14 @@ limitations under the License. package main import ( + "context" "flag" "net/http" "os" - "github.com/kserve/kserve/pkg/webhook/admission/localmodelcache" - "sigs.k8s.io/controller-runtime/pkg/webhook/admission" - - "github.com/kserve/kserve/pkg/utils" istio_networking "istio.io/api/networking/v1alpha3" istioclientv1beta1 "istio.io/client-go/pkg/apis/networking/v1beta1" - v1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/runtime" + corev1 "k8s.io/api/core/v1" "k8s.io/client-go/kubernetes" typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1" _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" @@ -41,6 +37,8 @@ import ( "sigs.k8s.io/controller-runtime/pkg/manager/signals" metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" + gatewayapiv1 "sigs.k8s.io/gateway-api/apis/v1" "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" "github.com/kserve/kserve/pkg/apis/serving/v1beta1" @@ -49,14 +47,13 @@ import ( trainedmodelcontroller "github.com/kserve/kserve/pkg/controller/v1alpha1/trainedmodel" "github.com/kserve/kserve/pkg/controller/v1alpha1/trainedmodel/reconcilers/modelconfig" v1beta1controller "github.com/kserve/kserve/pkg/controller/v1beta1/inferenceservice" + "github.com/kserve/kserve/pkg/utils" + "github.com/kserve/kserve/pkg/webhook/admission/localmodelcache" "github.com/kserve/kserve/pkg/webhook/admission/pod" "github.com/kserve/kserve/pkg/webhook/admission/servingruntime" ) -var ( - scheme = runtime.NewScheme() //nolint: unused - setupLog = ctrl.Log.WithName("setup") -) +var setupLog = ctrl.Log.WithName("setup") const ( LeaderLockName = "kserve-controller-manager-leader-lock" @@ -125,9 +122,11 @@ func main() { setupLog.Info("Setting up manager") mgr, err := manager.New(cfg, manager.Options{ Metrics: metricsserver.Options{ - BindAddress: options.metricsAddr}, + BindAddress: options.metricsAddr, + }, WebhookServer: webhook.NewServer(webhook.Options{ - Port: options.webhookPort}), + Port: options.webhookPort, + }), LeaderElection: options.enableLeaderElection, LeaderElectionID: LeaderLockName, HealthProbeBindAddress: options.probeAddr, @@ -151,12 +150,17 @@ func main() { os.Exit(1) } - deployConfig, err := v1beta1.NewDeployConfig(clientSet) + isvcConfigMap, err := v1beta1.GetInferenceServiceConfigMap(context.Background(), clientSet) + if err != nil { + setupLog.Error(err, "unable to get configmap", "name", constants.InferenceServiceConfigMapName, "namespace", constants.KServeNamespace) + os.Exit(1) + } + deployConfig, err := v1beta1.NewDeployConfig(isvcConfigMap) if err != nil { setupLog.Error(err, "unable to get deploy config.") os.Exit(1) } - ingressConfig, err := v1beta1.NewIngressConfig(clientSet) + ingressConfig, err := v1beta1.NewIngressConfig(isvcConfigMap) if err != nil { setupLog.Error(err, "unable to get ingress config.") os.Exit(1) @@ -189,8 +193,14 @@ func main() { } } + setupLog.Info("Setting up gateway api scheme") + if err := gatewayapiv1.Install(mgr.GetScheme()); err != nil { + setupLog.Error(err, "unable to add Gateway APIs to scheme") + os.Exit(1) + } + setupLog.Info("Setting up core scheme") - if err := v1.AddToScheme(mgr.GetScheme()); err != nil { + if err := corev1.AddToScheme(mgr.GetScheme()); err != nil { setupLog.Error(err, "unable to add Core APIs to scheme") os.Exit(1) } @@ -205,7 +215,7 @@ func main() { Log: ctrl.Log.WithName("v1beta1Controllers").WithName("InferenceService"), Scheme: mgr.GetScheme(), Recorder: eventBroadcaster.NewRecorder( - mgr.GetScheme(), v1.EventSource{Component: "v1beta1Controllers"}), + mgr.GetScheme(), corev1.EventSource{Component: "v1beta1Controllers"}), }).SetupWithManager(mgr, deployConfig, ingressConfig); err != nil { setupLog.Error(err, "unable to create controller", "v1beta1Controller", "InferenceService") os.Exit(1) @@ -219,7 +229,7 @@ func main() { Client: mgr.GetClient(), Log: ctrl.Log.WithName("v1beta1Controllers").WithName("TrainedModel"), Scheme: mgr.GetScheme(), - Recorder: eventBroadcaster.NewRecorder(mgr.GetScheme(), v1.EventSource{Component: "v1beta1Controllers"}), + Recorder: eventBroadcaster.NewRecorder(mgr.GetScheme(), corev1.EventSource{Component: "v1beta1Controllers"}), ModelConfigReconciler: modelconfig.NewModelConfigReconciler(mgr.GetClient(), clientSet, mgr.GetScheme()), }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "v1beta1Controllers", "TrainedModel") @@ -235,7 +245,7 @@ func main() { Clientset: clientSet, Log: ctrl.Log.WithName("v1alpha1Controllers").WithName("InferenceGraph"), Scheme: mgr.GetScheme(), - Recorder: eventBroadcaster.NewRecorder(mgr.GetScheme(), v1.EventSource{Component: "InferenceGraphController"}), + Recorder: eventBroadcaster.NewRecorder(mgr.GetScheme(), corev1.EventSource{Component: "InferenceGraphController"}), }).SetupWithManager(mgr, deployConfig); err != nil { setupLog.Error(err, "unable to create controller", "v1alpha1Controllers", "InferenceGraph") os.Exit(1) diff --git a/cmd/manager/main_test.go b/cmd/manager/main_test.go index a3fca5ab8e1..e15715d8927 100644 --- a/cmd/manager/main_test.go +++ b/cmd/manager/main_test.go @@ -36,39 +36,53 @@ func TestGetOptions(t *testing.T) { ExpectedOptions Options }{ {"defaults", []string{}, defaults}, - {"withWebhookPort", []string{"-webhook-port=8000"}, + { + "withWebhookPort", + []string{"-webhook-port=8000"}, Options{ metricsAddr: defaults.metricsAddr, webhookPort: 8000, enableLeaderElection: defaults.enableLeaderElection, probeAddr: defaults.probeAddr, zapOpts: defaults.zapOpts, - }}, - {"withMetricsAddr", []string{"-metrics-addr=:9090"}, + }, + }, + { + "withMetricsAddr", + []string{"-metrics-addr=:9090"}, Options{ metricsAddr: ":9090", webhookPort: defaults.webhookPort, enableLeaderElection: defaults.enableLeaderElection, probeAddr: defaults.probeAddr, zapOpts: defaults.zapOpts, - }}, - {"withEnableLeaderElection", []string{"-leader-elect=true"}, + }, + }, + { + "withEnableLeaderElection", + []string{"-leader-elect=true"}, Options{ metricsAddr: defaults.metricsAddr, webhookPort: defaults.webhookPort, enableLeaderElection: true, probeAddr: defaults.probeAddr, zapOpts: defaults.zapOpts, - }}, - {"withHealthProbeAddr", []string{"-health-probe-addr=:8090"}, + }, + }, + { + "withHealthProbeAddr", + []string{"-health-probe-addr=:8090"}, Options{ metricsAddr: defaults.metricsAddr, webhookPort: defaults.webhookPort, enableLeaderElection: defaults.enableLeaderElection, probeAddr: ":8090", zapOpts: defaults.zapOpts, - }}, - {"withZapFlags", []string{"-zap-devel"}, + }, + }, + { + "withZapFlags", + []string{"-zap-devel"}, Options{ metricsAddr: defaults.metricsAddr, webhookPort: defaults.webhookPort, @@ -77,16 +91,22 @@ func TestGetOptions(t *testing.T) { zapOpts: zap.Options{ Development: true, }, - }}, - {"withSeveral", []string{"-webhook-port=8000", "-leader-elect=true"}, + }, + }, + { + "withSeveral", + []string{"-webhook-port=8000", "-leader-elect=true"}, Options{ metricsAddr: defaults.metricsAddr, webhookPort: 8000, enableLeaderElection: true, probeAddr: defaults.probeAddr, zapOpts: defaults.zapOpts, - }}, - {"withAll", []string{"-metrics-addr=:9090", "-webhook-port=8000", "-leader-elect=true", "-health-probe-addr=:8080", "-zap-devel"}, + }, + }, + { + "withAll", + []string{"-metrics-addr=:9090", "-webhook-port=8000", "-leader-elect=true", "-health-probe-addr=:8080", "-zap-devel"}, Options{ metricsAddr: ":9090", webhookPort: 8000, @@ -95,7 +115,8 @@ func TestGetOptions(t *testing.T) { zapOpts: zap.Options{ Development: true, }, - }}, + }, + }, } for _, tc := range cases { diff --git a/cmd/router/main.go b/cmd/router/main.go index 01f5e7a4604..75e9ca14088 100644 --- a/cmd/router/main.go +++ b/cmd/router/main.go @@ -18,37 +18,112 @@ package main import ( "bytes" + "context" + "crypto/rand" "encoding/json" goerrors "errors" "fmt" "io" + "math/big" "net/http" + "net/url" "os" + "os/signal" "regexp" "strconv" "strings" + "syscall" "time" - "github.com/kserve/kserve/pkg/constants" "github.com/pkg/errors" - + flag "github.com/spf13/pflag" "github.com/tidwall/gjson" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/log/zap" - "crypto/rand" - "math/big" - "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" - flag "github.com/spf13/pflag" + "github.com/kserve/kserve/pkg/constants" ) var log = logf.Log.WithName("InferenceGraphRouter") +// _isInMesh is an auxiliary global variable for isInIstioMesh function. +var _isInMesh *bool + +// isInIstioMesh checks if the InferenceGraph pod belongs to the mesh by +// checking the presence of the sidecar. It is known that when the sidecar +// is present, Envoy will be using port 15000 with standard HTTP. Thus, the +// presence of the sidecar is assumed if this port responds with an HTTP 200 status +// when doing a "GET /" request. +// +// The result of the check is cached in the _isInMesh global variable. Since a +// pod cannot be modified, a single check is enough for the whole life of the pod. +// The check cannot be done at start-up, because there is the possibility of +// false negatives, since there is no guarantee that the Istio sidecar has already +// started. So, the isInIstioMesh func should be used after the first inference +// request is received when it is guaranteed that the Istio sidecar is in ready state. +// +// Reference: +// - https://istio.io/latest/docs/ops/deployment/application-requirements/#ports-used-by-istio) +func isInIstioMesh() (bool, error) { + if _isInMesh != nil { + return *_isInMesh, nil + } + + isInMesh := false + client := http.Client{ + Timeout: time.Second * 3, + } + response, err := client.Get("http://localhost:15000") + if err == nil { + if response.StatusCode == http.StatusOK { + isInMesh = true + } + } else if errors.Is(err, syscall.ECONNREFUSED) { + // Assume no Istio sidecar. Thus, this pod is not + // part of the mesh. + err = nil + } + + if response != nil && response.Body != nil { + err = response.Body.Close() + } + + _isInMesh = &isInMesh + return *_isInMesh, err +} + func callService(serviceUrl string, input []byte, headers http.Header) ([]byte, int, error) { defer timeTrack(time.Now(), "step", serviceUrl) log.Info("Entering callService", "url", serviceUrl) - req, err := http.NewRequest("POST", serviceUrl, bytes.NewBuffer(input)) + + parsedServiceUrl, parseServiceUrlErr := url.Parse(serviceUrl) + if parseServiceUrlErr != nil { + return nil, 500, parseServiceUrlErr + } + if parsedServiceUrl.Scheme == "https" { + if isInMesh, isInMeshErr := isInIstioMesh(); isInMeshErr != nil { + return nil, 500, isInMeshErr + } else if isInMesh { + // In this branch, it has been resolved that the Inference Graph is + // part of the Istio mesh. In this case, even if the target service + // is using HTTPS, it is better to use plain-text HTTP: + // * If the target service is also part of the mesh, Istio will take + // care of properly applying TLS policies (e.g. mutual TLS). + // * If the target service is _not_ part of the mesh, it still is better + // to let Istio manage TLS by configuring the sidecar to do TLS + // origination and prevent double TLS (see: https://istio.io/latest/docs/ops/common-problems/network-issues/#double-tls) + // + // If the Inference Graph is not part of the mesh, the indicated + // schema is used. + parsedServiceUrl.Scheme = "http" + serviceUrl = parsedServiceUrl.String() + + log.Info("Using plain-text schema to let Istio manage TLS termination", "url", serviceUrl) + } + } + + req, err := http.NewRequest(http.MethodPost, serviceUrl, bytes.NewBuffer(input)) if err != nil { log.Error(err, "An error occurred while preparing request object with serviceUrl.", "serviceUrl", serviceUrl) return nil, 500, err @@ -73,7 +148,6 @@ func callService(serviceUrl string, input []byte, headers http.Header) ([]byte, req.Header.Add("Content-Type", "application/json") } resp, err := http.DefaultClient.Do(req) - if err != nil { log.Error(err, "An error has occurred while calling service", "service", serviceUrl) return nil, 500, err @@ -244,7 +318,7 @@ func routeStep(nodeName string, graph v1alpha1.InferenceGraphSpec, input []byte, if step.Condition != "" { if !gjson.ValidBytes(responseBytes) { - return nil, 500, fmt.Errorf("invalid response") + return nil, 500, errors.New("invalid response") } // if the condition does not match for the step in the sequence we stop and return the response if !gjson.GetBytes(responseBytes, step.Condition).Exists() { @@ -335,9 +409,21 @@ func compilePatterns(patterns []string) ([]*regexp.Regexp, error) { return compiled, goerrors.Join(allErrors...) } +// Mainly used for kubernetes readiness probe. It responds with "503 shutting down" if server is shutting down, +// otherwise returns "200 OK". +func readyHandler(w http.ResponseWriter, req *http.Request) { + if isShuttingDown { + http.Error(w, "shutting down", http.StatusServiceUnavailable) + } else { + w.WriteHeader(http.StatusOK) + } +} + var ( jsonGraph = flag.String("graph-json", "", "serialized json graph def") compiledHeaderPatterns []*regexp.Regexp + isShuttingDown = false + drainSleepDuration = 30 * time.Second ) func main() { @@ -360,18 +446,44 @@ func main() { } http.HandleFunc("/", graphHandler) + http.HandleFunc(constants.RouterReadinessEndpoint, readyHandler) server := &http.Server{ - Addr: ":8080", // specify the address and port - Handler: http.HandlerFunc(graphHandler), // specify your HTTP handler - ReadTimeout: time.Minute, // set the maximum duration for reading the entire request, including the body - WriteTimeout: time.Minute, // set the maximum duration before timing out writes of the response - IdleTimeout: 3 * time.Minute, // set the maximum amount of time to wait for the next request when keep-alives are enabled + Addr: ":" + strconv.Itoa(constants.RouterPort), + Handler: nil, // default server mux + ReadTimeout: time.Minute, // https://medium.com/a-journey-with-go/go-understand-and-mitigate-slowloris-attack-711c1b1403f6 + WriteTimeout: time.Minute, // set the maximum duration before timing out writes of the response + IdleTimeout: 3 * time.Minute, // set the maximum amount of time to wait for the next request when keep-alives are enabled } - err = server.ListenAndServe() - if err != nil { - log.Error(err, "failed to listen on 8080") + go func() { + err = server.ListenAndServe() + if err != nil && !errors.Is(err, http.ErrServerClosed) { + log.Error(err, fmt.Sprintf("Failed to serve on address %v", server.Addr)) + os.Exit(1) + } + }() + + // Blocks until SIGTERM or SIGINT is received + handleSignals(server) +} + +func handleSignals(server *http.Server) { + signalChan := make(chan os.Signal, 1) + signal.Notify(signalChan, os.Interrupt, syscall.SIGTERM) + + sig := <-signalChan + log.Info("Received shutdown signal", "signal", sig) + // Fail the readiness probe + isShuttingDown = true + log.Info(fmt.Sprintf("Sleeping %v to allow K8s propagation of non-ready state", drainSleepDuration)) + // Sleep to give networking a little bit more time to remove the pod + // from its configuration and propagate that to all loadbalancers and nodes. + time.Sleep(drainSleepDuration) + // Shut down the server gracefully + if err := server.Shutdown(context.Background()); err != nil { + log.Error(err, "Failed to shutdown the server gracefully") os.Exit(1) } + log.Info("Server gracefully shutdown") } diff --git a/cmd/router/main_test.go b/cmd/router/main_test.go index 6d86c4b84d3..5b8d40c35bb 100644 --- a/cmd/router/main_test.go +++ b/cmd/router/main_test.go @@ -19,16 +19,19 @@ package main import ( "encoding/json" "fmt" - "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" - "github.com/stretchr/testify/assert" "io" - "knative.dev/pkg/apis" "net/http" "net/http/httptest" "regexp" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "knative.dev/pkg/apis" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/log/zap" - "testing" + + "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" ) func init() { @@ -44,7 +47,13 @@ func TestSimpleModelChainer(t *testing.T) { } response := map[string]interface{}{"predictions": "1"} responseBytes, err := json.Marshal(response) + if err != nil { + t.Fatalf("failed to marshal response: %v", err) + } _, err = rw.Write(responseBytes) + if err != nil { + t.Fatalf("failed to write response: %v", err) + } })) model1Url, err := apis.ParseURL(model1.URL) if err != nil { @@ -58,7 +67,13 @@ func TestSimpleModelChainer(t *testing.T) { } response := map[string]interface{}{"predictions": "2"} responseBytes, err := json.Marshal(response) + if err != nil { + t.Fatalf("failed to marshal response: %v", err) + } _, err = rw.Write(responseBytes) + if err != nil { + t.Fatalf("failed to write response: %v", err) + } })) model2Url, err := apis.ParseURL(model2.URL) if err != nil { @@ -100,8 +115,14 @@ func TestSimpleModelChainer(t *testing.T) { } res, _, err := routeStep("root", graphSpec, jsonBytes, headers) + if err != nil { + t.Fatalf("routeStep failed: %v", err) + } var response map[string]interface{} err = json.Unmarshal(res, &response) + if err != nil { + t.Fatalf("json.Unmarshal failed: %v", err) + } expectedResponse := map[string]interface{}{ "predictions": "2", } @@ -118,7 +139,13 @@ func TestSimpleModelEnsemble(t *testing.T) { } response := map[string]interface{}{"predictions": "1"} responseBytes, err := json.Marshal(response) + if err != nil { + t.Fatalf("failed to marshal response: %v", err) + } _, err = rw.Write(responseBytes) + if err != nil { + t.Fatalf("failed to write response: %v", err) + } })) model1Url, err := apis.ParseURL(model1.URL) if err != nil { @@ -128,11 +155,17 @@ func TestSimpleModelEnsemble(t *testing.T) { model2 := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { _, err := io.ReadAll(req.Body) if err != nil { - return + t.Fatalf("failed to read request body: %v", err) } response := map[string]interface{}{"predictions": "2"} responseBytes, err := json.Marshal(response) + if err != nil { + t.Fatalf("failed to marshal response: %v", err) + } _, err = rw.Write(responseBytes) + if err != nil { + t.Fatalf("failed to write response: %v", err) + } })) model2Url, err := apis.ParseURL(model2.URL) if err != nil { @@ -172,8 +205,14 @@ func TestSimpleModelEnsemble(t *testing.T) { "Authorization": {"Bearer Token"}, } res, _, err := routeStep("root", graphSpec, jsonBytes, headers) + if err != nil { + t.Fatalf("routeStep failed: %v", err) + } var response map[string]interface{} err = json.Unmarshal(res, &response) + if err != nil { + t.Fatalf("json.Unmarshal failed: %v", err) + } expectedResponse := map[string]interface{}{ "model1": map[string]interface{}{ "predictions": "1", @@ -204,7 +243,13 @@ func TestInferenceGraphWithCondition(t *testing.T) { }, } responseBytes, err := json.Marshal(response) + if err != nil { + t.Fatalf("failed to marshal response: %v", err) + } _, err = rw.Write(responseBytes) + if err != nil { + t.Fatalf("failed to write response: %v", err) + } })) model1Url, err := apis.ParseURL(model1.URL) if err != nil { @@ -227,7 +272,13 @@ func TestInferenceGraphWithCondition(t *testing.T) { }, } responseBytes, err := json.Marshal(response) + if err != nil { + t.Fatalf("failed to marshal response: %v", err) + } _, err = rw.Write(responseBytes) + if err != nil { + t.Fatalf("failed to write response: %v", err) + } })) model2Url, err := apis.ParseURL(model2.URL) if err != nil { @@ -252,7 +303,16 @@ func TestInferenceGraphWithCondition(t *testing.T) { }, } responseBytes, err := json.Marshal(response) + if err != nil { + t.Fatalf("failed to marshal response: %v", err) + } + if err != nil { + t.Fatalf("failed to marshal response: %v", err) + } _, err = rw.Write(responseBytes) + if err != nil { + t.Fatalf("failed to write response: %v", err) + } })) model3Url, err := apis.ParseURL(model3.URL) if err != nil { @@ -275,7 +335,13 @@ func TestInferenceGraphWithCondition(t *testing.T) { }, } responseBytes, err := json.Marshal(response) + if err != nil { + t.Fatalf("failed to marshal response: %v", err) + } _, err = rw.Write(responseBytes) + if err != nil { + t.Fatalf("failed to write response: %v", err) + } })) model4Url, err := apis.ParseURL(model4.URL) if err != nil { @@ -351,8 +417,14 @@ func TestInferenceGraphWithCondition(t *testing.T) { "Authorization": {"Bearer Token"}, } res, _, err := routeStep("root", graphSpec, jsonBytes, headers) + if err != nil { + t.Fatalf("routeStep failed: %v", err) + } var response map[string]interface{} err = json.Unmarshal(res, &response) + if err != nil { + t.Fatalf("json.Unmarshal failed: %v", err) + } expectedModel3Response := map[string]interface{}{ "predictions": []interface{}{ map[string]interface{}{ @@ -399,7 +471,13 @@ func TestCallServiceWhenNoneHeadersToPropagateIsEmpty(t *testing.T) { } } responseBytes, err := json.Marshal(response) + if err != nil { + t.Fatalf("failed to marshal response: %v", err) + } _, err = rw.Write(responseBytes) + if err != nil { + t.Fatalf("failed to write response: %v", err) + } })) model1Url, err := apis.ParseURL(model1.URL) if err != nil { @@ -421,8 +499,14 @@ func TestCallServiceWhenNoneHeadersToPropagateIsEmpty(t *testing.T) { // Propagating no header compiledHeaderPatterns = []*regexp.Regexp{} res, _, err := callService(model1Url.String(), jsonBytes, headers) + if err != nil { + t.Fatalf("callService failed: %v", err) + } var response map[string]interface{} err = json.Unmarshal(res, &response) + if err != nil { + t.Fatalf("json.Unmarshal failed: %v", err) + } expectedResponse := map[string]interface{}{ "predictions": "1", } @@ -450,7 +534,13 @@ func TestCallServiceWhen1HeaderToPropagate(t *testing.T) { } } responseBytes, err := json.Marshal(response) + if err != nil { + t.Fatalf("failed to marshal response: %v", err) + } _, err = rw.Write(responseBytes) + if err != nil { + t.Fatalf("failed to write response: %v", err) + } })) model1Url, err := apis.ParseURL(model1.URL) if err != nil { @@ -472,11 +562,17 @@ func TestCallServiceWhen1HeaderToPropagate(t *testing.T) { // Propagating only 1 header "Test-Header-Key" headersToPropagate := []string{"Test-Header-Key"} compiledHeaderPatterns, err = compilePatterns(headersToPropagate) - assert.Nil(t, err) + require.NoError(t, err) res, _, err := callService(model1Url.String(), jsonBytes, headers) + if err != nil { + t.Fatalf("callService failed: %v", err) + } var response map[string]interface{} err = json.Unmarshal(res, &response) + if err != nil { + t.Fatalf("json.Unmarshal failed: %v", err) + } expectedResponse := map[string]interface{}{ "predictions": "1", "Test-Header-Key": "Test-Header-Value", @@ -505,7 +601,13 @@ func TestCallServiceWhenMultipleHeadersToPropagate(t *testing.T) { } } responseBytes, err := json.Marshal(response) + if err != nil { + t.Fatalf("failed to marshal response: %v", err) + } _, err = rw.Write(responseBytes) + if err != nil { + t.Fatalf("failed to write response: %v", err) + } })) model1Url, err := apis.ParseURL(model1.URL) if err != nil { @@ -527,11 +629,17 @@ func TestCallServiceWhenMultipleHeadersToPropagate(t *testing.T) { // Propagating multiple headers "Test-Header-Key" headersToPropagate := []string{"Test-Header-Key", "Authorization"} compiledHeaderPatterns, err = compilePatterns(headersToPropagate) - assert.Nil(t, err) + require.NoError(t, err) res, _, err := callService(model1Url.String(), jsonBytes, headers) + if err != nil { + t.Fatalf("callService failed: %v", err) + } var response map[string]interface{} err = json.Unmarshal(res, &response) + if err != nil { + t.Fatalf("json.Unmarshal failed: %v", err) + } expectedResponse := map[string]interface{}{ "predictions": "1", "Test-Header-Key": "Test-Header-Value", @@ -569,7 +677,13 @@ func TestCallServiceWhenMultipleHeadersToPropagateUsingPatterns(t *testing.T) { } } responseBytes, err := json.Marshal(response) + if err != nil { + t.Fatalf("failed to marshal response: %v", err) + } _, err = rw.Write(responseBytes) + if err != nil { + t.Fatalf("failed to write response: %v", err) + } })) model1Url, err := apis.ParseURL(model1.URL) if err != nil { @@ -593,11 +707,17 @@ func TestCallServiceWhenMultipleHeadersToPropagateUsingPatterns(t *testing.T) { // Propagating multiple headers "Test-Header-Key" headersToPropagate := []string{"Test-Header-*", "Auth*"} compiledHeaderPatterns, err = compilePatterns(headersToPropagate) - assert.Nil(t, err) + require.NoError(t, err) res, _, err := callService(model1Url.String(), jsonBytes, headers) + if err != nil { + t.Fatalf("callService failed: %v", err) + } var response map[string]interface{} err = json.Unmarshal(res, &response) + if err != nil { + t.Fatalf("json.Unmarshal failed: %v", err) + } expectedResponse := map[string]interface{}{ "predictions": "1", "Test-Header-1": "Test-Header-1", @@ -629,7 +749,13 @@ func TestCallServiceWhenMultipleHeadersToPropagateUsingInvalidPattern(t *testing } } responseBytes, err := json.Marshal(response) + if err != nil { + t.Fatalf("failed to marshal response: %v", err) + } _, err = rw.Write(responseBytes) + if err != nil { + t.Fatalf("failed to write response: %v", err) + } })) model1Url, err := apis.ParseURL(model1.URL) if err != nil { @@ -653,11 +779,17 @@ func TestCallServiceWhenMultipleHeadersToPropagateUsingInvalidPattern(t *testing // Using invalid regex pattern headersToPropagate := []string{"Test-Header-[0-9", "Auth*"} compiledHeaderPatterns, err = compilePatterns(headersToPropagate) - assert.NotNil(t, err) + require.Error(t, err) res, _, err := callService(model1Url.String(), jsonBytes, headers) + if err != nil { + t.Fatalf("callService failed: %v", err) + } var response map[string]interface{} err = json.Unmarshal(res, &response) + if err != nil { + t.Fatalf("json.Unmarshal failed: %v", err) + } // Invalid pattern should be ignored. expectedResponse := map[string]interface{}{ "predictions": "1", diff --git a/cmd/spec-gen/main.go b/cmd/spec-gen/main.go index ebab0f8e823..270ed4db716 100644 --- a/cmd/spec-gen/main.go +++ b/cmd/spec-gen/main.go @@ -22,10 +22,11 @@ import ( "os" "strings" - kserve "github.com/kserve/kserve/pkg/openapi" "k8s.io/klog/v2" "k8s.io/kube-openapi/pkg/common" spec "k8s.io/kube-openapi/pkg/validation/spec" + + kserve "github.com/kserve/kserve/pkg/openapi" ) // Generate OpenAPI spec definitions for InferenceService Resource diff --git a/config/configmap/inferenceservice.yaml b/config/configmap/inferenceservice.yaml index 19d42279f92..81052313ea9 100644 --- a/config/configmap/inferenceservice.yaml +++ b/config/configmap/inferenceservice.yaml @@ -41,7 +41,38 @@ data: "defaultImageVersion": "latest" } } - + # ====================================== ISVC CONFIGURATION ====================================== + # Example + inferenceService: |- + { + "serviceAnnotationDisallowedList": [ + "my.custom.annotation/1" + ], + "serviceLabelDisallowedList": [ + "my.custom.label.1" + ] + } + # Example of isvc configuration + inferenceService: |- + { + # ServiceAnnotationDisallowedList is a list of annotations that are not allowed to be propagated to Knative + # revisions, which prevents the reconciliation loop to be triggered if the annotations is + # configured here are used. + # Default values are: + # "autoscaling.knative.dev/min-scale", + # "autoscaling.knative.dev/max-scale", + # "internal.serving.kserve.io/storage-initializer-sourceuri", + # "kubectl.kubernetes.io/last-applied-configuration" + # Any new value will be appended to the list. + "serviceAnnotationDisallowedList": [ + "my.custom.annotation/1" + ], + # ServiceLabelDisallowedList is a list of labels that are not allowed to be propagated to Knative revisions + # which prevents the reconciliation loop to be triggered if the labels is configured here are used. + "serviceLabelDisallowedList": [ + "my.custom.label.1" + ] + } # ====================================== STORAGE INITIALIZER CONFIGURATION ====================================== # Example storageInitializer: |- @@ -190,7 +221,9 @@ data: # ====================================== INGRESS CONFIGURATION ====================================== # Example ingress: |- - { + { + "enableGatewayApi": false, + "kserveIngressGateway": "kserve/kserve-ingress-gateway", "ingressGateway" : "knative-serving/knative-ingress-gateway", "localGateway" : "knative-serving/knative-local-gateway", "localGatewayService" : "knative-local-gateway.istio-system.svc.cluster.local", @@ -203,7 +236,17 @@ data: "disableIngressCreation": false } ingress: |- - { + { + # enableGatewayApi specifies whether to use Gateway API instead of Ingress to serve external traffic. + "enableGatewayApi": false, + + # KServe implements [Gateway API](https://gateway-api.sigs.k8s.io/) to serve external traffic. + # By default, KServe configures a default gateway to serve external traffic. + # But, KServe can be configured to use a custom gateway by modifying this configuration. + # The gateway should be specified in format / + # NOTE: This configuration only applicable for raw deployment. + "kserveIngressGateway": "kserve/kserve-ingress-gateway", + # ingressGateway specifies the ingress gateway to serve external traffic. # The gateway should be specified in format / # NOTE: This configuration only applicable for serverless deployment with Istio configured as network layer. @@ -480,8 +523,42 @@ data: "jobTTLSecondsAfterFinished": 3600, # The frequency at which the local model agent reconciles the local models # This is to detect if models are missing from local disk - "reconcilationFrequencyInSecs": 60 + "reconcilationFrequencyInSecs": 60, + # This is to disable localmodel pv and pvc management for namespaces without isvcs + "disableVolumeManagement": false } + + # ====================================== LOCALMODEL CONFIGURATION ====================================== + # Example + inferenceservice: |- + { + "resource": { + "cpuLimit": "1", + "memoryLimit": "2Gi", + "cpuRequest": "1", + "memoryRequest": "2Gi" + } + } + inferenceservice: |- + { + # resource contains the default resource configuration for the inference service. + # you can override this configuration by specifying the resources in the inference service yaml. + # If you want to unbound the resource (limits and requests), you can set the value to null or "" + # or just remove the specific field from the config. + "resource": { + # cpuLimit is the limits.cpu to set for the inference service. + "cpuLimit": "1", + + # memoryLimit is the limits.memory to set for the inference service. + "memoryLimit": "2Gi", + + # cpuRequest is the requests.cpu to set for the inference service. + "cpuRequest": "1", + + # memoryRequest is the requests.memory to set for the inference service. + "memoryRequest": "2Gi" + } + } explainers: |- { @@ -528,7 +605,9 @@ data: } ingress: |- - { + { + "enableGatewayApi": false, + "kserveIngressGateway": "kserve/kserve-ingress-gateway", "ingressGateway" : "knative-serving/knative-ingress-gateway", "localGateway" : "knative-serving/knative-local-gateway", "localGatewayService" : "knative-local-gateway.istio-system.svc.cluster.local", @@ -602,3 +681,13 @@ data: { "autoMountServiceAccountToken": true } + + inferenceService: |- + { + "resource": { + "cpuLimit": "1", + "memoryLimit": "2Gi", + "cpuRequest": "1", + "memoryRequest": "2Gi" + } + } diff --git a/config/crd/full/serving.kserve.io_clusterservingruntimes.yaml b/config/crd/full/serving.kserve.io_clusterservingruntimes.yaml index ae9e15eed33..556950f31ba 100644 --- a/config/crd/full/serving.kserve.io_clusterservingruntimes.yaml +++ b/config/crd/full/serving.kserve.io_clusterservingruntimes.yaml @@ -1002,6 +1002,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -1336,10 +1338,12 @@ spec: diskURI: type: string fsType: + default: ext4 type: string kind: type: string readOnly: + default: false type: boolean required: - diskName @@ -1699,6 +1703,13 @@ spec: required: - path type: object + image: + properties: + pullPolicy: + type: string + reference: + type: string + type: object iscsi: properties: chapAuthDiscovery: @@ -1712,6 +1723,7 @@ spec: iqn: type: string iscsiInterface: + default: default type: string lun: format: int32 @@ -1960,6 +1972,7 @@ spec: image: type: string keyring: + default: /etc/ceph/keyring type: string monitors: items: @@ -1967,6 +1980,7 @@ spec: type: array x-kubernetes-list-type: atomic pool: + default: rbd type: string readOnly: type: boolean @@ -1978,6 +1992,7 @@ spec: type: object x-kubernetes-map-type: atomic user: + default: admin type: string required: - image @@ -1986,6 +2001,7 @@ spec: scaleIO: properties: fsType: + default: xfs type: string gateway: type: string @@ -2003,6 +2019,7 @@ spec: sslEnabled: type: boolean storageMode: + default: ThinProvisioned type: string storagePool: type: string @@ -2964,6 +2981,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -3264,10 +3283,12 @@ spec: diskURI: type: string fsType: + default: ext4 type: string kind: type: string readOnly: + default: false type: boolean required: - diskName @@ -3627,6 +3648,13 @@ spec: required: - path type: object + image: + properties: + pullPolicy: + type: string + reference: + type: string + type: object iscsi: properties: chapAuthDiscovery: @@ -3640,6 +3668,7 @@ spec: iqn: type: string iscsiInterface: + default: default type: string lun: format: int32 @@ -3888,6 +3917,7 @@ spec: image: type: string keyring: + default: /etc/ceph/keyring type: string monitors: items: @@ -3895,6 +3925,7 @@ spec: type: array x-kubernetes-list-type: atomic pool: + default: rbd type: string readOnly: type: boolean @@ -3906,6 +3937,7 @@ spec: type: object x-kubernetes-map-type: atomic user: + default: admin type: string required: - image @@ -3914,6 +3946,7 @@ spec: scaleIO: properties: fsType: + default: xfs type: string gateway: type: string @@ -3931,6 +3964,7 @@ spec: sslEnabled: type: boolean storageMode: + default: ThinProvisioned type: string storagePool: type: string diff --git a/config/crd/full/serving.kserve.io_clusterstoragecontainers.yaml b/config/crd/full/serving.kserve.io_clusterstoragecontainers.yaml index 2a55ab532fe..e48c2660370 100644 --- a/config/crd/full/serving.kserve.io_clusterstoragecontainers.yaml +++ b/config/crd/full/serving.kserve.io_clusterstoragecontainers.yaml @@ -470,6 +470,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object diff --git a/config/crd/full/serving.kserve.io_inferencegraphs.yaml b/config/crd/full/serving.kserve.io_inferencegraphs.yaml index c1a577f549b..b1628bc70fe 100644 --- a/config/crd/full/serving.kserve.io_inferencegraphs.yaml +++ b/config/crd/full/serving.kserve.io_inferencegraphs.yaml @@ -478,8 +478,10 @@ spec: type: object type: object maxReplicas: + format: int32 type: integer minReplicas: + format: int32 type: integer nodes: additionalProperties: @@ -527,6 +529,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -559,6 +563,7 @@ spec: - rps type: string scaleTarget: + format: int32 type: integer timeout: format: int64 diff --git a/config/crd/full/serving.kserve.io_inferenceservices.yaml b/config/crd/full/serving.kserve.io_inferenceservices.yaml index 91fca492cca..63ac7a59f3e 100644 --- a/config/crd/full/serving.kserve.io_inferenceservices.yaml +++ b/config/crd/full/serving.kserve.io_inferenceservices.yaml @@ -936,6 +936,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -1643,6 +1645,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -2399,6 +2403,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -2654,8 +2660,10 @@ spec: type: string type: object maxReplicas: + format: int32 type: integer minReplicas: + format: int32 type: integer nodeName: type: string @@ -2698,13 +2706,10 @@ spec: properties: name: type: string - source: - properties: - resourceClaimName: - type: string - resourceClaimTemplateName: - type: string - type: object + resourceClaimName: + type: string + resourceClaimTemplateName: + type: string required: - name type: object @@ -2712,6 +2717,39 @@ spec: x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object restartPolicy: type: string runtimeClassName: @@ -2724,6 +2762,7 @@ spec: - rps type: string scaleTarget: + format: int32 type: integer schedulerName: type: string @@ -2763,6 +2802,8 @@ spec: runAsUser: format: int64 type: integer + seLinuxChangePolicy: + type: string seLinuxOptions: properties: level: @@ -2789,6 +2830,8 @@ spec: type: integer type: array x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + type: string sysctls: items: properties: @@ -2930,10 +2973,12 @@ spec: diskURI: type: string fsType: + default: ext4 type: string kind: type: string readOnly: + default: false type: boolean required: - diskName @@ -3293,6 +3338,13 @@ spec: required: - path type: object + image: + properties: + pullPolicy: + type: string + reference: + type: string + type: object iscsi: properties: chapAuthDiscovery: @@ -3306,6 +3358,7 @@ spec: iqn: type: string iscsiInterface: + default: default type: string lun: format: int32 @@ -3554,6 +3607,7 @@ spec: image: type: string keyring: + default: /etc/ceph/keyring type: string monitors: items: @@ -3561,6 +3615,7 @@ spec: type: array x-kubernetes-list-type: atomic pool: + default: rbd type: string readOnly: type: boolean @@ -3572,6 +3627,7 @@ spec: type: object x-kubernetes-map-type: atomic user: + default: admin type: string required: - image @@ -3580,6 +3636,7 @@ spec: scaleIO: properties: fsType: + default: xfs type: string gateway: type: string @@ -3597,6 +3654,7 @@ spec: sslEnabled: type: boolean storageMode: + default: ThinProvisioned type: string storagePool: type: string @@ -4575,6 +4633,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -5315,6 +5375,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -6016,6 +6078,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -6691,6 +6755,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -6956,8 +7022,10 @@ spec: type: string type: object maxReplicas: + format: int32 type: integer minReplicas: + format: int32 type: integer model: properties: @@ -7404,6 +7472,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -8098,6 +8168,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -8796,6 +8868,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -9481,6 +9555,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -10173,6 +10249,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -10436,13 +10514,10 @@ spec: properties: name: type: string - source: - properties: - resourceClaimName: - type: string - resourceClaimTemplateName: - type: string - type: object + resourceClaimName: + type: string + resourceClaimTemplateName: + type: string required: - name type: object @@ -10450,6 +10525,39 @@ spec: x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object restartPolicy: type: string runtimeClassName: @@ -10462,6 +10570,7 @@ spec: - rps type: string scaleTarget: + format: int32 type: integer schedulerName: type: string @@ -10501,6 +10610,8 @@ spec: runAsUser: format: int64 type: integer + seLinuxChangePolicy: + type: string seLinuxOptions: properties: level: @@ -10527,6 +10638,8 @@ spec: type: integer type: array x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + type: string sysctls: items: properties: @@ -10996,6 +11109,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -11683,6 +11798,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -12448,6 +12565,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -12723,10 +12842,12 @@ spec: diskURI: type: string fsType: + default: ext4 type: string kind: type: string readOnly: + default: false type: boolean required: - diskName @@ -13086,6 +13207,13 @@ spec: required: - path type: object + image: + properties: + pullPolicy: + type: string + reference: + type: string + type: object iscsi: properties: chapAuthDiscovery: @@ -13099,6 +13227,7 @@ spec: iqn: type: string iscsiInterface: + default: default type: string lun: format: int32 @@ -13347,6 +13476,7 @@ spec: image: type: string keyring: + default: /etc/ceph/keyring type: string monitors: items: @@ -13354,6 +13484,7 @@ spec: type: array x-kubernetes-list-type: atomic pool: + default: rbd type: string readOnly: type: boolean @@ -13365,6 +13496,7 @@ spec: type: object x-kubernetes-map-type: atomic user: + default: admin type: string required: - image @@ -13373,6 +13505,7 @@ spec: scaleIO: properties: fsType: + default: xfs type: string gateway: type: string @@ -13390,6 +13523,7 @@ spec: sslEnabled: type: boolean storageMode: + default: ThinProvisioned type: string storagePool: type: string @@ -14352,6 +14486,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -15057,6 +15193,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -15770,6 +15908,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -16050,13 +16190,10 @@ spec: properties: name: type: string - source: - properties: - resourceClaimName: - type: string - resourceClaimTemplateName: - type: string - type: object + resourceClaimName: + type: string + resourceClaimTemplateName: + type: string required: - name type: object @@ -16064,6 +16201,39 @@ spec: x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object restartPolicy: type: string runtimeClassName: @@ -16106,6 +16276,8 @@ spec: runAsUser: format: int64 type: integer + seLinuxChangePolicy: + type: string seLinuxOptions: properties: level: @@ -16132,6 +16304,8 @@ spec: type: integer type: array x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + type: string sysctls: items: properties: @@ -16272,10 +16446,12 @@ spec: diskURI: type: string fsType: + default: ext4 type: string kind: type: string readOnly: + default: false type: boolean required: - diskName @@ -16635,6 +16811,13 @@ spec: required: - path type: object + image: + properties: + pullPolicy: + type: string + reference: + type: string + type: object iscsi: properties: chapAuthDiscovery: @@ -16648,6 +16831,7 @@ spec: iqn: type: string iscsiInterface: + default: default type: string lun: format: int32 @@ -16896,6 +17080,7 @@ spec: image: type: string keyring: + default: /etc/ceph/keyring type: string monitors: items: @@ -16903,6 +17088,7 @@ spec: type: array x-kubernetes-list-type: atomic pool: + default: rbd type: string readOnly: type: boolean @@ -16914,6 +17100,7 @@ spec: type: object x-kubernetes-map-type: atomic user: + default: admin type: string required: - image @@ -16922,6 +17109,7 @@ spec: scaleIO: properties: fsType: + default: xfs type: string gateway: type: string @@ -16939,6 +17127,7 @@ spec: sslEnabled: type: boolean storageMode: + default: ThinProvisioned type: string storagePool: type: string @@ -17449,6 +17638,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -18603,6 +18794,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -19359,6 +19552,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -19614,8 +19809,10 @@ spec: type: string type: object maxReplicas: + format: int32 type: integer minReplicas: + format: int32 type: integer nodeName: type: string @@ -19658,13 +19855,10 @@ spec: properties: name: type: string - source: - properties: - resourceClaimName: - type: string - resourceClaimTemplateName: - type: string - type: object + resourceClaimName: + type: string + resourceClaimTemplateName: + type: string required: - name type: object @@ -19672,6 +19866,39 @@ spec: x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object restartPolicy: type: string runtimeClassName: @@ -19684,6 +19911,7 @@ spec: - rps type: string scaleTarget: + format: int32 type: integer schedulerName: type: string @@ -19723,6 +19951,8 @@ spec: runAsUser: format: int64 type: integer + seLinuxChangePolicy: + type: string seLinuxOptions: properties: level: @@ -19749,6 +19979,8 @@ spec: type: integer type: array x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + type: string sysctls: items: properties: @@ -19890,10 +20122,12 @@ spec: diskURI: type: string fsType: + default: ext4 type: string kind: type: string readOnly: + default: false type: boolean required: - diskName @@ -20253,6 +20487,13 @@ spec: required: - path type: object + image: + properties: + pullPolicy: + type: string + reference: + type: string + type: object iscsi: properties: chapAuthDiscovery: @@ -20266,6 +20507,7 @@ spec: iqn: type: string iscsiInterface: + default: default type: string lun: format: int32 @@ -20514,6 +20756,7 @@ spec: image: type: string keyring: + default: /etc/ceph/keyring type: string monitors: items: @@ -20521,6 +20764,7 @@ spec: type: array x-kubernetes-list-type: atomic pool: + default: rbd type: string readOnly: type: boolean @@ -20532,6 +20776,7 @@ spec: type: object x-kubernetes-map-type: atomic user: + default: admin type: string required: - image @@ -20540,6 +20785,7 @@ spec: scaleIO: properties: fsType: + default: xfs type: string gateway: type: string @@ -20557,6 +20803,7 @@ spec: sslEnabled: type: boolean storageMode: + default: ThinProvisioned type: string storagePool: type: string diff --git a/config/crd/full/serving.kserve.io_localmodelcaches.yaml b/config/crd/full/serving.kserve.io_localmodelcaches.yaml index b14727a7726..ab92f72ab7a 100644 --- a/config/crd/full/serving.kserve.io_localmodelcaches.yaml +++ b/config/crd/full/serving.kserve.io_localmodelcaches.yaml @@ -35,7 +35,6 @@ spec: nodeGroups: items: type: string - maxItems: 1 minItems: 1 type: array sourceModelUri: diff --git a/config/crd/full/serving.kserve.io_localmodelnodegroups.yaml b/config/crd/full/serving.kserve.io_localmodelnodegroups.yaml index f7b995c34b1..51beae879b9 100644 --- a/config/crd/full/serving.kserve.io_localmodelnodegroups.yaml +++ b/config/crd/full/serving.kserve.io_localmodelnodegroups.yaml @@ -144,10 +144,12 @@ spec: diskURI: type: string fsType: + default: ext4 type: string kind: type: string readOnly: + default: false type: boolean required: - diskName @@ -396,6 +398,7 @@ spec: iqn: type: string iscsiInterface: + default: default type: string lun: format: int32 @@ -547,6 +550,7 @@ spec: image: type: string keyring: + default: /etc/ceph/keyring type: string monitors: items: @@ -554,6 +558,7 @@ spec: type: array x-kubernetes-list-type: atomic pool: + default: rbd type: string readOnly: type: boolean @@ -566,6 +571,7 @@ spec: type: object x-kubernetes-map-type: atomic user: + default: admin type: string required: - image @@ -574,6 +580,7 @@ spec: scaleIO: properties: fsType: + default: xfs type: string gateway: type: string @@ -592,6 +599,7 @@ spec: sslEnabled: type: boolean storageMode: + default: ThinProvisioned type: string storagePool: type: string diff --git a/config/crd/full/serving.kserve.io_servingruntimes.yaml b/config/crd/full/serving.kserve.io_servingruntimes.yaml index c63bee0926d..b5f5e6bc55b 100644 --- a/config/crd/full/serving.kserve.io_servingruntimes.yaml +++ b/config/crd/full/serving.kserve.io_servingruntimes.yaml @@ -1002,6 +1002,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -1336,10 +1338,12 @@ spec: diskURI: type: string fsType: + default: ext4 type: string kind: type: string readOnly: + default: false type: boolean required: - diskName @@ -1699,6 +1703,13 @@ spec: required: - path type: object + image: + properties: + pullPolicy: + type: string + reference: + type: string + type: object iscsi: properties: chapAuthDiscovery: @@ -1712,6 +1723,7 @@ spec: iqn: type: string iscsiInterface: + default: default type: string lun: format: int32 @@ -1960,6 +1972,7 @@ spec: image: type: string keyring: + default: /etc/ceph/keyring type: string monitors: items: @@ -1967,6 +1980,7 @@ spec: type: array x-kubernetes-list-type: atomic pool: + default: rbd type: string readOnly: type: boolean @@ -1978,6 +1992,7 @@ spec: type: object x-kubernetes-map-type: atomic user: + default: admin type: string required: - image @@ -1986,6 +2001,7 @@ spec: scaleIO: properties: fsType: + default: xfs type: string gateway: type: string @@ -2003,6 +2019,7 @@ spec: sslEnabled: type: boolean storageMode: + default: ThinProvisioned type: string storagePool: type: string @@ -2964,6 +2981,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -3264,10 +3283,12 @@ spec: diskURI: type: string fsType: + default: ext4 type: string kind: type: string readOnly: + default: false type: boolean required: - diskName @@ -3627,6 +3648,13 @@ spec: required: - path type: object + image: + properties: + pullPolicy: + type: string + reference: + type: string + type: object iscsi: properties: chapAuthDiscovery: @@ -3640,6 +3668,7 @@ spec: iqn: type: string iscsiInterface: + default: default type: string lun: format: int32 @@ -3888,6 +3917,7 @@ spec: image: type: string keyring: + default: /etc/ceph/keyring type: string monitors: items: @@ -3895,6 +3925,7 @@ spec: type: array x-kubernetes-list-type: atomic pool: + default: rbd type: string readOnly: type: boolean @@ -3906,6 +3937,7 @@ spec: type: object x-kubernetes-map-type: atomic user: + default: admin type: string required: - image @@ -3914,6 +3946,7 @@ spec: scaleIO: properties: fsType: + default: xfs type: string gateway: type: string @@ -3931,6 +3964,7 @@ spec: sslEnabled: type: boolean storageMode: + default: ThinProvisioned type: string storagePool: type: string diff --git a/config/default/kustomization.yaml b/config/default/kustomization.yaml index 14f9a828e5e..cb57ade499f 100644 --- a/config/default/kustomization.yaml +++ b/config/default/kustomization.yaml @@ -74,6 +74,7 @@ replacements: kind: Certificate name: serving-cert namespace: kserve +# Replace the namespace with the namespace of the controller manager. - source: fieldPath: metadata.namespace kind: Deployment diff --git a/config/default/manager_auth_proxy_patch.yaml b/config/default/manager_auth_proxy_patch.yaml index 4eae8f36b01..e0f7b730b0d 100644 --- a/config/default/manager_auth_proxy_patch.yaml +++ b/config/default/manager_auth_proxy_patch.yaml @@ -30,5 +30,7 @@ spec: name: https - name: manager args: + # When changing args, also update manager.yaml because the following + # list of args will fully override the arguments in manager.yaml. - "--metrics-addr=127.0.0.1:8080" - "--leader-elect" diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index c75dbddb54b..67015c0325e 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -27,6 +27,10 @@ spec: containers: - command: - /manager + args: + # When changing arguments, make sure to review the args in manager_auth_proxy_patch.yaml + # and update as needed. + - "--leader-elect" image: ko://github.com/kserve/kserve/cmd/manager imagePullPolicy: Always name: manager @@ -47,14 +51,14 @@ spec: value: kserve-webhook-server-cert livenessProbe: failureThreshold: 5 - initialDelaySeconds: 10 + initialDelaySeconds: 30 httpGet: path: /healthz port: 8081 timeoutSeconds: 5 readinessProbe: - initialDelaySeconds: 10 - failureThreshold: 10 + initialDelaySeconds: 30 + failureThreshold: 5 periodSeconds: 5 httpGet: path: /readyz diff --git a/config/overlays/kubeflow/cluster-role.yaml b/config/overlays/kubeflow/cluster-role.yaml index a4ac6d50c27..4b0baa67ac1 100644 --- a/config/overlays/kubeflow/cluster-role.yaml +++ b/config/overlays/kubeflow/cluster-role.yaml @@ -21,8 +21,10 @@ rules: - apiGroups: - serving.kserve.io resources: + - inferencegraphs - inferenceservices - servingruntimes + - trainedmodels verbs: - get - list @@ -63,8 +65,10 @@ rules: - apiGroups: - serving.kserve.io resources: + - inferencegraphs - inferenceservices - servingruntimes + - trainedmodels verbs: - get - list diff --git a/config/overlays/kubeflow/kustomization.yaml b/config/overlays/kubeflow/kustomization.yaml index 5a2ff6fea5a..8268d0d6aba 100644 --- a/config/overlays/kubeflow/kustomization.yaml +++ b/config/overlays/kubeflow/kustomization.yaml @@ -21,6 +21,7 @@ configurations: - params.yaml replacements: +# Replace the namespace with the namespace of the controller manager. - source: fieldPath: metadata.namespace kind: Deployment @@ -126,7 +127,6 @@ replacements: options: delimiter: '/' index: 0 - patches: - path: patches/statefulset.yaml - path: patches/namespace.yaml diff --git a/config/overlays/test/configmap/inferenceservice-disable-istio.yaml b/config/overlays/test/configmap/inferenceservice-disable-istio.yaml index c96d934e11f..946582fb99b 100644 --- a/config/overlays/test/configmap/inferenceservice-disable-istio.yaml +++ b/config/overlays/test/configmap/inferenceservice-disable-istio.yaml @@ -5,7 +5,8 @@ metadata: namespace: kserve data: ingress: |- - { + { + "kserveIngressGateway": "kserve/kserve-ingress-gateway", "ingressGateway" : "knative-serving/knative-ingress-gateway", "localGateway" : "knative-serving/knative-local-gateway", "localGatewayService" : "knative-local-gateway.istio-system.svc.cluster.local", diff --git a/config/overlays/test/configmap/inferenceservice-enable-gateway-api.yaml b/config/overlays/test/configmap/inferenceservice-enable-gateway-api.yaml new file mode 100644 index 00000000000..47f90e7646c --- /dev/null +++ b/config/overlays/test/configmap/inferenceservice-enable-gateway-api.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: inferenceservice-config + namespace: kserve +data: + ingress: |- + { + "enableGatewayApi": true, + "kserveIngressGateway": "kserve/kserve-ingress-gateway", + "ingressGateway" : "knative-serving/knative-ingress-gateway", + "localGateway" : "knative-serving/knative-local-gateway", + "localGatewayService" : "knative-local-gateway.istio-system.svc.cluster.local", + "ingressDomain" : "example.com", + "ingressClassName" : "istio", + "domainTemplate": "{{ .Name }}-{{ .Namespace }}.{{ .IngressDomain }}", + "urlScheme": "http", + "disableIstioVirtualHost": false + } diff --git a/config/overlays/test/configmap/inferenceservice-enable-modelcache.yaml b/config/overlays/test/configmap/inferenceservice-enable-modelcache.yaml new file mode 100644 index 00000000000..92b02f30821 --- /dev/null +++ b/config/overlays/test/configmap/inferenceservice-enable-modelcache.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: inferenceservice-config + namespace: kserve +data: + localModel: |- + { + "enabled": true, + "jobNamespace": "kserve-localmodel-jobs", + "defaultJobImage" : "kserve/storage-initializer:latest", + "fsGroup": 1000 + } \ No newline at end of file diff --git a/config/overlays/test/configmap/inferenceservice-ingress.yaml b/config/overlays/test/configmap/inferenceservice-path-template.yaml similarity index 84% rename from config/overlays/test/configmap/inferenceservice-ingress.yaml rename to config/overlays/test/configmap/inferenceservice-path-template.yaml index 323784eb21a..0630249a17b 100644 --- a/config/overlays/test/configmap/inferenceservice-ingress.yaml +++ b/config/overlays/test/configmap/inferenceservice-path-template.yaml @@ -5,7 +5,9 @@ metadata: namespace: kserve data: ingress: |- - { + { + "enableGatewayApi": true, + "kserveIngressGateway": "kserve/kserve-ingress-gateway", "ingressGateway" : "knative-serving/knative-ingress-gateway", "localGateway": "knative-serving/knative-local-gateway", "localGatewayService" : "knative-local-gateway.istio-system.svc.cluster.local", diff --git a/config/overlays/test/configmap/inferenceservice.yaml b/config/overlays/test/configmap/inferenceservice.yaml index f1aafac4333..784cf5e35f7 100644 --- a/config/overlays/test/configmap/inferenceservice.yaml +++ b/config/overlays/test/configmap/inferenceservice.yaml @@ -17,7 +17,8 @@ data: "memoryRequest": "100Mi", "memoryLimit": "1Gi", "cpuRequest": "100m", - "cpuLimit": "1" + "cpuLimit": "1", + "enableDirectPvcVolumeMount": true } credentials: |- { @@ -31,6 +32,8 @@ data: } ingress: |- { + "enableGatewayApi": false, + "kserveIngressGateway": "kserve/kserve-ingress-gateway", "ingressGateway" : "knative-serving/knative-ingress-gateway", "localGateway": "knative-serving/knative-local-gateway", "localGatewayService" : "knative-local-gateway.istio-system.svc.cluster.local", diff --git a/config/overlays/test/gateway/ingress_gateway.yaml b/config/overlays/test/gateway/ingress_gateway.yaml new file mode 100644 index 00000000000..f7f315f8c62 --- /dev/null +++ b/config/overlays/test/gateway/ingress_gateway.yaml @@ -0,0 +1,17 @@ +apiVersion: gateway.networking.k8s.io/v1 +kind: Gateway +metadata: + name: kserve-ingress-gateway + namespace: kserve +spec: + gatewayClassName: envoy + listeners: + - name: http + port: 80 + protocol: HTTP + allowedRoutes: + namespaces: + from: All + infrastructure: + labels: + serving.kserve.io/gateway: kserve-ingress-gateway diff --git a/config/overlays/test/kustomization.yaml b/config/overlays/test/kustomization.yaml index 0ffe7ff9e09..b91b785df76 100644 --- a/config/overlays/test/kustomization.yaml +++ b/config/overlays/test/kustomization.yaml @@ -9,3 +9,4 @@ patches: - path: configmap/inferenceservice.yaml - path: manager_image_patch.yaml - path: localmodel_manager_image_patch.yaml +- path: localmodelnode_agent_image_patch.yaml diff --git a/config/overlays/test/localmodelnode_agent_image_patch.yaml b/config/overlays/test/localmodelnode_agent_image_patch.yaml new file mode 100644 index 00000000000..b2c0cdcaab5 --- /dev/null +++ b/config/overlays/test/localmodelnode_agent_image_patch.yaml @@ -0,0 +1,19 @@ +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: kserve-localmodelnode-agent + namespace: kserve +spec: + template: + spec: + containers: + - name: manager + image: kserve/kserve-localmodelnode-agent:latest + imagePullPolicy: IfNotPresent + resources: + limits: + cpu: 100m + memory: 300Mi + requests: + cpu: 100m + memory: 200Mi diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 71d89ced368..946fbd8980d 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -78,6 +78,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - httproutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - networking.istio.io resources: diff --git a/config/runtimes/kserve-huggingfaceserver-multinode.yaml b/config/runtimes/kserve-huggingfaceserver-multinode.yaml index 1c134f00739..f6c93004076 100644 --- a/config/runtimes/kserve-huggingfaceserver-multinode.yaml +++ b/config/runtimes/kserve-huggingfaceserver-multinode.yaml @@ -16,35 +16,33 @@ spec: - v1 containers: - name: kserve-container - image: kserve/huggingfaceserver:latest + image: kserve/huggingfaceserver:latest-gpu command: ["bash", "-c"] args: - | - ray start --head --disable-usage-stats --include-dashboard false - # wait for other node to join - until [[ $(ray status | grep -c node_) -eq ${PIPELINE_PARALLEL_SIZE} ]]; do - echo "Waiting..." - sleep 1 - done - ray status - export MODEL=${MODEL_ID} if [[ ! -z ${MODEL_DIR} ]] then - MODEL=${MODEL_DIR} + export MODEL=${MODEL_DIR} fi - python3 -m huggingfaceserver --model_name=${MODEL_NAME} --model_dir=${MODEL} --tensor-parallel-size=${TENSOR_PARALLEL_SIZE} --pipeline-parallel-size=${PIPELINE_PARALLEL_SIZE} + export RAY_ADDRESS=${POD_IP}:${RAY_PORT} + ray start --head --disable-usage-stats --include-dashboard false + python ./huggingfaceserver/health_check.py registered_nodes --retries 200 --probe_name runtime_start + + python -m huggingfaceserver --model_name=${MODEL_NAME} --model_dir=${MODEL} --tensor-parallel-size=${TENSOR_PARALLEL_SIZE} --pipeline-parallel-size=${PIPELINE_PARALLEL_SIZE} env: - name: RAY_PORT - value: "6379" - - name: RAY_ADDRESS - value: 127.0.0.1:6379 + value: "6379" - name: POD_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace + - name: POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP - name: VLLM_CONFIG_ROOT value: /tmp - name: HF_HUB_CACHE @@ -60,41 +58,39 @@ spec: - name: shm mountPath: /dev/shm livenessProbe: - failureThreshold: 3 - periodSeconds: 30 + failureThreshold: 2 + periodSeconds: 5 successThreshold: 1 - timeoutSeconds: 5 - initialDelaySeconds: 10 + timeoutSeconds: 15 exec: command: - bash - -c - | - ./huggingfaceserver/health_check.py liveness + python ./huggingfaceserver/health_check.py registered_node_and_runtime_health --health_check_url http://localhost:8080 --probe_name head_liveness readinessProbe: failureThreshold: 2 - periodSeconds: 10 + periodSeconds: 5 successThreshold: 1 - timeoutSeconds: 5 - initialDelaySeconds: 10 + timeoutSeconds: 15 exec: command: - bash - -c - | - ./huggingfaceserver/health_check.py readiness ${PIPELINE_PARALLEL_SIZE} http://localhost:8080 + python ./huggingfaceserver/health_check.py runtime_health --health_check_url http://localhost:8080 --probe_name head_readiness startupProbe: failureThreshold: 40 periodSeconds: 30 successThreshold: 1 - timeoutSeconds: 5 - initialDelaySeconds: 5 + timeoutSeconds: 30 + initialDelaySeconds: 60 exec: command: - bash - -c - | - ./huggingfaceserver/health_check.py startup + python ./huggingfaceserver/health_check.py registered_node_and_runtime_health --health_check_url http://localhost:8080 --probe_name head_startup volumes: - name: shm emptyDir: @@ -105,33 +101,34 @@ spec: tensorParallelSize: 1 containers: - name: worker-container - image: kserve/huggingfaceserver:latest + image: kserve/huggingfaceserver:latest-gpu command: ["bash", "-c"] args: - | + export RAY_HEAD_ADDRESS=${HEAD_SVC}.${POD_NAMESPACE}.svc.cluster.local:6379 SECONDS=0 while true; do - if (( SECONDS <= 120 )); then - if ray health-check --address "${HEAD_SVC}.${POD_NAMESPACE}.svc.cluster.local:6379" > /dev/null 2>&1; then - echo "Global Control Service(GCS) is ready." + if (( SECONDS <= 240 )); then + if ray health-check --address "${RAY_HEAD_ADDRESS}" > /dev/null 2>&1; then + echo "Ray Global Control Service(GCS) is ready." break fi - echo "$SECONDS seconds elapsed: Waiting for Global Control Service(GCS) to be ready." + echo "$SECONDS seconds elapsed: Waiting for Ray Global Control Service(GCS) to be ready." else - if ray health-check --address "${HEAD_SVC}.${POD_NAMESPACE}.svc.cluster.local:6379"; then - echo "Global Control Service(GCS) is ready. Any error messages above can be safely ignored." + if ray health-check --address "${RAY_HEAD_ADDRESS}"; then + echo "Ray Global Control Service(GCS) is ready. Any error messages above can be safely ignored." break fi - echo "$SECONDS seconds elapsed: Still waiting for Global Control Service(GCS) to be ready." + echo "$SECONDS seconds elapsed: Still waiting for Ray Global Control Service(GCS) to be ready." fi - + sleep 5 done - RAY_HEAD_ADDRESS="${HEAD_SVC}.${POD_NAMESPACE}.svc.cluster.local:6379" echo "Attempting to connect to Ray cluster at $RAY_HEAD_ADDRESS ..." - ray start --address="$RAY_HEAD_ADDRESS" --block + ray start --address="${RAY_HEAD_ADDRESS}" --block + env: - name: POD_NAME valueFrom: @@ -141,6 +138,7 @@ spec: valueFrom: fieldRef: fieldPath: metadata.namespace + resources: requests: cpu: "2" @@ -152,27 +150,31 @@ spec: - name: shm mountPath: /dev/shm livenessProbe: - failureThreshold: 3 + failureThreshold: 2 periodSeconds: 5 successThreshold: 1 - timeoutSeconds: 5 + timeoutSeconds: 15 exec: command: - bash - -c - | - ./huggingfaceserver/health_check.py registered_nodes ${PIPELINE_PARALLEL_SIZE} + export RAY_ADDRESS=${HEAD_SVC}.${POD_NAMESPACE}.svc.cluster.local:6379 + python ./huggingfaceserver/health_check.py registered_nodes --probe_name worker_liveness startupProbe: - failureThreshold: 12 - periodSeconds: 5 + failureThreshold: 40 + periodSeconds: 30 successThreshold: 1 - timeoutSeconds: 5 + timeoutSeconds: 30 + initialDelaySeconds: 60 exec: command: - bash - - -c + - -c - | - ./huggingfaceserver/health_check.py startup + export RAY_HEAD_NODE=${HEAD_SVC}.${POD_NAMESPACE}.svc.cluster.local + export RAY_ADDRESS=${RAY_HEAD_NODE}:6379 + python ./huggingfaceserver/health_check.py registered_node_and_runtime_models --runtime_url http://${RAY_HEAD_NODE}:8080/v1/models --probe_name worker_startup volumes: - name: shm emptyDir: diff --git a/docs/apis/Dockerfile b/docs/apis/Dockerfile index 13d853047d3..eeb025f93b5 100644 --- a/docs/apis/Dockerfile +++ b/docs/apis/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.22 +FROM golang:1.23 RUN apt-get update && apt-get -y upgrade && apt-get -y install git diff --git a/docs/samples/explanation/alibi/alibiexplainer/poetry.lock b/docs/samples/explanation/alibi/alibiexplainer/poetry.lock index c5b71d55ae9..fe7e8592aa0 100644 --- a/docs/samples/explanation/alibi/alibiexplainer/poetry.lock +++ b/docs/samples/explanation/alibi/alibiexplainer/poetry.lock @@ -1865,7 +1865,7 @@ files = [ [[package]] name = "kserve" -version = "0.14.0" +version = "0.15.0rc0" description = "KServe Python SDK" optional = false python-versions = ">=3.9,<3.13" diff --git a/docs/samples/explanation/alibi/alibiexplainer/pyproject.toml b/docs/samples/explanation/alibi/alibiexplainer/pyproject.toml index 0430b71c3b5..aa31bc29409 100644 --- a/docs/samples/explanation/alibi/alibiexplainer/pyproject.toml +++ b/docs/samples/explanation/alibi/alibiexplainer/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "alibiexplainer" -version = "0.14.0rc1" +version = "0.15.0rc0" description = "Model Explanation Server. Not intended for use outside KServe Frameworks Images." authors = ["cliveseldon "] license = "https://github.com/kserve/kserve/blob/master/LICENSE" diff --git a/go.mod b/go.mod index b8d6f3738bd..409d804e009 100644 --- a/go.mod +++ b/go.mod @@ -1,10 +1,10 @@ module github.com/kserve/kserve -go 1.22.7 +go 1.23.6 require ( - cloud.google.com/go/storage v1.43.0 - github.com/aws/aws-sdk-go v1.55.5 + cloud.google.com/go/storage v1.50.0 + github.com/aws/aws-sdk-go v1.55.6 github.com/cloudevents/sdk-go/v2 v2.15.2 github.com/fsnotify/fsnotify v1.7.0 github.com/getkin/kin-openapi v0.127.0 @@ -16,52 +16,61 @@ require ( github.com/googleapis/google-cloud-go-testing v0.0.0-20210719221736-1c9a4c676720 github.com/json-iterator/go v1.1.12 github.com/kelseyhightower/envconfig v1.4.0 - github.com/onsi/ginkgo/v2 v2.20.1 - github.com/onsi/gomega v1.34.2 + github.com/onsi/ginkgo/v2 v2.22.2 + github.com/onsi/gomega v1.36.2 github.com/pkg/errors v0.9.1 github.com/spf13/cobra v1.8.1 github.com/spf13/pflag v1.0.5 - github.com/stretchr/testify v1.9.0 + github.com/stretchr/testify v1.10.0 github.com/tidwall/gjson v1.17.3 go.uber.org/zap v1.27.0 gomodules.xyz/jsonpatch/v2 v2.4.0 - google.golang.org/api v0.195.0 - google.golang.org/protobuf v1.34.2 + google.golang.org/api v0.220.0 + google.golang.org/protobuf v1.36.4 gopkg.in/go-playground/validator.v9 v9.31.0 - istio.io/api v1.23.0 - istio.io/client-go v1.23.0 - k8s.io/api v0.30.4 - k8s.io/apimachinery v0.30.4 - k8s.io/client-go v0.30.4 - k8s.io/code-generator v0.30.4 - k8s.io/component-helpers v0.30.4 + istio.io/api v1.24.2 + istio.io/client-go v1.24.2 + k8s.io/api v0.32.1 + k8s.io/apimachinery v0.32.1 + k8s.io/client-go v0.32.1 + k8s.io/code-generator v0.32.1 + k8s.io/component-helpers v0.32.1 k8s.io/klog/v2 v2.130.1 - k8s.io/kube-openapi v0.0.0-20240827152857-f7e401e7b4c2 - k8s.io/utils v0.0.0-20240821151609-f90d01438635 - knative.dev/networking v0.0.0-20240815142417-37fdbdd0854b - knative.dev/pkg v0.0.0-20240815051656-89743d9bbf7c - knative.dev/serving v0.42.2 - sigs.k8s.io/controller-runtime v0.18.5 + k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f + k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 + knative.dev/networking v0.0.0-20250117155906-67d1c274ba6a + knative.dev/pkg v0.0.0-20250117084104-c43477f0052b + knative.dev/serving v0.44.0 + sigs.k8s.io/controller-runtime v0.20.1 + sigs.k8s.io/gateway-api v1.2.1 sigs.k8s.io/yaml v1.4.0 ) require ( - cloud.google.com/go v0.115.1 // indirect - cloud.google.com/go/auth v0.9.1 // indirect - cloud.google.com/go/auth/oauth2adapt v0.2.4 // indirect - cloud.google.com/go/compute/metadata v0.5.0 // indirect - cloud.google.com/go/iam v1.2.0 // indirect + cel.dev/expr v0.19.0 // indirect + cloud.google.com/go v0.116.0 // indirect + cloud.google.com/go/auth v0.14.1 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.7 // indirect + cloud.google.com/go/compute/metadata v0.6.0 // indirect + cloud.google.com/go/iam v1.2.2 // indirect + cloud.google.com/go/monitoring v1.21.2 // indirect contrib.go.opencensus.io/exporter/ocagent v0.7.1-0.20200907061046-05415f1de66d // indirect contrib.go.opencensus.io/exporter/prometheus v0.4.2 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.48.1 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.48.1 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blendle/zapdriver v1.3.1 // indirect github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/emicklei/go-restful/v3 v3.12.1 // indirect - github.com/evanphx/json-patch v5.9.0+incompatible // indirect + github.com/envoyproxy/go-control-plane/envoy v1.32.4 // indirect + github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect github.com/evanphx/json-patch/v5 v5.9.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/go-kit/log v0.2.1 // indirect github.com/go-logfmt/logfmt v0.6.0 // indirect github.com/go-logr/stdr v1.2.2 // indirect @@ -72,24 +81,24 @@ require ( github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect github.com/golang/protobuf v1.5.4 // indirect - github.com/google/gnostic-models v0.6.8 // indirect - github.com/google/go-containerregistry v0.20.2 // indirect + github.com/google/btree v1.1.3 // indirect + github.com/google/gnostic-models v0.6.9 // indirect + github.com/google/go-containerregistry v0.13.0 // indirect github.com/google/gofuzz v1.2.0 // indirect - github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5 // indirect - github.com/google/s2a-go v0.1.8 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.3.3 // indirect - github.com/googleapis/gax-go/v2 v2.13.0 // indirect + github.com/google/pprof v0.0.0-20250208200701-d0013a598941 // indirect + github.com/google/s2a-go v0.1.9 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect + github.com/googleapis/gax-go/v2 v2.14.1 // indirect github.com/gorilla/websocket v1.5.3 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect github.com/hashicorp/golang-lru v1.0.2 // indirect - github.com/imdario/mergo v0.3.16 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/invopop/yaml v0.3.1 // indirect github.com/jmespath/go-jmespath v0.4.1-0.20220621161143-b0104c826a24 // indirect github.com/josharian/intern v1.0.0 // indirect - github.com/klauspost/compress v1.17.9 // indirect + github.com/klauspost/compress v1.17.11 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect @@ -98,8 +107,9 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/perimeterx/marshmallow v1.1.5 // indirect + github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/prometheus/client_golang v1.20.2 // indirect + github.com/prometheus/client_golang v1.20.4 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.57.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect @@ -107,35 +117,42 @@ require ( github.com/stretchr/objx v0.5.2 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.1 // indirect + github.com/x448/float16 v0.8.4 // indirect go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect - go.opentelemetry.io/otel v1.29.0 // indirect - go.opentelemetry.io/otel/metric v1.29.0 // indirect - go.opentelemetry.io/otel/trace v1.29.0 // indirect - go.uber.org/atomic v1.11.0 // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/contrib/detectors/gcp v1.32.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 // indirect + go.opentelemetry.io/otel v1.34.0 // indirect + go.opentelemetry.io/otel/metric v1.34.0 // indirect + go.opentelemetry.io/otel/sdk v1.34.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.32.0 // indirect + go.opentelemetry.io/otel/trace v1.34.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/crypto v0.26.0 // indirect - golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 // indirect - golang.org/x/mod v0.20.0 // indirect - golang.org/x/net v0.28.0 // indirect - golang.org/x/oauth2 v0.22.0 // indirect - golang.org/x/sync v0.8.0 // indirect - golang.org/x/sys v0.24.0 // indirect - golang.org/x/term v0.23.0 // indirect - golang.org/x/text v0.17.0 // indirect - golang.org/x/time v0.6.0 // indirect - golang.org/x/tools v0.24.0 // indirect - google.golang.org/genproto v0.0.0-20240827150818-7e3bb234dfed // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240827150818-7e3bb234dfed // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240827150818-7e3bb234dfed // indirect - google.golang.org/grpc v1.66.0 // indirect + golang.org/x/crypto v0.32.0 // indirect + golang.org/x/mod v0.22.0 // indirect + golang.org/x/net v0.34.0 // indirect + golang.org/x/oauth2 v0.25.0 // indirect + golang.org/x/sync v0.10.0 // indirect + golang.org/x/sys v0.29.0 // indirect + golang.org/x/term v0.28.0 // indirect + golang.org/x/text v0.21.0 // indirect + golang.org/x/time v0.9.0 // indirect + golang.org/x/tools v0.29.0 // indirect + google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250127172529-29210b9bc287 // indirect + google.golang.org/grpc v1.70.0 // indirect + gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/go-playground/assert.v1 v1.2.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/apiextensions-apiserver v0.30.4 // indirect - k8s.io/gengo/v2 v2.0.0-20240826214909-a7b603a56eb7 // indirect - sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect + k8s.io/apiextensions-apiserver v0.32.0 // indirect + k8s.io/gengo/v2 v2.0.0-20240911193312-2b36238f13e9 // indirect + sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect ) + +// Fixes CVE-2024-45338 +replace golang.org/x/net => golang.org/x/net v0.33.0 diff --git a/go.sum b/go.sum index 4b9bdbc5c7a..a56db02436c 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +cel.dev/expr v0.19.0 h1:lXuo+nDhpyJSpWxpPVi5cPUwzKb+dsdOiw6IreM5yt0= +cel.dev/expr v0.19.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= @@ -14,26 +16,30 @@ cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKV cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.115.1 h1:Jo0SM9cQnSkYfp44+v+NQXHpcHqlnRJk2qxh6yvxxxQ= -cloud.google.com/go v0.115.1/go.mod h1:DuujITeaufu3gL68/lOFIirVNJwQeyf5UXyi+Wbgknc= -cloud.google.com/go/auth v0.9.1 h1:+pMtLEV2k0AXKvs/tGZojuj6QaioxfUjOpMsG5Gtx+w= -cloud.google.com/go/auth v0.9.1/go.mod h1:Sw8ocT5mhhXxFklyhT12Eiy0ed6tTrPMCJjSI8KhYLk= -cloud.google.com/go/auth/oauth2adapt v0.2.4 h1:0GWE/FUsXhf6C+jAkWgYm7X9tK8cuEIfy19DBn6B6bY= -cloud.google.com/go/auth/oauth2adapt v0.2.4/go.mod h1:jC/jOpwFP6JBxhB3P5Rr0a9HLMC/Pe3eaL4NmdvqPtc= +cloud.google.com/go v0.116.0 h1:B3fRrSDkLRt5qSHWe40ERJvhvnQwdZiHu0bJOpldweE= +cloud.google.com/go v0.116.0/go.mod h1:cEPSRWPzZEswwdr9BxE6ChEn01dWlTaF05LiC2Xs70U= +cloud.google.com/go/auth v0.14.1 h1:AwoJbzUdxA/whv1qj3TLKwh3XX5sikny2fc40wUl+h0= +cloud.google.com/go/auth v0.14.1/go.mod h1:4JHUxlGXisL0AW8kXPtUF6ztuOksyfUQNFjfsOCXkPM= +cloud.google.com/go/auth/oauth2adapt v0.2.7 h1:/Lc7xODdqcEw8IrZ9SvwnlLX6j9FHQM74z6cBk9Rw6M= +cloud.google.com/go/auth/oauth2adapt v0.2.7/go.mod h1:NTbTTzfvPl1Y3V1nPpOgl2w6d/FjO7NNUQaWSox6ZMc= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY= -cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY= +cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I= +cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/iam v1.2.0 h1:kZKMKVNk/IsSSc/udOb83K0hL/Yh/Gcqpz+oAkoIFN8= -cloud.google.com/go/iam v1.2.0/go.mod h1:zITGuWgsLZxd8OwAlX+eMFgZDXzBm7icj1PVTYG766Q= -cloud.google.com/go/longrunning v0.6.0 h1:mM1ZmaNsQsnb+5n1DNPeL0KwQd9jQRqSqSDEkBZr+aI= -cloud.google.com/go/longrunning v0.6.0/go.mod h1:uHzSZqW89h7/pasCWNYdUpwGz3PcVWhrWupreVPYLts= +cloud.google.com/go/iam v1.2.2 h1:ozUSofHUGf/F4tCNy/mu9tHLTaxZFLOUiKzjcgWHGIA= +cloud.google.com/go/iam v1.2.2/go.mod h1:0Ys8ccaZHdI1dEUilwzqng/6ps2YB6vRsjIe00/+6JY= +cloud.google.com/go/logging v1.12.0 h1:ex1igYcGFd4S/RZWOCU51StlIEuey5bjqwH9ZYjHibk= +cloud.google.com/go/logging v1.12.0/go.mod h1:wwYBt5HlYP1InnrtYI0wtwttpVU1rifnMT7RejksUAM= +cloud.google.com/go/longrunning v0.6.2 h1:xjDfh1pQcWPEvnfjZmwjKQEcHnpz6lHjfy7Fo0MK+hc= +cloud.google.com/go/longrunning v0.6.2/go.mod h1:k/vIs83RN4bE3YCswdXC5PFfWVILjm3hpEUlSko4PiI= +cloud.google.com/go/monitoring v1.21.2 h1:FChwVtClH19E7pJ+e0xUhJPGksctZNVOk2UhMmblmdU= +cloud.google.com/go/monitoring v1.21.2/go.mod h1:hS3pXvaG8KgWTSz+dAdyzPrGUYmi2Q+WFX8g2hqVEZU= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -43,8 +49,10 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -cloud.google.com/go/storage v1.43.0 h1:CcxnSohZwizt4LCzQHWvBf1/kvtHUn7gk9QERXPyXFs= -cloud.google.com/go/storage v1.43.0/go.mod h1:ajvxEa7WmZS1PxvKRq4bq0tFT3vMd502JwstCcYv0Q0= +cloud.google.com/go/storage v1.50.0 h1:3TbVkzTooBvnZsk7WaAQfOsNrdoM8QHusXA1cpk6QJs= +cloud.google.com/go/storage v1.50.0/go.mod h1:l7XeiD//vx5lfqE3RavfmU9yvk5Pp0Zhcv482poyafY= +cloud.google.com/go/trace v1.11.2 h1:4ZmaBdL8Ng/ajrgKqY5jfvzqMXbrDcBsUGXOT9aqTtI= +cloud.google.com/go/trace v1.11.2/go.mod h1:bn7OwXd4pd5rFuAnTrzBuoZ4ax2XQeG3qNgYmfCy0Io= contrib.go.opencensus.io/exporter/ocagent v0.7.1-0.20200907061046-05415f1de66d h1:LblfooH1lKOpp1hIhukktmSAxFkqMPFk9KR6iZ0MJNI= contrib.go.opencensus.io/exporter/ocagent v0.7.1-0.20200907061046-05415f1de66d/go.mod h1:IshRmMJBhDfFj5Y67nVhMYTTIze91RUeT73ipWKs/GY= contrib.go.opencensus.io/exporter/prometheus v0.4.2 h1:sqfsYl5GIY/L570iT+l93ehxaWJs2/OwXtiWwew3oAg= @@ -52,6 +60,14 @@ contrib.go.opencensus.io/exporter/prometheus v0.4.2/go.mod h1:dvEHbiKmgvbr5pjaF9 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 h1:3c8yed4lgqTt+oTQ+JNMDo+F4xprBf+O/il4ZC0nRLw= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0/go.mod h1:obipzmGjfSjam60XLwGfqUkJsfiheAl+TUjG+4yzyPM= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.48.1 h1:UQ0AhxogsIRZDkElkblfnwjc3IaltCm2HUMvezQaL7s= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.48.1/go.mod h1:jyqM3eLpJ3IbIFDTKVz2rF9T/xWGW0rIriGwnz8l9Tk= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.48.1 h1:oTX4vsorBZo/Zdum6OKPA4o7544hm6smoRv1QjpTwGo= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.48.1/go.mod h1:0wEl7vrAD8mehJyohS9HZy+WyEOaQO2mJx86Cvh93kM= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.48.1 h1:8nn+rsCvTq9axyEh382S0PFLBeaFwNsT43IrPWzctRU= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.48.1/go.mod h1:viRWSEhtMZqz1rhwmOVKkWl6SwmVowfL9O2YR5gI2PE= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= @@ -59,8 +75,8 @@ github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= -github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= +github.com/aws/aws-sdk-go v1.55.6 h1:cSg4pvZ3m8dgYcgqB97MrcdjUmZ1BeMYKUxMMB89IPk= +github.com/aws/aws-sdk-go v1.55.6/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -81,6 +97,8 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cloudevents/sdk-go/v2 v2.15.2 h1:54+I5xQEnI73RBhWHxbI1XJcqOFOVJN85vb41+8mHUc= github.com/cloudevents/sdk-go/v2 v2.15.2/go.mod h1:lL7kSWAE/V8VI4Wh0jbL2v/jvqsm6tjmaQBSvxcv4uE= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78 h1:QVw89YDxXxEe+l8gU8ETbOasdwEV+avkR75ZzsVV9WI= +github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -91,7 +109,15 @@ github.com/emicklei/go-restful/v3 v3.12.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRr github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.13.4 h1:zEqyPVyku6IvWCFwux4x9RxkLOMUL+1vC9xUFv5l2/M= +github.com/envoyproxy/go-control-plane v0.13.4/go.mod h1:kDfuBlDVsSj2MjrLEtRWtHlsWIFcGyB2RMO44Dc5GZA= +github.com/envoyproxy/go-control-plane/envoy v1.32.4 h1:jb83lalDRZSpPWW2Z7Mck/8kXZ5CQAFYVjQcdVIr83A= +github.com/envoyproxy/go-control-plane/envoy v1.32.4/go.mod h1:Gzjc5k8JcJswLjAx1Zm+wSYE20UrLtt7JZMWiWQXQEw= +github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 h1:/G9QYbddjL25KvtKTv3an9lx6VBE2cnb8wp1vEGNYGI= +github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8= +github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU= github.com/evanphx/json-patch v5.9.0+incompatible h1:fBXyNpNMuTTDdquAq/uisOr2lShz4oaXpDTX2bLe7ls= github.com/evanphx/json-patch v5.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg= @@ -100,6 +126,8 @@ github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2 github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= +github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/getkin/kin-openapi v0.127.0 h1:Mghqi3Dhryf3F8vR370nN67pAERW+3a95vomb3MAREY= github.com/getkin/kin-openapi v0.127.0/go.mod h1:OZrfXzUfGrNbsKj+xmFBx6E5c6yH3At/tAKSc2UszXM= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -149,8 +177,9 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfU github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= @@ -178,8 +207,10 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= -github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= +github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= +github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= +github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw= +github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -194,8 +225,8 @@ github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-containerregistry v0.20.2 h1:B1wPJ1SN/S7pB+ZAimcciVD+r+yV/l/DSArMxlbwseo= -github.com/google/go-containerregistry v0.20.2/go.mod h1:z38EKdKh4h7IP2gSfUUqEvalZBqs6AoLeWfUy34nQC8= +github.com/google/go-containerregistry v0.13.0 h1:y1C7Z3e149OJbOPDBxLYR8ITPz8dTKqQwjErKVHJC8k= +github.com/google/go-containerregistry v0.13.0/go.mod h1:J9FQ+eSS4a1aC2GNZxvNpbWhgp0487v+cgiilB4FqDo= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -211,20 +242,20 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5 h1:5iH8iuqE5apketRbSFBy+X1V0o+l+8NF1avt4HWl7cA= -github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +github.com/google/pprof v0.0.0-20250208200701-d0013a598941 h1:43XjGa6toxLpeksjcxs1jIoIyr+vUfOqY2c6HB4bpoc= +github.com/google/pprof v0.0.0-20250208200701-d0013a598941/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM= -github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA= +github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= +github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.3.3 h1:QRje2j5GZimBzlbhGA2V2QlGNgL8G6e+wGo/+/2bWI0= -github.com/googleapis/enterprise-certificate-proxy v0.3.3/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA= +github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw= +github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.13.0 h1:yitjD5f7jQHhyDsnhKEBU52NdvvdSeGzlAnDPT0hH1s= -github.com/googleapis/gax-go/v2 v2.13.0/go.mod h1:Z/fvTZXF8/uw7Xu5GuslPw+bplx6SS338j1Is2S+B7A= +github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q= +github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA= github.com/googleapis/google-cloud-go-testing v0.0.0-20210719221736-1c9a4c676720 h1:zC34cGQu69FG7qzJ3WiKW244WfhDC3xxYMeNOX2gtUQ= github.com/googleapis/google-cloud-go-testing v0.0.0-20210719221736-1c9a4c676720/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= @@ -239,8 +270,6 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= -github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/invopop/yaml v0.3.1 h1:f0+ZpmhfBSS4MhG+4HYseMdJhoeeopbSKbq5Rpeelso= @@ -265,8 +294,8 @@ github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dv github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= -github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= +github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= @@ -297,10 +326,10 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/onsi/ginkgo/v2 v2.20.1 h1:YlVIbqct+ZmnEph770q9Q7NVAz4wwIiVNahee6JyUzo= -github.com/onsi/ginkgo/v2 v2.20.1/go.mod h1:lG9ey2Z29hR41WMVthyJBGUBcBhGOtoPF2VFMvBXFCI= -github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8= -github.com/onsi/gomega v1.34.2/go.mod h1:v1xfxRgk0KIsG+QOdm7p8UosrOzPYRo60fd3B/1Dukc= +github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU= +github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk= +github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8= +github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= @@ -309,6 +338,8 @@ github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -319,8 +350,8 @@ github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqr github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ= -github.com/prometheus/client_golang v1.20.2 h1:5ctymQzZlyOON1666svgwn3s6IKWgfbjsejTMiXIyjg= -github.com/prometheus/client_golang v1.20.2/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_golang v1.20.4 h1:Tgh3Yr67PaOv/uTqloMsCEdeuFTatm5zIq5+qNN23vI= +github.com/prometheus/client_golang v1.20.4/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -348,8 +379,8 @@ github.com/prometheus/statsd_exporter v0.27.1 h1:tcRJOmwlA83HPfWzosAgr2+zEN5XDFv github.com/prometheus/statsd_exporter v0.27.1/go.mod h1:vA6ryDfsN7py/3JApEst6nLTJboq66XsNcJGNmC88NQ= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= -github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= @@ -371,8 +402,8 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stvp/go-udp-testing v0.0.0-20201019212854-469649b16807/go.mod h1:7jxmlfBCDBXRzr0eAQJ48XC1hBu1np4CS5+cHEYfwpc= github.com/tidwall/gjson v1.17.3 h1:bwWLZU7icoKRG+C+0PNwIKC6FCJO/Q3p2pZvuP0jN94= github.com/tidwall/gjson v1.17.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= @@ -385,10 +416,13 @@ github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0 github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -397,21 +431,27 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 h1:r6I7RJCN86bpD/FQwedZ0vSixDpwuWREjW9oRMsmqDc= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0/go.mod h1:B9yO6b04uB80CzjedvewuqDhxJxi11s7/GtiGa8bAjI= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8= -go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw= -go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8= -go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc= -go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8= -go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE= -go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg= -go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4= -go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/contrib/detectors/gcp v1.32.0 h1:P78qWqkLSShicHmAzfECaTgvslqHxblNE9j62Ws1NK8= +go.opentelemetry.io/contrib/detectors/gcp v1.32.0/go.mod h1:TVqo0Sda4Cv8gCIixd7LuLwW4EylumVWfhjZJjDD4DU= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0 h1:PS8wXpbyaDJQ2VDHHncMe9Vct0Zn1fEjpsjrLxGJoSc= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0/go.mod h1:HDBUsEjOuRC0EzKZ1bSaRGZWUBAzo+MhAcUUORSr4D0= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q= +go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= +go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0 h1:WDdP9acbMYjbKIyJUhTvtzj601sVJOqgWdUxSdR/Ysc= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0/go.mod h1:BLbf7zbNIONBLPwvFnwNHGj4zge8uTCM/UPIVW1Mq2I= +go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= +go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= +go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A= +go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU= +go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU= +go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ= +go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= +go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= -go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= @@ -421,13 +461,13 @@ go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= -golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= +golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -438,8 +478,6 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 h1:kx6Ds3MlpiUHKj7syVnbp57++8WpuKPcR5yjLBjvLEA= -golang.org/x/exp v0.0.0-20240823005443-9b4947da3948/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -460,44 +498,14 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= -golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= -golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= +golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -505,8 +513,8 @@ golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4Iltr golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= -golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70= +golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -518,8 +526,12 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -542,7 +554,6 @@ golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -551,36 +562,38 @@ golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= -golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= -golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= +golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= +golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= -golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= -golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= +golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -623,8 +636,11 @@ golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= -golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE= +golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -648,8 +664,8 @@ google.golang.org/api v0.25.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.195.0 h1:Ude4N8FvTKnnQJHU48RFI40jOBgIrL8Zqr3/QeST6yU= -google.golang.org/api v0.195.0/go.mod h1:DOGRWuv3P8TU8Lnz7uQc4hyNqrBpMtD9ppW3wBJurgc= +google.golang.org/api v0.220.0 h1:3oMI4gdBgB72WFVwE1nerDD8W3HUOS4kypK6rRLbGns= +google.golang.org/api v0.220.0/go.mod h1:26ZAlY6aN/8WgpCzjPNy18QpYaz7Zgg1h0qe1GkZEmY= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -687,12 +703,12 @@ google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7Fc google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20240827150818-7e3bb234dfed h1:4C4dbrVFtfIp3GXJdMX1Sj25mahfn5DywOo65/2ISQ8= -google.golang.org/genproto v0.0.0-20240827150818-7e3bb234dfed/go.mod h1:ICjniACoWvcDz8c8bOsHVKuuSGDJy1z5M4G0DM3HzTc= -google.golang.org/genproto/googleapis/api v0.0.0-20240827150818-7e3bb234dfed h1:3RgNmBoI9MZhsj3QxC+AP/qQhNwpCLOvYDYYsFrhFt0= -google.golang.org/genproto/googleapis/api v0.0.0-20240827150818-7e3bb234dfed/go.mod h1:OCdP9MfskevB/rbYvHTsXTtKC+3bHWajPdoKgjcYkfo= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240827150818-7e3bb234dfed h1:J6izYgfBXAI3xTKLgxzTmUltdYaLsuBxFCgDHWJ/eXg= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240827150818-7e3bb234dfed/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= +google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 h1:ToEetK57OidYuqD4Q5w+vfEnPvPpuTwedCNVohYJfNk= +google.golang.org/genproto v0.0.0-20241118233622-e639e219e697/go.mod h1:JJrvXBWRZaFMxBufik1a4RpFw4HhgVtBBWQeQgUj2cc= +google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 h1:CkkIfIt50+lT6NHAVoRYEyAvQGFM7xEwXUUywFvEb3Q= +google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576/go.mod h1:1R3kvZ1dtP3+4p4d3G8uJ8rFk/fWlScl38vanWACI08= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250127172529-29210b9bc287 h1:J1H9f+LEdWAfHcez/4cvaVBox7cOYT+IU6rgqj5x++8= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250127172529-29210b9bc287/go.mod h1:8BS3B93F/U1juMFq9+EDk+qOT5CO1R9IzXxG3PTqiRk= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -706,8 +722,8 @@ google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3Iji google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.66.0 h1:DibZuoBznOxbDQxRINckZcUvnCEvrW9pcWIE2yF9r1c= -google.golang.org/grpc v1.66.0/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y= +google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ= +google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -722,8 +738,8 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM= +google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -731,6 +747,8 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= +gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= gopkg.in/go-playground/validator.v9 v9.31.0 h1:bmXmP2RSNtFES+bn4uYuHT7iJFJv7Vj+an+ZQdDaD1M= @@ -756,44 +774,46 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -istio.io/api v1.23.0 h1:yqv3lNW6XSYS5XkbEkxsmFROXIQznp4lFWqj7xKEqCA= -istio.io/api v1.23.0/go.mod h1:QPSTGXuIQdnZFEm3myf9NZ5uBMwCdJWUvfj9ZZ+2oBM= -istio.io/client-go v1.23.0 h1://xojbifr84q29WE3eMx74p36hD4lvcejX1KxE3iJvY= -istio.io/client-go v1.23.0/go.mod h1:3qX/KBS5aR47QV4JhphcZl5ysnZ53x78TBjNQLM2TC4= -k8s.io/api v0.30.4 h1:XASIELmW8w8q0i1Y4124LqPoWMycLjyQti/fdYHYjCs= -k8s.io/api v0.30.4/go.mod h1:ZqniWRKu7WIeLijbbzetF4U9qZ03cg5IRwl8YVs8mX0= -k8s.io/apiextensions-apiserver v0.30.4 h1:FwOMIk/rzZvM/Gx0IOz0+biZ+dlnlCeyfXW17uzV1qE= -k8s.io/apiextensions-apiserver v0.30.4/go.mod h1:m8cAkJ9PVU8Olb4cPW4hrUDBZGvoSJ0kY0G0CfdGQac= -k8s.io/apimachinery v0.30.4 h1:5QHQI2tInzr8LsT4kU/2+fSeibH1eIHswNx480cqIoY= -k8s.io/apimachinery v0.30.4/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc= -k8s.io/client-go v0.30.4 h1:eculUe+HPQoPbixfwmaSZGsKcOf7D288tH6hDAdd+wY= -k8s.io/client-go v0.30.4/go.mod h1:IBS0R/Mt0LHkNHF4E6n+SUDPG7+m2po6RZU7YHeOpzc= -k8s.io/code-generator v0.30.4 h1:1J2AcpPNBGh/NH9+m4TDh8Yj+mSbM+JyQhH0QdIMwmE= -k8s.io/code-generator v0.30.4/go.mod h1:Dd8gxOr5ieh9yHCLKnIkKDmk1H2glH8nYCAqwFogD2M= -k8s.io/component-helpers v0.30.4 h1:A4KYmrz12HZtGZ8TAnanl0SUx7n6tKduxzB3NHvinr0= -k8s.io/component-helpers v0.30.4/go.mod h1:h5D4gI8hGQXMHw90qJq41PRUJrn2dvFA3ElZFUTzRps= -k8s.io/gengo/v2 v2.0.0-20240826214909-a7b603a56eb7 h1:cErOOTkQ3JW19o4lo91fFurouhP8NcoBvb7CkvhZZpk= -k8s.io/gengo/v2 v2.0.0-20240826214909-a7b603a56eb7/go.mod h1:EJykeLsmFC60UQbYJezXkEsG2FLrt0GPNkU5iK5GWxU= +istio.io/api v1.24.2 h1:jYjcN6Iq0RPtQj/3KMFsybxmfqmjGN/dxhL7FGJEdIM= +istio.io/api v1.24.2/go.mod h1:MQnRok7RZ20/PE56v0LxmoWH0xVxnCQPNuf9O7PAN1I= +istio.io/client-go v1.24.2 h1:JTTfBV6dv+AAW+AfccyrdX4T1f9CpsXd1Yzo1s/IYAI= +istio.io/client-go v1.24.2/go.mod h1:dgZ9EmJzh1EECzf6nQhwNL4R6RvlyeH/RXeNeNp/MRg= +k8s.io/api v0.32.1 h1:f562zw9cy+GvXzXf0CKlVQ7yHJVYzLfL6JAS4kOAaOc= +k8s.io/api v0.32.1/go.mod h1:/Yi/BqkuueW1BgpoePYBRdDYfjPF5sgTr5+YqDZra5k= +k8s.io/apiextensions-apiserver v0.32.0 h1:S0Xlqt51qzzqjKPxfgX1xh4HBZE+p8KKBq+k2SWNOE0= +k8s.io/apiextensions-apiserver v0.32.0/go.mod h1:86hblMvN5yxMvZrZFX2OhIHAuFIMJIZ19bTvzkP+Fmw= +k8s.io/apimachinery v0.32.1 h1:683ENpaCBjma4CYqsmZyhEzrGz6cjn1MY/X2jB2hkZs= +k8s.io/apimachinery v0.32.1/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= +k8s.io/client-go v0.32.1 h1:otM0AxdhdBIaQh7l1Q0jQpmo7WOFIk5FFa4bg6YMdUU= +k8s.io/client-go v0.32.1/go.mod h1:aTTKZY7MdxUaJ/KiUs8D+GssR9zJZi77ZqtzcGXIiDg= +k8s.io/code-generator v0.32.1 h1:4lw1kFNDuFYXquTkB7Sl5EwPMUP2yyW9hh6BnFfRZFY= +k8s.io/code-generator v0.32.1/go.mod h1:zaILfm00CVyP/6/pJMJ3zxRepXkxyDfUV5SNG4CjZI4= +k8s.io/component-helpers v0.32.1 h1:TwdsSM1vW9GjnfX18lkrZbwE5G9psCIS2/rhenTDXd8= +k8s.io/component-helpers v0.32.1/go.mod h1:1JT1Ei3FD29yFQ18F3laj1WyvxYdHIhyxx6adKMFQXI= +k8s.io/gengo/v2 v2.0.0-20240911193312-2b36238f13e9 h1:si3PfKm8dDYxgfbeA6orqrtLkvvIeH8UqffFJDl0bz4= +k8s.io/gengo/v2 v2.0.0-20240911193312-2b36238f13e9/go.mod h1:EJykeLsmFC60UQbYJezXkEsG2FLrt0GPNkU5iK5GWxU= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kube-openapi v0.0.0-20240827152857-f7e401e7b4c2 h1:GKE9U8BH16uynoxQii0auTjmmmuZ3O0LFMN6S0lPPhI= -k8s.io/kube-openapi v0.0.0-20240827152857-f7e401e7b4c2/go.mod h1:coRQXBK9NxO98XUv3ZD6AK3xzHCxV6+b7lrquKwaKzA= -k8s.io/utils v0.0.0-20240821151609-f90d01438635 h1:2wThSvJoW/Ncn9TmQEYXRnevZXi2duqHWf5OX9S3zjI= -k8s.io/utils v0.0.0-20240821151609-f90d01438635/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -knative.dev/networking v0.0.0-20240815142417-37fdbdd0854b h1:ws/Jeho6on84+5tfNKLAKriVVGIwivHbgPEtZjBfcs0= -knative.dev/networking v0.0.0-20240815142417-37fdbdd0854b/go.mod h1:2eMQVGLBZ5Kj1C4kKPuPhO7BsUeF6fkmhZFDQPIP+88= -knative.dev/pkg v0.0.0-20240815051656-89743d9bbf7c h1:2crXVk4FG0dSG6WHaIT+WKbUzn7qG2wn0AfYmvA22zs= -knative.dev/pkg v0.0.0-20240815051656-89743d9bbf7c/go.mod h1:cI2RPEEHZk+/dBpfHobs0aBdPA1mMZVUVWnGAc8NSzM= -knative.dev/serving v0.42.2 h1:yKieg3MeNvpVz+4JJPbvmpee3v3LK3zO5h5HJBtzaNk= -knative.dev/serving v0.42.2/go.mod h1:3cgU8/864RcqA0ZPrc3jFcmS3uJL/mOlUZiYsXonwaE= +k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y= +k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4= +k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro= +k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +knative.dev/networking v0.0.0-20250117155906-67d1c274ba6a h1:FaDPXtv42+AkYh/mE269pttPSZ3fDVAjJiEsYUaM4SM= +knative.dev/networking v0.0.0-20250117155906-67d1c274ba6a/go.mod h1:AIKYMfZydhwXR/60c/3KXEnqEnH6aNEEqulifdqJVcQ= +knative.dev/pkg v0.0.0-20250117084104-c43477f0052b h1:a+gP7Yzu5NmoX2w1p8nfTgmSKF+aHLKGzqYT82ijJTw= +knative.dev/pkg v0.0.0-20250117084104-c43477f0052b/go.mod h1:bedSpkdLybR6JhL1J7XDLpd+JMKM/x8M5Apr80i5TeE= +knative.dev/serving v0.44.0 h1:c6TXhoSAI6eXt0/1ET3C69jMWYA4ES9FskSan/fBaac= +knative.dev/serving v0.44.0/go.mod h1:9bFONngDZtkdYZkP5ko9LDS9ZelnFY9SaPoHKG0vFxs= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/controller-runtime v0.18.5 h1:nTHio/W+Q4aBlQMgbnC5hZb4IjIidyrizMai9P6n4Rk= -sigs.k8s.io/controller-runtime v0.18.5/go.mod h1:TVoGrfdpbA9VRFaRnKgk9P5/atA0pMwq+f+msb9M8Sg= -sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= -sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= -sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +sigs.k8s.io/controller-runtime v0.20.1 h1:JbGMAG/X94NeM3xvjenVUaBjy6Ui4Ogd/J5ZtjZnHaE= +sigs.k8s.io/controller-runtime v0.20.1/go.mod h1:BrP3w158MwvB3ZbNpaAcIKkHQ7YGpYnzpoSTZ8E14WU= +sigs.k8s.io/gateway-api v1.2.1 h1:fZZ/+RyRb+Y5tGkwxFKuYuSRQHu9dZtbjenblleOLHM= +sigs.k8s.io/gateway-api v1.2.1/go.mod h1:EpNfEXNjiYfUJypf0eZ0P5iXA9ekSGWaS1WgPaM42X0= +sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8= +sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo= +sigs.k8s.io/structured-merge-diff/v4 v4.4.2 h1:MdmvkGuXi/8io6ixD5wud3vOLwc1rj0aNqRlpuvjmwA= +sigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/hack/generate-install.sh b/hack/generate-install.sh index 9e691ca2717..0f461ad5147 100755 --- a/hack/generate-install.sh +++ b/hack/generate-install.sh @@ -59,6 +59,7 @@ RELEASES=( "v0.14.0-rc0" "v0.14.0-rc1" "v0.14.0" + "v0.15.0-rc0" ) TAG=$1 diff --git a/hack/quick_install.sh b/hack/quick_install.sh index 71f78c87df7..fedf67bc376 100755 --- a/hack/quick_install.sh +++ b/hack/quick_install.sh @@ -20,8 +20,9 @@ Help() { export ISTIO_VERSION=1.23.2 export KNATIVE_OPERATOR_VERSION=v1.15.7 export KNATIVE_SERVING_VERSION=1.15.2 -export KSERVE_VERSION=v0.14.0 +export KSERVE_VERSION=v0.15.0-rc0 export CERT_MANAGER_VERSION=v1.16.1 +export GATEWAY_API_VERSION=v1.2.1 SCRIPT_DIR="$(dirname -- "${BASH_SOURCE[0]}")" export SCRIPT_DIR @@ -86,6 +87,9 @@ if [ "$(get_kube_version)" -lt 24 ]; then exit 1 fi +echo "Installing Gateway API CRDs ..." +kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/${GATEWAY_API_VERSION}/standard-install.yaml + helm repo add istio https://istio-release.storage.googleapis.com/charts --force-update helm install istio-base istio/base -n istio-system --wait --set defaultRevision=default --create-namespace --version ${ISTIO_VERSION} helm install istiod istio/istiod -n istio-system --wait --version ${ISTIO_VERSION} \ @@ -136,5 +140,5 @@ fi # Install KServe helm install kserve-crd oci://ghcr.io/kserve/charts/kserve-crd --version ${KSERVE_VERSION} --namespace kserve --create-namespace --wait helm install kserve oci://ghcr.io/kserve/charts/kserve --version ${KSERVE_VERSION} --namespace kserve --create-namespace --wait \ - --set-string kserve.controller.deploymentMode="${deploymentMode}" --set kserve.modelmesh.enabled=false + --set-string kserve.controller.deploymentMode="${deploymentMode}" echo "😀 Successfully installed KServe" diff --git a/hack/verify-golint.sh b/hack/verify-golint.sh index 217919db726..e1694a01e93 100755 --- a/hack/verify-golint.sh +++ b/hack/verify-golint.sh @@ -18,15 +18,22 @@ set -o errexit set -o pipefail # golangci-lint binary path +# Check if GOBIN is set, if not use GOPATH/bin golangci_lint_binary="$(go env GOPATH)/bin/golangci-lint" +if [ -n "$(go env GOBIN)" ]; then + golangci_lint_binary="$(go env GOBIN)/golangci-lint" +fi # Check if golangci-lint is already installed if ! command -v golangci-lint &> /dev/null; then echo "installing golangci-lint" - go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.55.2 - # Verify golangci-lint installation - $golangci_lint_binary --version + go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.63 + # Verify golangci-lint installation + $golangci_lint_binary --version +elif ! $golangci_lint_binary --version | grep -q "1.63"; then + echo "golangci-lint version 1.63 is required" + exit 1 fi # Run golangci-lint -$golangci_lint_binary run +$golangci_lint_binary run --fix diff --git a/hack/violation_exceptions.list b/hack/violation_exceptions.list index 2e4589521d4..59bcd530252 100644 --- a/hack/violation_exceptions.list +++ b/hack/violation_exceptions.list @@ -13,6 +13,8 @@ API rule violation: list_type_missing,github.com/kserve/kserve/pkg/apis/serving/ API rule violation: list_type_missing,github.com/kserve/kserve/pkg/apis/serving/v1alpha1,TrainedModelList,Items API rule violation: list_type_missing,github.com/kserve/kserve/pkg/apis/serving/v1beta1,ComponentStatusSpec,Traffic API rule violation: list_type_missing,github.com/kserve/kserve/pkg/apis/serving/v1beta1,InferenceServiceList,Items +API rule violation: list_type_missing,github.com/kserve/kserve/pkg/apis/serving/v1beta1,InferenceServicesConfig,ServiceAnnotationDisallowedList +API rule violation: list_type_missing,github.com/kserve/kserve/pkg/apis/serving/v1beta1,InferenceServicesConfig,ServiceLabelDisallowedList API rule violation: list_type_missing,github.com/kserve/kserve/pkg/apis/serving/v1beta1,LoggerSpec,MetadataHeaders API rule violation: list_type_missing,github.com/kserve/kserve/pkg/apis/serving/v1beta1,PodSpec,Containers API rule violation: list_type_missing,github.com/kserve/kserve/pkg/apis/serving/v1beta1,PodSpec,EphemeralContainers @@ -33,6 +35,7 @@ API rule violation: names_match,github.com/kserve/kserve/pkg/apis/serving/v1beta API rule violation: names_match,github.com/kserve/kserve/pkg/apis/serving/v1beta1,ExplainerConfig,ContainerImage API rule violation: names_match,github.com/kserve/kserve/pkg/apis/serving/v1beta1,ExplainerExtensionSpec,StorageURI API rule violation: names_match,github.com/kserve/kserve/pkg/apis/serving/v1beta1,ExplainersConfig,ARTExplainer +API rule violation: names_match,github.com/kserve/kserve/pkg/apis/serving/v1beta1,IngressConfig,EnableGatewayAPI API rule violation: names_match,github.com/kserve/kserve/pkg/apis/serving/v1beta1,IngressConfig,LocalGatewayServiceName API rule violation: names_match,github.com/kserve/kserve/pkg/apis/serving/v1beta1,ModelStatus,ModelCopies API rule violation: names_match,github.com/kserve/kserve/pkg/apis/serving/v1beta1,ModelStatus,ModelRevisionStates diff --git a/localmodelnode.Dockerfile b/localmodel-agent.Dockerfile similarity index 94% rename from localmodelnode.Dockerfile rename to localmodel-agent.Dockerfile index edb39d36d69..1efc82763b0 100644 --- a/localmodelnode.Dockerfile +++ b/localmodel-agent.Dockerfile @@ -1,5 +1,5 @@ # Build the manager binary -FROM golang:1.22 AS builder +FROM golang:1.23 AS builder # Copy in the go src WORKDIR /go/src/github.com/kserve/kserve diff --git a/localmodel.Dockerfile b/localmodel.Dockerfile index 931adf5b61a..fcfa8577911 100644 --- a/localmodel.Dockerfile +++ b/localmodel.Dockerfile @@ -1,5 +1,5 @@ # Build the manager binary -FROM golang:1.22 AS builder +FROM golang:1.23 AS builder # Copy in the go src WORKDIR /go/src/github.com/kserve/kserve diff --git a/pkg/agent/downloader.go b/pkg/agent/downloader.go index 2c6b2fb0687..90936fa7300 100644 --- a/pkg/agent/downloader.go +++ b/pkg/agent/downloader.go @@ -17,19 +17,18 @@ limitations under the License. package agent import ( - "encoding/hex" "encoding/json" - "fmt" "os" "path/filepath" "regexp" "strings" "sync" - "github.com/kserve/kserve/pkg/agent/storage" - "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" "github.com/pkg/errors" "go.uber.org/zap" + + "github.com/kserve/kserve/pkg/agent/storage" + "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" ) type Downloader struct { @@ -43,7 +42,7 @@ func (d *Downloader) DownloadModel(modelName string, modelSpec *v1alpha1.ModelSp if modelSpec != nil { sha256 := storage.AsSha256(modelSpec) successFile := filepath.Join(d.ModelDir, modelName, - fmt.Sprintf("SUCCESS.%s", sha256)) + "SUCCESS."+sha256) d.Logger.Infof("Downloading %s to model dir %s", modelSpec.StorageURI, d.ModelDir) // Download if the event there is a success file and the event is one which we wish to Download _, err := os.Stat(successFile) @@ -66,7 +65,7 @@ func (d *Downloader) DownloadModel(modelName string, modelSpec *v1alpha1.ModelSp if err != nil { return errors.Wrapf(createErr, "failed to encode model spec") } - err = os.WriteFile(successFile, encodedJson, 0644) //#nosec + err = os.WriteFile(successFile, encodedJson, 0o644) // #nosec G306 if err != nil { return errors.Wrapf(createErr, "failed to write the success file") } @@ -97,21 +96,13 @@ func (d *Downloader) download(modelName string, storageUri string) error { return nil } -// nolint: unused -func hash(s string) string { - src := []byte(s) - dst := make([]byte, hex.EncodedLen(len(src))) - hex.Encode(dst, src) - return string(dst) -} - func extractProtocol(storageURI string) (storage.Protocol, error) { if storageURI == "" { - return "", fmt.Errorf("there is no storageUri supplied") + return "", errors.New("there is no storageUri supplied") } if !regexp.MustCompile(`\w+?://`).MatchString(storageURI) { - return "", fmt.Errorf("there is no protocol specified for the storageUri") + return "", errors.New("there is no protocol specified for the storageUri") } for _, prefix := range storage.SupportedProtocols { @@ -119,5 +110,5 @@ func extractProtocol(storageURI string) (storage.Protocol, error) { return prefix, nil } } - return "", fmt.Errorf("protocol not supported for storageUri") + return "", errors.New("protocol not supported for storageUri") } diff --git a/pkg/agent/downloader_test.go b/pkg/agent/downloader_test.go index 7d6a61ed3ef..c17335ffdd7 100644 --- a/pkg/agent/downloader_test.go +++ b/pkg/agent/downloader_test.go @@ -20,13 +20,14 @@ import ( logger "log" "os" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "go.uber.org/zap" + "github.com/kserve/kserve/pkg/agent/mocks" "github.com/kserve/kserve/pkg/agent/storage" "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" "github.com/kserve/kserve/pkg/modelconfig" - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - "go.uber.org/zap" ) var _ = Describe("Downloader", func() { @@ -67,7 +68,7 @@ var _ = Describe("Downloader", func() { }, } err := downloader.DownloadModel(modelConfig.Name, &modelConfig.Spec) - Expect(err).ShouldNot(BeNil()) + Expect(err).Should(HaveOccurred()) }) }) @@ -81,7 +82,7 @@ var _ = Describe("Downloader", func() { }, } err := downloader.DownloadModel(modelConfig.Name, &modelConfig.Spec) - Expect(err).ShouldNot(BeNil()) + Expect(err).Should(HaveOccurred()) }) }) @@ -95,7 +96,7 @@ var _ = Describe("Downloader", func() { }, } err := downloader.DownloadModel(modelConfig.Name, &modelConfig.Spec) - Expect(err).ShouldNot(BeNil()) + Expect(err).Should(HaveOccurred()) }) }) }) diff --git a/pkg/agent/mocks/s3mock.go b/pkg/agent/mocks/s3mock.go index facafd8c183..33226860228 100644 --- a/pkg/agent/mocks/s3mock.go +++ b/pkg/agent/mocks/s3mock.go @@ -17,7 +17,7 @@ limitations under the License. package mocks import ( - "fmt" + "errors" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/s3" @@ -40,8 +40,7 @@ func (m *MockS3Client) ListObjects(*s3.ListObjectsInput) (*s3.ListObjectsOutput, }, nil } -type MockS3Downloader struct { -} +type MockS3Downloader struct{} func (m *MockS3Downloader) DownloadWithIterator(aws.Context, s3manager.BatchDownloadIterator, ...func(*s3manager.Downloader)) error { return nil @@ -54,7 +53,7 @@ type MockS3FailDownloader struct { func (m *MockS3FailDownloader) DownloadWithIterator(aws.Context, s3manager.BatchDownloadIterator, ...func(*s3manager.Downloader)) error { var errs []s3manager.Error errs = append(errs, s3manager.Error{ - OrigErr: fmt.Errorf("failed to download"), + OrigErr: errors.New("failed to download"), Bucket: aws.String("modelRepo"), Key: aws.String("model1/model.pt"), }) diff --git a/pkg/agent/puller.go b/pkg/agent/puller.go index d3adbd15d7c..99458677185 100644 --- a/pkg/agent/puller.go +++ b/pkg/agent/puller.go @@ -28,7 +28,7 @@ import ( "go.uber.org/zap" "github.com/kserve/kserve/pkg/agent/storage" - v1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" + "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" ) type OpType string @@ -51,7 +51,7 @@ type ModelOp struct { OnStartup bool ModelName string Op OpType - Spec *v1.ModelSpec + Spec *v1alpha1.ModelSpec } type WaitGroupWrapper struct { @@ -146,7 +146,7 @@ func (p *Puller) modelProcessor(modelName string, ops <-chan *ModelOp) { // this is important for handling Load --> Unload requests sent in tandem // Load --> Unload = 0 (cancel first load) // Load --> Unload --> Load = 1 Load (cancel second load?) - var processOp = func(modelOp *ModelOp) { + processOp := func(modelOp *ModelOp) { switch modelOp.Op { case Add: p.logger.Infof("Downloading model from %s", modelOp.Spec.StorageURI) @@ -173,7 +173,7 @@ func (p *Puller) modelProcessor(modelName string, ops <-chan *ModelOp) { } } }() - if resp.StatusCode == 200 { + if resp.StatusCode == http.StatusOK { p.logger.Infof("Successfully loaded model %s", modelName) } else { body, err := io.ReadAll(resp.Body) @@ -205,7 +205,7 @@ func (p *Puller) modelProcessor(modelName string, ops <-chan *ModelOp) { } }() } - if resp.StatusCode == 200 { + if resp.StatusCode == http.StatusOK { p.logger.Infof("Successfully unloaded model %s", modelName) } else { body, err := io.ReadAll(resp.Body) diff --git a/pkg/agent/storage/gcs.go b/pkg/agent/storage/gcs.go index f99180dc62b..579813042dc 100644 --- a/pkg/agent/storage/gcs.go +++ b/pkg/agent/storage/gcs.go @@ -45,25 +45,23 @@ func (p *GCSProvider) DownloadModel(modelDir string, modelName string, storageUr } ctx := context.Background() gcsObjectDownloader := &GCSObjectDownloader{ - Context: ctx, StorageUri: storageUri, ModelDir: modelDir, ModelName: modelName, Bucket: tokens[0], Item: prefix, } - it, err := gcsObjectDownloader.GetObjectIterator(p.Client) + it, err := gcsObjectDownloader.GetObjectIterator(ctx, p.Client) if err != nil { return fmt.Errorf("unable to get object iterator because: %w", err) } - if err := gcsObjectDownloader.Download(p.Client, it); err != nil { + if err := gcsObjectDownloader.Download(ctx, p.Client, it); err != nil { return fmt.Errorf("unable to download object/s because: %w", err) } return nil } type GCSObjectDownloader struct { - Context context.Context StorageUri string ModelDir string ModelName string @@ -71,15 +69,15 @@ type GCSObjectDownloader struct { Item string } -func (g *GCSObjectDownloader) GetObjectIterator(client stiface.Client) (stiface.ObjectIterator, error) { +func (g *GCSObjectDownloader) GetObjectIterator(ctx context.Context, client stiface.Client) (stiface.ObjectIterator, error) { query := &gstorage.Query{Prefix: g.Item} - return client.Bucket(g.Bucket).Objects(g.Context, query), nil + return client.Bucket(g.Bucket).Objects(ctx, query), nil } -func (g *GCSObjectDownloader) Download(client stiface.Client, it stiface.ObjectIterator) error { +func (g *GCSObjectDownloader) Download(ctx context.Context, client stiface.Client, it stiface.ObjectIterator) error { var errs []error // flag to help determine if query prefix returned an empty iterator - var foundObject = false + foundObject := false for { attrs, err := it.Next() @@ -94,7 +92,7 @@ func (g *GCSObjectDownloader) Download(client stiface.Client, it stiface.ObjectI foundObject = true if FileExists(fileName) { - log.Info("Deleting", fileName) + log.Info("Deleting file", "name", fileName) if err := os.Remove(fileName); err != nil { return fmt.Errorf("file is unable to be deleted: %w", err) } @@ -103,7 +101,7 @@ func (g *GCSObjectDownloader) Download(client stiface.Client, it stiface.ObjectI if err != nil { return fmt.Errorf("file is already created: %w", err) } - if err := g.DownloadFile(client, attrs, file); err != nil { + if err := g.DownloadFile(ctx, client, attrs, file); err != nil { errs = append(errs, err) } } @@ -116,8 +114,8 @@ func (g *GCSObjectDownloader) Download(client stiface.Client, it stiface.ObjectI return nil } -func (g *GCSObjectDownloader) DownloadFile(client stiface.Client, attrs *gstorage.ObjectAttrs, file *os.File) error { - reader, err := client.Bucket(attrs.Bucket).Object(attrs.Name).NewReader(g.Context) +func (g *GCSObjectDownloader) DownloadFile(ctx context.Context, client stiface.Client, attrs *gstorage.ObjectAttrs, file *os.File) error { + reader, err := client.Bucket(attrs.Bucket).Object(attrs.Name).NewReader(ctx) if err != nil { return fmt.Errorf("failed to create reader for object(%s) in bucket(%s): %w", attrs.Name, diff --git a/pkg/agent/storage/https.go b/pkg/agent/storage/https.go index 694137255ed..ed728cac110 100644 --- a/pkg/agent/storage/https.go +++ b/pkg/agent/storage/https.go @@ -68,7 +68,7 @@ type HTTPSDownloader struct { func (h *HTTPSDownloader) Download(client http.Client) error { // Create request - req, err := http.NewRequest("GET", h.StorageUri, nil) + req, err := http.NewRequest(http.MethodGet, h.StorageUri, nil) if err != nil { return err } @@ -96,11 +96,11 @@ func (h *HTTPSDownloader) Download(client http.Client) error { } }() - if resp.StatusCode != 200 { + if resp.StatusCode != http.StatusOK { return fmt.Errorf("URI: %s returned a %d response code", h.StorageUri, resp.StatusCode) } // Write content into file(s) - contentType := resp.Header.Get("Content-type") + contentType := resp.Header.Get("Content-Type") fileDirectory := filepath.Join(h.ModelDir, h.ModelName) switch { @@ -142,6 +142,7 @@ func (h *HTTPSDownloader) extractHeaders() (headers map[string]string, err error } func createNewFile(fileFullName string) (*os.File, error) { + fileFullName = filepath.Clean(fileFullName) if FileExists(fileFullName) { if err := os.Remove(fileFullName); err != nil { return nil, fmt.Errorf("file is unable to be deleted: %w", err) @@ -168,13 +169,14 @@ func extractZipFiles(reader io.Reader, dest string) error { // Read all the files from zip archive for _, zipFile := range zipReader.File { - fileFullPath := filepath.Join(dest, zipFile.Name) // #nosecG305 - if !strings.HasPrefix(fileFullPath, filepath.Clean(dest)+string(os.PathSeparator)) { + dest = filepath.Clean(dest) + fileFullPath := filepath.Clean(filepath.Join(dest, filepath.Clean(zipFile.Name))) + if !strings.HasPrefix(fileFullPath, dest+string(os.PathSeparator)) { return fmt.Errorf("%s: illegal file path", fileFullPath) } if zipFile.Mode().IsDir() { - err = os.MkdirAll(fileFullPath, 0755) + err = os.MkdirAll(fileFullPath, 0o755) if err != nil { return fmt.Errorf("unable to create new directory %s", fileFullPath) } @@ -191,17 +193,18 @@ func extractZipFiles(reader io.Reader, dest string) error { return fmt.Errorf("unable to open file: %w", err) } - _, ioErr := io.CopyN(file, rc, DEFAULT_MAX_DECOMPRESSION_SIZE) // gosec G110 - closeErr := file.Close() - if closeErr != nil { - return closeErr + if zipFile.UncompressedSize64 > DEFAULT_MAX_DECOMPRESSION_SIZE { + return fmt.Errorf("file %s exceeds the maximum decompression size %d", zipFile.Name, DEFAULT_MAX_DECOMPRESSION_SIZE) } - closeErr = rc.Close() - if closeErr != nil { + limitReader := io.LimitReader(rc, DEFAULT_MAX_DECOMPRESSION_SIZE) + if _, err = io.Copy(file, limitReader); err != nil { + return err + } + if closeErr := file.Close(); closeErr != nil { return closeErr } - if ioErr != nil && !errors.Is(ioErr, io.EOF) { - return fmt.Errorf("unable to copy file content: %w", err) + if closeErr := rc.Close(); closeErr != nil { + return closeErr } } return nil @@ -230,9 +233,10 @@ func extractTarFiles(reader io.Reader, dest string) error { return fmt.Errorf("unable to access next tar file: %w", err) } - fileFullPath := filepath.Join(dest, header.Name) // #nosec G305 + dest = filepath.Clean(dest) + fileFullPath := filepath.Clean(filepath.Join(dest, filepath.Clean(header.Name))) if header.Typeflag == tar.TypeDir { - err = os.MkdirAll(fileFullPath, 0755) + err = os.MkdirAll(fileFullPath, 0o755) if err != nil { return fmt.Errorf("unable to create new directory %s", fileFullPath) } @@ -245,9 +249,8 @@ func extractTarFiles(reader io.Reader, dest string) error { return err } - // gosec G110 - _, ioErr := io.CopyN(newFile, tr, DEFAULT_MAX_DECOMPRESSION_SIZE) - if ioErr != nil && !errors.Is(ioErr, io.EOF) { + limitReader := io.LimitReader(tr, DEFAULT_MAX_DECOMPRESSION_SIZE) + if _, err := io.Copy(newFile, limitReader); err != nil { return fmt.Errorf("unable to copy contents to %s: %w", header.Name, err) } } diff --git a/pkg/agent/storage/s3.go b/pkg/agent/storage/s3.go index a54e3a8c876..f941ef08775 100644 --- a/pkg/agent/storage/s3.go +++ b/pkg/agent/storage/s3.go @@ -88,7 +88,7 @@ func (s *S3ObjectDownloader) GetAllObjects(s3Svc s3iface.S3API) ([]s3manager.Bat return nil, fmt.Errorf("%s has no objects or does not exist", s.StorageUri) } - var foundObject = false + foundObject := false for _, object := range resp.Contents { if strings.HasSuffix(*object.Key, "/") { diff --git a/pkg/agent/storage/utils.go b/pkg/agent/storage/utils.go index 7e8f7b8fa1f..fe4d15b6def 100644 --- a/pkg/agent/storage/utils.go +++ b/pkg/agent/storage/utils.go @@ -19,6 +19,7 @@ package storage import ( "context" "crypto/sha256" + "encoding/hex" "fmt" "net/http" "os" @@ -54,7 +55,7 @@ func AsSha256(o interface{}) string { h := sha256.New() h.Write([]byte(fmt.Sprintf("%v", o))) - return fmt.Sprintf("%x", h.Sum(nil)) + return hex.EncodeToString(h.Sum(nil)) } func Create(fileName string) (*os.File, error) { @@ -63,7 +64,7 @@ func Create(fileName string) (*os.File, error) { // compatible with any model / server container, using any user ID. Note we // also need to enable the `+x` bit to ensure the folder is "listable": // https://stackoverflow.com/a/30788944/5015573 - if err := os.MkdirAll(filepath.Dir(fileName), 0777); err != nil { + if err := os.MkdirAll(filepath.Dir(fileName), 0o777); err != nil { return nil, err } return os.Create(fileName) @@ -160,7 +161,6 @@ func GetProvider(providers map[Protocol]Provider, protocol Protocol) (Provider, } sess, err = session.NewSession(&awsConfig) - if err != nil { return nil, err } diff --git a/pkg/agent/storage/utils_test.go b/pkg/agent/storage/utils_test.go index 0b78b3a5e8b..66c46dc7447 100644 --- a/pkg/agent/storage/utils_test.go +++ b/pkg/agent/storage/utils_test.go @@ -23,8 +23,9 @@ import ( "syscall" "testing" - "github.com/kserve/kserve/pkg/agent/mocks" "github.com/onsi/gomega" + + "github.com/kserve/kserve/pkg/agent/mocks" ) func TestCreate(t *testing.T) { @@ -39,14 +40,17 @@ func TestCreate(t *testing.T) { folderPath := path.Join(tmpDir, "foo") filePath := path.Join(folderPath, "bar.txt") f, err := Create(filePath) + if err != nil { + t.Fatalf("Unable to create file: %v", err) + } defer f.Close() - g.Expect(err).To(gomega.BeNil()) + g.Expect(err).ToNot(gomega.HaveOccurred()) g.Expect(folderPath).To(gomega.BeADirectory()) info, _ := os.Stat(folderPath) mode := info.Mode() - expectedMode := os.FileMode(0777) + expectedMode := os.FileMode(0o777) g.Expect(mode.Perm()).To(gomega.Equal(expectedMode)) } @@ -58,7 +62,7 @@ func TestFileExists(t *testing.T) { // Test case for existing file f, err := os.CreateTemp(tmpDir, "tmpfile") - g.Expect(err).To(gomega.BeNil()) + g.Expect(err).ToNot(gomega.HaveOccurred()) g.Expect(FileExists(f.Name())).To(gomega.BeTrue()) f.Close() @@ -75,17 +79,23 @@ func TestRemoveDir(t *testing.T) { syscall.Umask(0) tmpDir, _ := os.MkdirTemp("", "test") subDir, _ := os.MkdirTemp(tmpDir, "test") - os.CreateTemp(subDir, "tmp") + + f, err := os.CreateTemp(subDir, "tmp") + if err != nil { + t.Fatalf("os.CreateTemp failed: %v", err) + } + defer f.Close() + os.CreateTemp(tmpDir, "tmp") - err := RemoveDir(tmpDir) - g.Expect(err).To(gomega.BeNil()) + err = RemoveDir(tmpDir) + g.Expect(err).ToNot(gomega.HaveOccurred()) _, err = os.Stat(tmpDir) g.Expect(os.IsNotExist(err)).To(gomega.BeTrue()) // Test case for non existing directory err = RemoveDir("directoryNotExist") - g.Expect(err).NotTo(gomega.BeNil()) + g.Expect(err).To(gomega.HaveOccurred()) } func TestGetProvider(t *testing.T) { @@ -99,13 +109,13 @@ func TestGetProvider(t *testing.T) { }, } provider, err := GetProvider(mockProviders, S3) - g.Expect(err).To(gomega.BeNil()) + g.Expect(err).ToNot(gomega.HaveOccurred()) g.Expect(provider).Should(gomega.Equal(mockProviders[S3])) // When providers map does not have specified provider for _, protocol := range SupportedProtocols { provider, err = GetProvider(map[Protocol]Provider{}, protocol) - g.Expect(err).To(gomega.BeNil()) + g.Expect(err).ToNot(gomega.HaveOccurred()) g.Expect(provider).ShouldNot(gomega.BeNil()) } } diff --git a/pkg/agent/syncer.go b/pkg/agent/syncer.go index 6f9f9356cec..5dea4c6794d 100644 --- a/pkg/agent/syncer.go +++ b/pkg/agent/syncer.go @@ -18,20 +18,20 @@ package agent import ( "encoding/json" - "fmt" "io" "os" "path/filepath" "strings" - "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" "github.com/pkg/errors" "go.uber.org/zap" + + "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" ) type FileError error -var NoSuccessFile FileError = fmt.Errorf("no success file can be found") +var ErrNoSuccessFile FileError = errors.New("no success file can be found") func SyncModelDir(modelDir string, logger *zap.SugaredLogger) (map[string]modelWrapper, error) { logger.Infof("Syncing from model dir %s", modelDir) diff --git a/pkg/agent/watcher.go b/pkg/agent/watcher.go index 79a2b740f94..abd61c2d924 100644 --- a/pkg/agent/watcher.go +++ b/pkg/agent/watcher.go @@ -24,10 +24,11 @@ import ( "github.com/fsnotify/fsnotify" "github.com/google/go-cmp/cmp" + "go.uber.org/zap" + "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" "github.com/kserve/kserve/pkg/constants" "github.com/kserve/kserve/pkg/modelconfig" - "go.uber.org/zap" ) type Watcher struct { diff --git a/pkg/agent/watcher_test.go b/pkg/agent/watcher_test.go index ae79d4dd880..597d900da6a 100644 --- a/pkg/agent/watcher_test.go +++ b/pkg/agent/watcher_test.go @@ -31,15 +31,19 @@ import ( gstorage "cloud.google.com/go/storage" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/s3/s3manager" + "github.com/go-logr/zapr" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/pkg/errors" + "go.uber.org/zap" + "k8s.io/apimachinery/pkg/api/resource" + "sigs.k8s.io/controller-runtime/pkg/log" + "github.com/kserve/kserve/pkg/agent/mocks" "github.com/kserve/kserve/pkg/agent/storage" "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" "github.com/kserve/kserve/pkg/constants" "github.com/kserve/kserve/pkg/modelconfig" - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - "go.uber.org/zap" - "k8s.io/apimachinery/pkg/api/resource" ) var _ = Describe("Watcher", func() { @@ -55,6 +59,7 @@ var _ = Describe("Watcher", func() { sugar = zapLogger.Sugar() logger.Printf("Creating temp dir %v\n", modelDir) SetDefaultEventuallyTimeout(2 * time.Minute) + log.SetLogger(zapr.NewLogger(zapLogger)) }) AfterEach(func() { os.RemoveAll(modelDir) @@ -92,8 +97,13 @@ var _ = Describe("Watcher", func() { } file, _ := json.MarshalIndent(modelConfigs, "", " ") - if err := os.WriteFile("/tmp/configs/"+constants.ModelConfigFileName, file, os.ModePerm); err != nil { - logger.Fatal(err, " Failed to write config files") + tmpFile, err := os.Create("/tmp/configs/" + constants.ModelConfigFileName) // #nosec G303 + if err != nil { + logger.Fatal(err, "failed to create model config file") + } + + if _, err := tmpFile.Write(file); err != nil { + logger.Fatal(err, "tmpFile.Write failed") } watcher := NewWatcher("/tmp/configs", modelDir, sugar) @@ -124,6 +134,7 @@ var _ = Describe("Watcher", func() { Expect(watcher.ModelTracker).Should(Equal(modelSpecMap)) DeferCleanup(func() { + tmpFile.Close() os.RemoveAll("/tmp/configs") }) }) @@ -308,12 +319,11 @@ var _ = Describe("Watcher", func() { logger.Printf("Using temp dir %v\n", modelDir) var errs []s3manager.Error errs = append(errs, s3manager.Error{ - OrigErr: fmt.Errorf("failed to download"), + OrigErr: errors.New("failed to download"), Bucket: aws.String("modelRepo"), Key: aws.String("model1/model.pt"), }) - var err error - err = s3manager.NewBatchError("BatchedDownloadIncomplete", "some objects have failed to download.", errs) + err := s3manager.NewBatchError("BatchedDownloadIncomplete", "some objects have failed to download.", errs) watcher := NewWatcher("/tmp/configs", modelDir, sugar) puller := Puller{ channelMap: make(map[string]*ModelChannel), @@ -373,11 +383,11 @@ var _ = Describe("Watcher", func() { modelName := "model1" modelStorageURI := "gs://testBucket/" err := cl.DownloadModel(modelDir, modelName, modelStorageURI) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) testFile := filepath.Join(modelDir, modelName, "testModel1") dat, err := os.ReadFile(testFile) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) Expect(string(dat)).To(Equal(modelContents)) }) }) @@ -431,7 +441,6 @@ var _ = Describe("Watcher", func() { Fail("Failed to write contents.") } - const secondaryContents = "Secondary Contents" w2 := bkt.Object("testModel2").NewWriter(ctx) if _, err := fmt.Fprint(w2, modelContents); err != nil { Fail("Failed to write contents.") @@ -439,7 +448,7 @@ var _ = Describe("Watcher", func() { modelStorageURI := "gs://testBucket/" err := cl.DownloadModel(modelDir, "", modelStorageURI) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) }) }) @@ -480,7 +489,6 @@ var _ = Describe("Watcher", func() { Fail("Failed to write contents.") } - const secondaryContents = "Secondary Contents" w2 := bkt.Object("testModel2").NewWriter(ctx) if _, err := fmt.Fprint(w2, modelContents); err != nil { Fail("Failed to write contents.") @@ -549,7 +557,7 @@ var _ = Describe("Watcher", func() { watcher.parseConfig(modelConfigs, true) go puller.processCommands(watcher.ModelEvents) puller.waitGroup.wg.Wait() - Expect(len(puller.channelMap)).To(Equal(0)) + Expect(puller.channelMap).To(BeEmpty()) Expect(puller.opStats["model1"][Add]).Should(Equal(1)) Expect(puller.opStats["model2"][Add]).Should(Equal(1)) }) @@ -588,11 +596,11 @@ var _ = Describe("Watcher", func() { } err := cl.DownloadModel(modelDir, modelName, modelStorageURI) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) testFile := filepath.Join(modelDir, modelName, modelFile) dat, err := os.ReadFile(testFile) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) Expect(string(dat)).To(Equal(modelContents + "\n")) } }) @@ -610,7 +618,7 @@ var _ = Describe("Watcher", func() { } actualErr := cl.DownloadModel(modelDir, modelName, invalidModelStorageURI) - Expect(actualErr).NotTo(Equal(nil)) + Expect(actualErr).To(HaveOccurred()) }) }) @@ -670,9 +678,9 @@ var _ = Describe("Watcher", func() { } err := zipcl.DownloadModel(modelDir, zipModel, zipStorageURI) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) err = tarcl.DownloadModel(modelDir, tarModel, tarStorageURI) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) } }) }) diff --git a/pkg/apis/apis.go b/pkg/apis/apis.go index f7577e59ed2..41eb7733eca 100644 --- a/pkg/apis/apis.go +++ b/pkg/apis/apis.go @@ -21,9 +21,10 @@ limitations under the License. package apis import ( + "k8s.io/apimachinery/pkg/runtime" + "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" "github.com/kserve/kserve/pkg/apis/serving/v1beta1" - "k8s.io/apimachinery/pkg/runtime" ) // AddToSchemes may be used to add all resources defined in the project to a Scheme diff --git a/pkg/apis/serving/v1alpha1/inference_graph.go b/pkg/apis/serving/v1alpha1/inference_graph.go index 7f0b12db6c9..3605fc49326 100644 --- a/pkg/apis/serving/v1alpha1/inference_graph.go +++ b/pkg/apis/serving/v1alpha1/inference_graph.go @@ -54,15 +54,15 @@ type InferenceGraphSpec struct { TimeoutSeconds *int64 `json:"timeout,omitempty"` // Minimum number of replicas, defaults to 1 but can be set to 0 to enable scale-to-zero. // +optional - MinReplicas *int `json:"minReplicas,omitempty"` + MinReplicas *int32 `json:"minReplicas,omitempty"` // Maximum number of replicas for autoscaling. // +optional - MaxReplicas int `json:"maxReplicas,omitempty"` + MaxReplicas int32 `json:"maxReplicas,omitempty"` // ScaleTarget specifies the integer target value of the metric type the Autoscaler watches for. // concurrency and rps targets are supported by Knative Pod Autoscaler // (https://knative.dev/docs/serving/autoscaling/autoscaling-targets/). // +optional - ScaleTarget *int `json:"scaleTarget,omitempty"` + ScaleTarget *int32 `json:"scaleTarget,omitempty"` // ScaleMetric defines the scaling metric type watched by autoscaler // possible values are concurrency, rps, cpu, memory. concurrency, rps are supported via // Knative Pod Autoscaler(https://knative.dev/docs/serving/autoscaling/autoscaling-metrics). diff --git a/pkg/apis/serving/v1alpha1/inference_graph_validation.go b/pkg/apis/serving/v1alpha1/inference_graph_validation.go index f51090f6005..a3b2e96e2b4 100644 --- a/pkg/apis/serving/v1alpha1/inference_graph_validation.go +++ b/pkg/apis/serving/v1alpha1/inference_graph_validation.go @@ -18,6 +18,7 @@ package v1alpha1 import ( "context" + "errors" "fmt" "regexp" @@ -187,7 +188,7 @@ func validateInferenceGraphRouterRoot(ig *InferenceGraph) error { return nil } } - return fmt.Errorf(RootNodeNotFoundError) + return errors.New(RootNodeNotFoundError) } // Validation of inference graph router type diff --git a/pkg/apis/serving/v1alpha1/inference_graph_validation_test.go b/pkg/apis/serving/v1alpha1/inference_graph_validation_test.go index 7bfa67bcb31..2f49133b646 100644 --- a/pkg/apis/serving/v1alpha1/inference_graph_validation_test.go +++ b/pkg/apis/serving/v1alpha1/inference_graph_validation_test.go @@ -18,6 +18,7 @@ package v1alpha1 import ( "context" + "errors" "fmt" "testing" @@ -124,7 +125,7 @@ func TestInferenceGraph_ValidateCreate(t *testing.T) { "without root node": { ig: makeTestInferenceGraph(), nodes: make(map[string]InferenceRouter), - errMatcher: gomega.MatchError(fmt.Errorf(RootNodeNotFoundError)), + errMatcher: gomega.MatchError(errors.New(RootNodeNotFoundError)), warningsMatcher: gomega.BeEmpty(), }, "with root node": { @@ -277,7 +278,6 @@ func TestInferenceGraph_ValidateCreate(t *testing.T) { if !g.Expect(warnings).To(scenario.warningsMatcher) { t.Errorf("got %s, want %t", warnings, scenario.warningsMatcher) } - }) } } diff --git a/pkg/apis/serving/v1alpha1/local_model_cache_types.go b/pkg/apis/serving/v1alpha1/local_model_cache_types.go index 745b2ba954c..6259fed6c23 100644 --- a/pkg/apis/serving/v1alpha1/local_model_cache_types.go +++ b/pkg/apis/serving/v1alpha1/local_model_cache_types.go @@ -32,7 +32,6 @@ type LocalModelCacheSpec struct { // group of nodes to cache the model on. // Todo: support more than 1 node groups // +kubebuilder:validation:MinItems=1 - // +kubebuilder:validation:MaxItems=1 NodeGroups []string `json:"nodeGroups" validate:"required"` } diff --git a/pkg/apis/serving/v1alpha1/servingruntime_types.go b/pkg/apis/serving/v1alpha1/servingruntime_types.go index 551d6554494..8555c5d48a6 100644 --- a/pkg/apis/serving/v1alpha1/servingruntime_types.go +++ b/pkg/apis/serving/v1alpha1/servingruntime_types.go @@ -17,13 +17,14 @@ limitations under the License. package v1alpha1 import ( - "fmt" + "errors" - "github.com/kserve/kserve/pkg/constants" "gopkg.in/go-playground/validator.v9" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + + "github.com/kserve/kserve/pkg/constants" ) // +k8s:openapi-gen=true @@ -174,8 +175,7 @@ type ServingRuntimeSpec struct { // ServingRuntimeStatus defines the observed state of ServingRuntime // +k8s:openapi-gen=true -type ServingRuntimeStatus struct { -} +type ServingRuntimeStatus struct{} // ServerType constant for specifying the runtime name // +k8s:openapi-gen=true @@ -339,7 +339,7 @@ func (srSpec *ServingRuntimeSpec) validatePodSpecAndWorkerSpec() error { // Additional validation for WorkerSpec if srSpec.WorkerSpec != nil { if len(srSpec.WorkerSpec.Containers) == 0 { - return fmt.Errorf("spec.workerSpec.containers: Required value") + return errors.New("spec.workerSpec.containers: Required value") } } diff --git a/pkg/apis/serving/v1alpha1/servingruntime_types_test.go b/pkg/apis/serving/v1alpha1/servingruntime_types_test.go index 32ea93dfaf7..3b28845441e 100644 --- a/pkg/apis/serving/v1alpha1/servingruntime_types_test.go +++ b/pkg/apis/serving/v1alpha1/servingruntime_types_test.go @@ -18,12 +18,14 @@ package v1alpha1 import ( "fmt" - "github.com/kserve/kserve/pkg/constants" + "testing" + "github.com/onsi/gomega" "google.golang.org/protobuf/proto" - "testing" - v1 "k8s.io/api/core/v1" + "github.com/kserve/kserve/pkg/constants" + + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" "sigs.k8s.io/yaml" ) @@ -43,25 +45,25 @@ func TestMarshalServingRuntime(t *testing.T) { "key1": "val1", "key2": "val2", }, - Containers: []v1.Container{ + Containers: []corev1.Container{ { Args: []string{"arg1", "arg2"}, Command: []string{"command", "command2"}, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "name", Value: "value"}, { Name: "fromSecret", - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{Key: "mykey"}, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{Key: "mykey"}, }, }, }, Image: "image", Name: "name", ImagePullPolicy: "IfNotPresent", - Resources: v1.ResourceRequirements{ - Limits: v1.ResourceList{ - v1.ResourceMemory: resource.MustParse("200Mi"), + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("200Mi"), }, }, }, @@ -95,25 +97,25 @@ func TestServingRuntimeSpec_IsDisabled(t *testing.T) { spec: ServingRuntimeSpec{ GrpcDataEndpoint: &endpoint, ServingRuntimePodSpec: ServingRuntimePodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Args: []string{"arg1", "arg2"}, Command: []string{"command", "command2"}, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "name", Value: "value"}, { Name: "fromSecret", - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{Key: "mykey"}, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{Key: "mykey"}, }, }, }, Image: "image", Name: "name", ImagePullPolicy: "IfNotPresent", - Resources: v1.ResourceRequirements{ - Limits: v1.ResourceList{ - v1.ResourceMemory: resource.MustParse("200Mi"), + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("200Mi"), }, }, }, @@ -133,25 +135,25 @@ func TestServingRuntimeSpec_IsDisabled(t *testing.T) { GrpcDataEndpoint: &endpoint, Disabled: proto.Bool(true), ServingRuntimePodSpec: ServingRuntimePodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Args: []string{"arg1", "arg2"}, Command: []string{"command", "command2"}, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "name", Value: "value"}, { Name: "fromSecret", - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{Key: "mykey"}, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{Key: "mykey"}, }, }, }, Image: "image", Name: "name", ImagePullPolicy: "IfNotPresent", - Resources: v1.ResourceRequirements{ - Limits: v1.ResourceList{ - v1.ResourceMemory: resource.MustParse("200Mi"), + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("200Mi"), }, }, }, @@ -204,25 +206,25 @@ func TestServingRuntimeSpec_ValidateField(t *testing.T) { spec: ServingRuntimeSpec{ GrpcDataEndpoint: &endpoint, ServingRuntimePodSpec: ServingRuntimePodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Args: []string{"arg1", "arg2"}, Command: []string{"command", "command2"}, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "name", Value: "value"}, { Name: "fromSecret", - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{Key: "mykey"}, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{Key: "mykey"}, }, }, }, Image: "image", Name: "name", ImagePullPolicy: "IfNotPresent", - Resources: v1.ResourceRequirements{ - Limits: v1.ResourceList{ - v1.ResourceMemory: resource.MustParse("200Mi"), + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("200Mi"), }, }, }, @@ -242,25 +244,25 @@ func TestServingRuntimeSpec_ValidateField(t *testing.T) { GrpcDataEndpoint: &endpoint, Disabled: proto.Bool(true), ServingRuntimePodSpec: ServingRuntimePodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Args: []string{"arg1", "arg2"}, Command: []string{"command", "command2"}, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "name", Value: "value"}, { Name: "fromSecret", - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{Key: "mykey"}, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{Key: "mykey"}, }, }, }, Image: "image", Name: "name", ImagePullPolicy: "IfNotPresent", - Resources: v1.ResourceRequirements{ - Limits: v1.ResourceList{ - v1.ResourceMemory: resource.MustParse("200Mi"), + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("200Mi"), }, }, }, @@ -285,25 +287,25 @@ func TestServingRuntimeSpec_ValidateField(t *testing.T) { GrpcDataEndpoint: &endpoint, Disabled: proto.Bool(true), ServingRuntimePodSpec: ServingRuntimePodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Args: []string{"arg1", "arg2"}, Command: []string{"command", "command2"}, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "name", Value: "value"}, { Name: "fromSecret", - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{Key: "mykey"}, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{Key: "mykey"}, }, }, }, Image: "image", Name: "name", ImagePullPolicy: "IfNotPresent", - Resources: v1.ResourceRequirements{ - Limits: v1.ResourceList{ - v1.ResourceMemory: resource.MustParse("200Mi"), + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("200Mi"), }, }, }, @@ -311,25 +313,25 @@ func TestServingRuntimeSpec_ValidateField(t *testing.T) { }, WorkerSpec: &WorkerSpec{ ServingRuntimePodSpec: ServingRuntimePodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Args: []string{"arg1", "arg2"}, Command: []string{"command", "command2"}, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "name", Value: "value"}, { Name: "fromSecret", - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{Key: "mykey"}, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{Key: "mykey"}, }, }, }, Image: "image", Name: "name", ImagePullPolicy: "IfNotPresent", - Resources: v1.ResourceRequirements{ - Limits: v1.ResourceList{ - v1.ResourceMemory: resource.MustParse("200Mi"), + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("200Mi"), }, }, }, @@ -368,25 +370,25 @@ func TestServingRuntimeSpec_IsMultiModelRuntime(t *testing.T) { spec: ServingRuntimeSpec{ GrpcDataEndpoint: &endpoint, ServingRuntimePodSpec: ServingRuntimePodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Args: []string{"arg1", "arg2"}, Command: []string{"command", "command2"}, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "name", Value: "value"}, { Name: "fromSecret", - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{Key: "mykey"}, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{Key: "mykey"}, }, }, }, Image: "image", Name: "name", ImagePullPolicy: "IfNotPresent", - Resources: v1.ResourceRequirements{ - Limits: v1.ResourceList{ - v1.ResourceMemory: resource.MustParse("200Mi"), + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("200Mi"), }, }, }, @@ -406,25 +408,25 @@ func TestServingRuntimeSpec_IsMultiModelRuntime(t *testing.T) { GrpcDataEndpoint: &endpoint, MultiModel: proto.Bool(true), ServingRuntimePodSpec: ServingRuntimePodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Args: []string{"arg1", "arg2"}, Command: []string{"command", "command2"}, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "name", Value: "value"}, { Name: "fromSecret", - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{Key: "mykey"}, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{Key: "mykey"}, }, }, }, Image: "image", Name: "name", ImagePullPolicy: "IfNotPresent", - Resources: v1.ResourceRequirements{ - Limits: v1.ResourceList{ - v1.ResourceMemory: resource.MustParse("200Mi"), + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("200Mi"), }, }, }, @@ -463,25 +465,25 @@ func TestServingRuntimeSpec_IsProtocolVersionSupported(t *testing.T) { spec: ServingRuntimeSpec{ GrpcDataEndpoint: &endpoint, ServingRuntimePodSpec: ServingRuntimePodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Args: []string{"arg1", "arg2"}, Command: []string{"command", "command2"}, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "name", Value: "value"}, { Name: "fromSecret", - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{Key: "mykey"}, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{Key: "mykey"}, }, }, }, Image: "image", Name: "name", ImagePullPolicy: "IfNotPresent", - Resources: v1.ResourceRequirements{ - Limits: v1.ResourceList{ - v1.ResourceMemory: resource.MustParse("200Mi"), + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("200Mi"), }, }, }, @@ -503,25 +505,25 @@ func TestServingRuntimeSpec_IsProtocolVersionSupported(t *testing.T) { MultiModel: proto.Bool(true), Disabled: proto.Bool(true), ServingRuntimePodSpec: ServingRuntimePodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Args: []string{"arg1", "arg2"}, Command: []string{"command", "command2"}, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "name", Value: "value"}, { Name: "fromSecret", - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{Key: "mykey"}, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{Key: "mykey"}, }, }, }, Image: "image", Name: "name", ImagePullPolicy: "IfNotPresent", - Resources: v1.ResourceRequirements{ - Limits: v1.ResourceList{ - v1.ResourceMemory: resource.MustParse("200Mi"), + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("200Mi"), }, }, }, @@ -545,25 +547,25 @@ func TestServingRuntimeSpec_IsProtocolVersionSupported(t *testing.T) { constants.ProtocolGRPCV2, }, ServingRuntimePodSpec: ServingRuntimePodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Args: []string{"arg1", "arg2"}, Command: []string{"command", "command2"}, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "name", Value: "value"}, { Name: "fromSecret", - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{Key: "mykey"}, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{Key: "mykey"}, }, }, }, Image: "image", Name: "name", ImagePullPolicy: "IfNotPresent", - Resources: v1.ResourceRequirements{ - Limits: v1.ResourceList{ - v1.ResourceMemory: resource.MustParse("200Mi"), + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("200Mi"), }, }, }, @@ -587,25 +589,25 @@ func TestServingRuntimeSpec_IsProtocolVersionSupported(t *testing.T) { constants.ProtocolGRPCV2, }, ServingRuntimePodSpec: ServingRuntimePodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Args: []string{"arg1", "arg2"}, Command: []string{"command", "command2"}, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "name", Value: "value"}, { Name: "fromSecret", - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{Key: "mykey"}, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{Key: "mykey"}, }, }, }, Image: "image", Name: "name", ImagePullPolicy: "IfNotPresent", - Resources: v1.ResourceRequirements{ - Limits: v1.ResourceList{ - v1.ResourceMemory: resource.MustParse("200Mi"), + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("200Mi"), }, }, }, @@ -645,25 +647,25 @@ func TestServingRuntimeSpec_GetPriority(t *testing.T) { spec: ServingRuntimeSpec{ GrpcDataEndpoint: &endpoint, ServingRuntimePodSpec: ServingRuntimePodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Args: []string{"arg1", "arg2"}, Command: []string{"command", "command2"}, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "name", Value: "value"}, { Name: "fromSecret", - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{Key: "mykey"}, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{Key: "mykey"}, }, }, }, Image: "image", Name: "name", ImagePullPolicy: "IfNotPresent", - Resources: v1.ResourceRequirements{ - Limits: v1.ResourceList{ - v1.ResourceMemory: resource.MustParse("200Mi"), + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("200Mi"), }, }, }, @@ -689,25 +691,25 @@ func TestServingRuntimeSpec_GetPriority(t *testing.T) { spec: ServingRuntimeSpec{ GrpcDataEndpoint: &endpoint, ServingRuntimePodSpec: ServingRuntimePodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Args: []string{"arg1", "arg2"}, Command: []string{"command", "command2"}, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "name", Value: "value"}, { Name: "fromSecret", - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{Key: "mykey"}, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{Key: "mykey"}, }, }, }, Image: "image", Name: "name", ImagePullPolicy: "IfNotPresent", - Resources: v1.ResourceRequirements{ - Limits: v1.ResourceList{ - v1.ResourceMemory: resource.MustParse("200Mi"), + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("200Mi"), }, }, }, @@ -732,25 +734,25 @@ func TestServingRuntimeSpec_GetPriority(t *testing.T) { spec: ServingRuntimeSpec{ GrpcDataEndpoint: &endpoint, ServingRuntimePodSpec: ServingRuntimePodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Args: []string{"arg1", "arg2"}, Command: []string{"command", "command2"}, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "name", Value: "value"}, { Name: "fromSecret", - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{Key: "mykey"}, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{Key: "mykey"}, }, }, }, Image: "image", Name: "name", ImagePullPolicy: "IfNotPresent", - Resources: v1.ResourceRequirements{ - Limits: v1.ResourceList{ - v1.ResourceMemory: resource.MustParse("200Mi"), + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("200Mi"), }, }, }, diff --git a/pkg/apis/serving/v1alpha1/storage_container_types_test.go b/pkg/apis/serving/v1alpha1/storage_container_types_test.go index 544f065ebdb..452bee87e42 100644 --- a/pkg/apis/serving/v1alpha1/storage_container_types_test.go +++ b/pkg/apis/serving/v1alpha1/storage_container_types_test.go @@ -20,29 +20,29 @@ import ( "testing" "github.com/onsi/gomega" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" ) func TestStorageContainerSpec_IsStorageUriSupported(t *testing.T) { g := gomega.NewGomegaWithT(t) customSpec := StorageContainerSpec{ - Container: v1.Container{ + Container: corev1.Container{ Image: "kserve/custom:latest", - Resources: v1.ResourceRequirements{ - Limits: v1.ResourceList{ - v1.ResourceMemory: resource.MustParse("200Mi"), + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("200Mi"), }, }, }, SupportedUriFormats: []SupportedUriFormat{{Prefix: "custom://"}}, } s3AzureSpec := StorageContainerSpec{ - Container: v1.Container{ + Container: corev1.Container{ Image: "kserve/storage-initializer:latest", - Resources: v1.ResourceRequirements{ - Limits: v1.ResourceList{ - v1.ResourceMemory: resource.MustParse("200Mi"), + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("200Mi"), }, }, }, @@ -83,7 +83,7 @@ func TestStorageContainerSpec_IsStorageUriSupported(t *testing.T) { for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { supported, err := tc.spec.IsStorageUriSupported(tc.storageUri) - g.Expect(err).To(gomega.BeNil()) + g.Expect(err).ToNot(gomega.HaveOccurred()) g.Expect(supported).To(gomega.Equal(tc.supported)) }) } diff --git a/pkg/apis/serving/v1alpha1/trained_model_status.go b/pkg/apis/serving/v1alpha1/trained_model_status.go index e0a17091030..6a1c5de02c8 100644 --- a/pkg/apis/serving/v1alpha1/trained_model_status.go +++ b/pkg/apis/serving/v1alpha1/trained_model_status.go @@ -17,7 +17,7 @@ limitations under the License. package v1alpha1 import ( - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" "knative.dev/pkg/apis" duckv1 "knative.dev/pkg/apis/duck/v1" ) @@ -71,17 +71,17 @@ func (ss *TrainedModelStatus) GetCondition(t apis.ConditionType) *apis.Condition // IsConditionReady returns the readiness for a given condition func (ss *TrainedModelStatus) IsConditionReady(t apis.ConditionType) bool { - return conditionSet.Manage(ss).GetCondition(t) != nil && conditionSet.Manage(ss).GetCondition(t).Status == v1.ConditionTrue + return conditionSet.Manage(ss).GetCondition(t) != nil && conditionSet.Manage(ss).GetCondition(t).Status == corev1.ConditionTrue } func (ss *TrainedModelStatus) SetCondition(conditionType apis.ConditionType, condition *apis.Condition) { switch { case condition == nil: - case condition.Status == v1.ConditionUnknown: + case condition.Status == corev1.ConditionUnknown: conditionSet.Manage(ss).MarkUnknown(conditionType, condition.Reason, condition.Message) - case condition.Status == v1.ConditionTrue: + case condition.Status == corev1.ConditionTrue: conditionSet.Manage(ss).MarkTrue(conditionType) - case condition.Status == v1.ConditionFalse: + case condition.Status == corev1.ConditionFalse: conditionSet.Manage(ss).MarkFalse(conditionType, condition.Reason, condition.Message) } } diff --git a/pkg/apis/serving/v1alpha1/trained_model_status_test.go b/pkg/apis/serving/v1alpha1/trained_model_status_test.go index c35854f7171..4e944e69b8b 100644 --- a/pkg/apis/serving/v1alpha1/trained_model_status_test.go +++ b/pkg/apis/serving/v1alpha1/trained_model_status_test.go @@ -17,14 +17,15 @@ limitations under the License. package v1alpha1 import ( + "testing" + "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/onsi/gomega" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" "knative.dev/pkg/apis" duckv1 "knative.dev/pkg/apis/duck/v1" knservingv1 "knative.dev/serving/pkg/apis/serving/v1" - "testing" ) func TestTrainedModelStatus_IsReady(t *testing.T) { @@ -42,7 +43,7 @@ func TestTrainedModelStatus_IsReady(t *testing.T) { Status: duckv1.Status{ Conditions: duckv1.Conditions{{ Type: "Foo", - Status: v1.ConditionTrue, + Status: corev1.ConditionTrue, }}, }, }, @@ -53,7 +54,7 @@ func TestTrainedModelStatus_IsReady(t *testing.T) { Status: duckv1.Status{ Conditions: duckv1.Conditions{{ Type: knservingv1.ServiceConditionReady, - Status: v1.ConditionFalse, + Status: corev1.ConditionFalse, }}, }, }, @@ -64,7 +65,7 @@ func TestTrainedModelStatus_IsReady(t *testing.T) { Status: duckv1.Status{ Conditions: duckv1.Conditions{{ Type: knservingv1.ServiceConditionReady, - Status: v1.ConditionUnknown, + Status: corev1.ConditionUnknown, }}, }, }, @@ -86,15 +87,15 @@ func TestTrainedModelStatus_IsReady(t *testing.T) { Conditions: duckv1.Conditions{ { Type: "ConfigurationsReady", - Status: v1.ConditionTrue, + Status: corev1.ConditionTrue, }, { Type: "RoutesReady", - Status: v1.ConditionTrue, + Status: corev1.ConditionTrue, }, { Type: knservingv1.ServiceConditionReady, - Status: v1.ConditionTrue, + Status: corev1.ConditionTrue, }, }, }, @@ -107,11 +108,11 @@ func TestTrainedModelStatus_IsReady(t *testing.T) { Conditions: duckv1.Conditions{ { Type: "Foo", - Status: v1.ConditionTrue, + Status: corev1.ConditionTrue, }, { Type: knservingv1.ConfigurationConditionReady, - Status: v1.ConditionFalse, + Status: corev1.ConditionFalse, }, }, }, @@ -146,14 +147,14 @@ func TestTrainedModelStatus_GetCondition(t *testing.T) { Status: duckv1.Status{ Conditions: duckv1.Conditions{{ Type: "Foo", - Status: v1.ConditionFalse, + Status: corev1.ConditionFalse, }}, }, }, Condition: "Foo", matcher: &apis.Condition{ Type: "Foo", - Status: v1.ConditionFalse, + Status: corev1.ConditionFalse, }, }, { name: "Get Ready condition", @@ -161,14 +162,14 @@ func TestTrainedModelStatus_GetCondition(t *testing.T) { Status: duckv1.Status{ Conditions: duckv1.Conditions{{ Type: knservingv1.ServiceConditionReady, - Status: v1.ConditionUnknown, + Status: corev1.ConditionUnknown, }}, }, }, Condition: knservingv1.ServiceConditionReady, matcher: &apis.Condition{ Type: knservingv1.ServiceConditionReady, - Status: v1.ConditionUnknown, + Status: corev1.ConditionUnknown, }, }} @@ -197,7 +198,7 @@ func TestTrainedModelStatus_IsConditionReady(t *testing.T) { Status: duckv1.Status{ Conditions: duckv1.Conditions{{ Type: "Foo", - Status: v1.ConditionTrue, + Status: corev1.ConditionTrue, }}, }, }, @@ -209,7 +210,7 @@ func TestTrainedModelStatus_IsConditionReady(t *testing.T) { Status: duckv1.Status{ Conditions: duckv1.Conditions{{ Type: knservingv1.ServiceConditionReady, - Status: v1.ConditionFalse, + Status: corev1.ConditionFalse, }}, }, }, @@ -221,7 +222,7 @@ func TestTrainedModelStatus_IsConditionReady(t *testing.T) { Status: duckv1.Status{ Conditions: duckv1.Conditions{{ Type: knservingv1.ServiceConditionReady, - Status: v1.ConditionUnknown, + Status: corev1.ConditionUnknown, }}, }, }, @@ -245,15 +246,15 @@ func TestTrainedModelStatus_IsConditionReady(t *testing.T) { Conditions: duckv1.Conditions{ { Type: "ConfigurationsReady", - Status: v1.ConditionTrue, + Status: corev1.ConditionTrue, }, { Type: "RoutesReady", - Status: v1.ConditionTrue, + Status: corev1.ConditionTrue, }, { Type: knservingv1.ServiceConditionReady, - Status: v1.ConditionTrue, + Status: corev1.ConditionTrue, }, }, }, @@ -267,11 +268,11 @@ func TestTrainedModelStatus_IsConditionReady(t *testing.T) { Conditions: duckv1.Conditions{ { Type: "Foo", - Status: v1.ConditionTrue, + Status: corev1.ConditionTrue, }, { Type: knservingv1.ConfigurationConditionReady, - Status: v1.ConditionFalse, + Status: corev1.ConditionFalse, }, }, }, @@ -297,72 +298,73 @@ func TestTrainedModelStatus_SetCondition(t *testing.T) { condition *apis.Condition conditionType apis.ConditionType expected *apis.Condition - }{{ - name: "set condition on empty status", - serviceStatus: TrainedModelStatus{}, - condition: &apis.Condition{ - Type: "Foo", - Status: v1.ConditionTrue, - }, - conditionType: "Foo", - expected: &apis.Condition{ - Type: "Foo", - Status: v1.ConditionTrue, - }, - }, { - name: "modify existing condition", - serviceStatus: TrainedModelStatus{ - Status: duckv1.Status{ - Conditions: duckv1.Conditions{{ - Type: "Foo", - Status: v1.ConditionTrue, - }}, + }{ + { + name: "set condition on empty status", + serviceStatus: TrainedModelStatus{}, + condition: &apis.Condition{ + Type: "Foo", + Status: corev1.ConditionTrue, }, - }, - condition: &apis.Condition{ - Type: "Foo", - Status: v1.ConditionFalse, - }, - conditionType: "Foo", - expected: &apis.Condition{ - Type: "Foo", - Status: v1.ConditionFalse, - }, - }, { - name: "set condition unknown", - serviceStatus: TrainedModelStatus{ - Status: duckv1.Status{ - Conditions: duckv1.Conditions{{ - Type: "Foo", - Status: v1.ConditionFalse, - }}, + conditionType: "Foo", + expected: &apis.Condition{ + Type: "Foo", + Status: corev1.ConditionTrue, }, - }, - condition: &apis.Condition{ - Type: "Foo", - Status: v1.ConditionUnknown, - Reason: "For testing purpose", - }, - conditionType: "Foo", - expected: &apis.Condition{ - Type: "Foo", - Status: v1.ConditionUnknown, - Reason: "For testing purpose", - }, - }, { - name: "condition is nil", - serviceStatus: TrainedModelStatus{ - Status: duckv1.Status{ - Conditions: duckv1.Conditions{{ - Type: knservingv1.ServiceConditionReady, - Status: v1.ConditionTrue, - }}, + }, { + name: "modify existing condition", + serviceStatus: TrainedModelStatus{ + Status: duckv1.Status{ + Conditions: duckv1.Conditions{{ + Type: "Foo", + Status: corev1.ConditionTrue, + }}, + }, }, + condition: &apis.Condition{ + Type: "Foo", + Status: corev1.ConditionFalse, + }, + conditionType: "Foo", + expected: &apis.Condition{ + Type: "Foo", + Status: corev1.ConditionFalse, + }, + }, { + name: "set condition unknown", + serviceStatus: TrainedModelStatus{ + Status: duckv1.Status{ + Conditions: duckv1.Conditions{{ + Type: "Foo", + Status: corev1.ConditionFalse, + }}, + }, + }, + condition: &apis.Condition{ + Type: "Foo", + Status: corev1.ConditionUnknown, + Reason: "For testing purpose", + }, + conditionType: "Foo", + expected: &apis.Condition{ + Type: "Foo", + Status: corev1.ConditionUnknown, + Reason: "For testing purpose", + }, + }, { + name: "condition is nil", + serviceStatus: TrainedModelStatus{ + Status: duckv1.Status{ + Conditions: duckv1.Conditions{{ + Type: knservingv1.ServiceConditionReady, + Status: corev1.ConditionTrue, + }}, + }, + }, + condition: nil, + conditionType: "Foo", + expected: nil, }, - condition: nil, - conditionType: "Foo", - expected: nil, - }, } for _, tc := range cases { @@ -370,7 +372,6 @@ func TestTrainedModelStatus_SetCondition(t *testing.T) { tc.serviceStatus.SetCondition(tc.conditionType, tc.condition) res := tc.serviceStatus.GetCondition(tc.conditionType) g.Expect(cmp.Equal(res, tc.expected, cmpopts.IgnoreFields(apis.Condition{}, "LastTransitionTime", "Severity"))).To(gomega.BeTrue()) - }) } } diff --git a/pkg/apis/serving/v1alpha1/trained_model_test.go b/pkg/apis/serving/v1alpha1/trained_model_test.go index 7c01ce40418..24800f3cee5 100644 --- a/pkg/apis/serving/v1alpha1/trained_model_test.go +++ b/pkg/apis/serving/v1alpha1/trained_model_test.go @@ -18,9 +18,10 @@ package v1alpha1 import ( "fmt" + "testing" + "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "testing" ) func TestTrainedModelList_TotalRequestedMemory(t *testing.T) { diff --git a/pkg/apis/serving/v1alpha1/trainedmodel_webhook.go b/pkg/apis/serving/v1alpha1/trainedmodel_webhook.go index c81df71d78f..d7bdf4ec36c 100644 --- a/pkg/apis/serving/v1alpha1/trainedmodel_webhook.go +++ b/pkg/apis/serving/v1alpha1/trainedmodel_webhook.go @@ -24,11 +24,12 @@ import ( "sigs.k8s.io/controller-runtime/pkg/webhook/admission" - "github.com/kserve/kserve/pkg/agent/storage" - "github.com/kserve/kserve/pkg/utils" "k8s.io/apimachinery/pkg/runtime" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/webhook" + + "github.com/kserve/kserve/pkg/agent/storage" + "github.com/kserve/kserve/pkg/utils" ) // regular expressions for validation of isvc name diff --git a/pkg/apis/serving/v1alpha1/trainedmodel_webhook_test.go b/pkg/apis/serving/v1alpha1/trainedmodel_webhook_test.go index 396277e0934..547d59f5713 100644 --- a/pkg/apis/serving/v1alpha1/trainedmodel_webhook_test.go +++ b/pkg/apis/serving/v1alpha1/trainedmodel_webhook_test.go @@ -302,15 +302,18 @@ func TestValidateDelete(t *testing.T) { } func (tm *TrainedModel) update(tmField string, value string) { - if tmField == name { + switch tmField { + case name: tm.Name = value - } else if tmField == infereceservice { + case infereceservice: tm.Spec.InferenceService = value - } else if tmField == storageURI { + case storageURI: tm.Spec.Model.StorageURI = value - } else if tmField == framework { + case framework: tm.Spec.Model.Framework = value - } else if tmField == memory { + case memory: tm.Spec.Model.Memory = resource.MustParse(value) + default: + // do nothing } } diff --git a/pkg/apis/serving/v1alpha1/v1alpha1.go b/pkg/apis/serving/v1alpha1/v1alpha1.go index d0893bfb934..bb0bda42cee 100644 --- a/pkg/apis/serving/v1alpha1/v1alpha1.go +++ b/pkg/apis/serving/v1alpha1/v1alpha1.go @@ -24,9 +24,10 @@ limitations under the License. package v1alpha1 import ( - "github.com/kserve/kserve/pkg/constants" "k8s.io/apimachinery/pkg/runtime/schema" "sigs.k8s.io/controller-runtime/pkg/scheme" + + "github.com/kserve/kserve/pkg/constants" ) var ( diff --git a/pkg/apis/serving/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/serving/v1alpha1/zz_generated.deepcopy.go index c1a6c62857c..e225e2f3897 100644 --- a/pkg/apis/serving/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/serving/v1alpha1/zz_generated.deepcopy.go @@ -254,12 +254,12 @@ func (in *InferenceGraphSpec) DeepCopyInto(out *InferenceGraphSpec) { } if in.MinReplicas != nil { in, out := &in.MinReplicas, &out.MinReplicas - *out = new(int) + *out = new(int32) **out = **in } if in.ScaleTarget != nil { in, out := &in.ScaleTarget, &out.ScaleTarget - *out = new(int) + *out = new(int32) **out = **in } if in.ScaleMetric != nil { diff --git a/pkg/apis/serving/v1beta1/component.go b/pkg/apis/serving/v1beta1/component.go index f00a33d7129..b0a95a06d7f 100644 --- a/pkg/apis/serving/v1beta1/component.go +++ b/pkg/apis/serving/v1beta1/component.go @@ -17,15 +17,17 @@ limitations under the License. package v1beta1 import ( + "errors" "fmt" "reflect" "strings" - "github.com/kserve/kserve/pkg/constants" - "github.com/kserve/kserve/pkg/utils" appsv1 "k8s.io/api/apps/v1" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/kserve/kserve/pkg/constants" + "github.com/kserve/kserve/pkg/utils" ) // Known error messages @@ -61,7 +63,7 @@ var ( type ComponentImplementation interface { Default(config *InferenceServicesConfig) Validate() error - GetContainer(metadata metav1.ObjectMeta, extensions *ComponentExtensionSpec, config *InferenceServicesConfig, predictorHost ...string) *v1.Container + GetContainer(metadata metav1.ObjectMeta, extensions *ComponentExtensionSpec, config *InferenceServicesConfig, predictorHost ...string) *corev1.Container GetStorageUri() *string GetStorageSpec() *StorageSpec GetProtocol() constants.InferenceServiceProtocol @@ -79,15 +81,15 @@ type Component interface { type ComponentExtensionSpec struct { // Minimum number of replicas, defaults to 1 but can be set to 0 to enable scale-to-zero. // +optional - MinReplicas *int `json:"minReplicas,omitempty"` + MinReplicas *int32 `json:"minReplicas,omitempty"` // Maximum number of replicas for autoscaling. // +optional - MaxReplicas int `json:"maxReplicas,omitempty"` + MaxReplicas int32 `json:"maxReplicas,omitempty"` // ScaleTarget specifies the integer target value of the metric type the Autoscaler watches for. // concurrency and rps targets are supported by Knative Pod Autoscaler // (https://knative.dev/docs/serving/autoscaling/autoscaling-targets/). // +optional - ScaleTarget *int `json:"scaleTarget,omitempty"` + ScaleTarget *int32 `json:"scaleTarget,omitempty"` // ScaleMetric defines the scaling metric type watched by autoscaler // possible values are concurrency, rps, cpu, memory. concurrency, rps are supported via // Knative Pod Autoscaler(https://knative.dev/docs/serving/autoscaling/autoscaling-metrics). @@ -117,7 +119,6 @@ type ComponentExtensionSpec struct { // More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ // +optional Annotations map[string]string `json:"annotations,omitempty"` - // The deployment strategy to use to replace existing pods with new ones. Only applicable for raw deployment mode. // +optional DeploymentStrategy *appsv1.DeploymentStrategy `json:"deploymentStrategy,omitempty"` @@ -171,18 +172,18 @@ func validateStorageSpec(storageSpec *StorageSpec, storageURI *string) error { return nil } -func validateReplicas(minReplicas *int, maxReplicas int) error { +func validateReplicas(minReplicas *int32, maxReplicas int32) error { if minReplicas == nil { minReplicas = &constants.DefaultMinReplicas } if *minReplicas < 0 { - return fmt.Errorf(MinReplicasLowerBoundExceededError) + return errors.New(MinReplicasLowerBoundExceededError) } if maxReplicas < 0 { - return fmt.Errorf(MaxReplicasLowerBoundExceededError) + return errors.New(MaxReplicasLowerBoundExceededError) } if *minReplicas > maxReplicas && maxReplicas != 0 { - return fmt.Errorf(MinReplicasShouldBeLessThanMaxError) + return errors.New(MinReplicasShouldBeLessThanMaxError) } return nil } @@ -192,7 +193,7 @@ func validateContainerConcurrency(containerConcurrency *int64) error { return nil } if *containerConcurrency < 0 { - return fmt.Errorf(ParallelismLowerBoundExceededError) + return errors.New(ParallelismLowerBoundExceededError) } return nil } @@ -200,7 +201,7 @@ func validateContainerConcurrency(containerConcurrency *int64) error { func validateLogger(logger *LoggerSpec) error { if logger != nil { if !(logger.Mode == LogAll || logger.Mode == LogRequest || logger.Mode == LogResponse) { - return fmt.Errorf(InvalidLoggerType) + return errors.New(InvalidLoggerType) } } return nil @@ -235,7 +236,7 @@ func NonNilComponents(objects []ComponentImplementation) (results []ComponentImp func ExactlyOneErrorFor(component Component) error { componentType := reflect.ValueOf(component).Type().Elem() implementationTypes := []string{} - for i := 0; i < componentType.NumField()-1; i++ { + for i := range componentType.NumField() - 1 { implementationTypes = append(implementationTypes, componentType.Field(i).Name) } return fmt.Errorf( diff --git a/pkg/apis/serving/v1beta1/component_test.go b/pkg/apis/serving/v1beta1/component_test.go index 7a670592737..a7f87b29b90 100644 --- a/pkg/apis/serving/v1beta1/component_test.go +++ b/pkg/apis/serving/v1beta1/component_test.go @@ -17,6 +17,7 @@ limitations under the License. package v1beta1 import ( + "errors" "fmt" "strings" "testing" @@ -24,6 +25,7 @@ import ( "github.com/onsi/gomega" "github.com/onsi/gomega/types" "google.golang.org/protobuf/proto" + "k8s.io/utils/ptr" ) func TestComponentExtensionSpec_Validate(t *testing.T) { @@ -35,7 +37,7 @@ func TestComponentExtensionSpec_Validate(t *testing.T) { }{ "InvalidReplica": { spec: ComponentExtensionSpec{ - MinReplicas: GetIntReference(3), + MinReplicas: ptr.To(int32(3)), MaxReplicas: 2, }, matcher: gomega.Not(gomega.BeNil()), @@ -149,7 +151,7 @@ func TestComponentExtensionSpec_validateLogger(t *testing.T) { logger: &LoggerSpec{ Mode: "InvalidMode", }, - matcher: gomega.MatchError(fmt.Errorf(InvalidLoggerType)), + matcher: gomega.MatchError(errors.New(InvalidLoggerType)), }, "LoggerIsNil": { logger: nil, diff --git a/pkg/apis/serving/v1beta1/configmap.go b/pkg/apis/serving/v1beta1/configmap.go index ac3f474e072..7ea92433da3 100644 --- a/pkg/apis/serving/v1beta1/configmap.go +++ b/pkg/apis/serving/v1beta1/configmap.go @@ -19,11 +19,14 @@ package v1beta1 import ( "context" "encoding/json" + "errors" "fmt" + "strings" "text/template" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/validation" "k8s.io/client-go/kubernetes" "github.com/kserve/kserve/pkg/constants" @@ -31,12 +34,14 @@ import ( // ConfigMap Keys const ( - ExplainerConfigKeyName = "explainers" - IngressConfigKeyName = "ingress" - DeployConfigName = "deploy" - LocalModelConfigName = "localModel" - SecurityConfigName = "security" - ServiceConfigName = "service" + ExplainerConfigKeyName = "explainers" + InferenceServiceConfigKeyName = "inferenceService" + IngressConfigKeyName = "ingress" + DeployConfigName = "deploy" + LocalModelConfigName = "localModel" + SecurityConfigName = "security" + ServiceConfigName = "service" + ResourceConfigName = "resource" ) const ( @@ -45,6 +50,14 @@ const ( DefaultUrlScheme = "http" ) +// Error messages +const ( + ErrKserveIngressGatewayRequired = "invalid ingress config - kserveIngressGateway is required" + ErrInvalidKserveIngressGatewayFormat = "invalid ingress config - kserveIngressGateway should be in the format /" + ErrInvalidKserveIngressGatewayName = "invalid ingress config - kserveIngressGateway gateway name is invalid" + ErrInvalidKserveIngressGatewayNamespace = "invalid ingress config - kserveIngressGateway gateway namespace is invalid" +) + // +kubebuilder:object:generate=false type ExplainerConfig struct { // explainer docker image name @@ -62,10 +75,19 @@ type ExplainersConfig struct { type InferenceServicesConfig struct { // Explainer configurations Explainers ExplainersConfig `json:"explainers"` + // ServiceAnnotationDisallowedList is a list of annotations that are not allowed to be propagated to Knative + // revisions + ServiceAnnotationDisallowedList []string `json:"serviceAnnotationDisallowedList,omitempty"` + // ServiceLabelDisallowedList is a list of labels that are not allowed to be propagated to Knative revisions + ServiceLabelDisallowedList []string `json:"serviceLabelDisallowedList,omitempty"` + // Resource configurations + Resource ResourceConfig `json:"resource,omitempty"` } // +kubebuilder:object:generate=false type IngressConfig struct { + EnableGatewayAPI bool `json:"enableGatewayApi,omitempty"` + KserveIngressGateway string `json:"kserveIngressGateway,omitempty"` IngressGateway string `json:"ingressGateway,omitempty"` KnativeLocalGatewayService string `json:"knativeLocalGatewayService,omitempty"` LocalGateway string `json:"localGateway,omitempty"` @@ -93,6 +115,15 @@ type LocalModelConfig struct { FSGroup *int64 `json:"fsGroup,omitempty"` JobTTLSecondsAfterFinished *int32 `json:"jobTTLSecondsAfterFinished,omitempty"` ReconcilationFrequencyInSecs *int64 `json:"reconcilationFrequencyInSecs,omitempty"` + DisableVolumeManagement bool `json:"disableVolumeManagement,omitempty"` +} + +// +kubebuilder:object:generate=false +type ResourceConfig struct { + CPULimit string `json:"cpuLimit,omitempty"` + MemoryLimit string `json:"memoryLimit,omitempty"` + CPURequest string `json:"cpuRequest,omitempty"` + MemoryRequest string `json:"memoryRequest,omitempty"` } // +kubebuilder:object:generate=false @@ -107,36 +138,85 @@ type ServiceConfig struct { ServiceClusterIPNone bool `json:"serviceClusterIPNone,omitempty"` } -func NewInferenceServicesConfig(clientset kubernetes.Interface) (*InferenceServicesConfig, error) { - configMap, err := clientset.CoreV1().ConfigMaps(constants.KServeNamespace).Get(context.TODO(), constants.InferenceServiceConfigMapName, metav1.GetOptions{}) - if err != nil { +func GetInferenceServiceConfigMap(ctx context.Context, clientset kubernetes.Interface) (*corev1.ConfigMap, error) { + if configMap, err := clientset.CoreV1().ConfigMaps(constants.KServeNamespace).Get( + ctx, constants.InferenceServiceConfigMapName, metav1.GetOptions{}); err != nil { return nil, err + } else { + return configMap, nil } +} + +func NewInferenceServicesConfig(isvcConfigMap *corev1.ConfigMap) (*InferenceServicesConfig, error) { icfg := &InferenceServicesConfig{} for _, err := range []error{ - getComponentConfig(ExplainerConfigKeyName, configMap, &icfg.Explainers), + getComponentConfig(ExplainerConfigKeyName, isvcConfigMap, &icfg.Explainers), + getComponentConfig(InferenceServiceConfigKeyName, isvcConfigMap, &icfg), } { if err != nil { return nil, err } } + + if isvc, ok := isvcConfigMap.Data[InferenceServiceConfigKeyName]; ok { + errisvc := json.Unmarshal([]byte(isvc), &icfg) + if errisvc != nil { + return nil, fmt.Errorf("unable to parse isvc config json: %w", errisvc) + } + if icfg.ServiceAnnotationDisallowedList == nil { + icfg.ServiceAnnotationDisallowedList = constants.ServiceAnnotationDisallowedList + } else { + icfg.ServiceAnnotationDisallowedList = append( + constants.ServiceAnnotationDisallowedList, + icfg.ServiceAnnotationDisallowedList...) + } + if icfg.ServiceLabelDisallowedList == nil { + icfg.ServiceLabelDisallowedList = constants.RevisionTemplateLabelDisallowedList + } else { + icfg.ServiceLabelDisallowedList = append( + constants.RevisionTemplateLabelDisallowedList, + icfg.ServiceLabelDisallowedList...) + } + } return icfg, nil } -func NewIngressConfig(clientset kubernetes.Interface) (*IngressConfig, error) { - configMap, err := clientset.CoreV1().ConfigMaps(constants.KServeNamespace).Get(context.TODO(), constants.InferenceServiceConfigMapName, metav1.GetOptions{}) - if err != nil { - return nil, err +func validateIngressGateway(ingressConfig *IngressConfig) error { + if ingressConfig.KserveIngressGateway == "" { + return errors.New(ErrKserveIngressGatewayRequired) + } + splits := strings.Split(ingressConfig.KserveIngressGateway, "/") + if len(splits) != 2 { + return errors.New(ErrInvalidKserveIngressGatewayFormat) + } + errs := validation.IsDNS1123Label(splits[0]) + if len(errs) != 0 { + return errors.New(ErrInvalidKserveIngressGatewayNamespace) + } + errs = validation.IsDNS1123Label(splits[1]) + if len(errs) != 0 { + return errors.New(ErrInvalidKserveIngressGatewayName) } + return nil +} + +func NewIngressConfig(isvcConfigMap *corev1.ConfigMap) (*IngressConfig, error) { ingressConfig := &IngressConfig{} - if ingress, ok := configMap.Data[IngressConfigKeyName]; ok { + if ingress, ok := isvcConfigMap.Data[IngressConfigKeyName]; ok { err := json.Unmarshal([]byte(ingress), &ingressConfig) if err != nil { return nil, fmt.Errorf("unable to parse ingress config json: %w", err) } - + if ingressConfig.EnableGatewayAPI { + if ingressConfig.KserveIngressGateway == "" { + return nil, errors.New("invalid ingress config - kserveIngressGateway is required") + } + if err := validateIngressGateway(ingressConfig); err != nil { + return nil, err + } + } if ingressConfig.IngressGateway == "" { - return nil, fmt.Errorf("invalid ingress config - ingressGateway is required") + return nil, errors.New("invalid ingress config - ingressGateway is required") } if ingressConfig.PathTemplate != "" { // TODO: ensure that the generated path is valid, that is: @@ -148,7 +228,7 @@ func NewIngressConfig(clientset kubernetes.Interface) (*IngressConfig, error) { return nil, fmt.Errorf("invalid ingress config, unable to parse pathTemplate: %w", err) } if ingressConfig.IngressDomain == "" { - return nil, fmt.Errorf("invalid ingress config - ingressDomain is required if pathTemplate is given") + return nil, errors.New("invalid ingress config - ingressDomain is required if pathTemplate is given") } } @@ -172,7 +252,7 @@ func NewIngressConfig(clientset kubernetes.Interface) (*IngressConfig, error) { return ingressConfig, nil } -func getComponentConfig(key string, configMap *v1.ConfigMap, componentConfig interface{}) error { +func getComponentConfig(key string, configMap *corev1.ConfigMap, componentConfig interface{}) error { if data, ok := configMap.Data[key]; ok { err := json.Unmarshal([]byte(data), componentConfig) if err != nil { @@ -182,39 +262,31 @@ func getComponentConfig(key string, configMap *v1.ConfigMap, componentConfig int return nil } -func NewDeployConfig(clientset kubernetes.Interface) (*DeployConfig, error) { - configMap, err := clientset.CoreV1().ConfigMaps(constants.KServeNamespace).Get(context.TODO(), constants.InferenceServiceConfigMapName, metav1.GetOptions{}) - if err != nil { - return nil, err - } +func NewDeployConfig(isvcConfigMap *corev1.ConfigMap) (*DeployConfig, error) { deployConfig := &DeployConfig{} - if deploy, ok := configMap.Data[DeployConfigName]; ok { + if deploy, ok := isvcConfigMap.Data[DeployConfigName]; ok { err := json.Unmarshal([]byte(deploy), &deployConfig) if err != nil { return nil, fmt.Errorf("unable to parse deploy config json: %w", err) } if deployConfig.DefaultDeploymentMode == "" { - return nil, fmt.Errorf("invalid deploy config, defaultDeploymentMode is required") + return nil, errors.New("invalid deploy config, defaultDeploymentMode is required") } if deployConfig.DefaultDeploymentMode != string(constants.Serverless) && deployConfig.DefaultDeploymentMode != string(constants.RawDeployment) && deployConfig.DefaultDeploymentMode != string(constants.ModelMeshDeployment) { - return nil, fmt.Errorf("invalid deployment mode. Supported modes are Serverless," + + return nil, errors.New("invalid deployment mode. Supported modes are Serverless," + " RawDeployment and ModelMesh") } } return deployConfig, nil } -func NewLocalModelConfig(clientset kubernetes.Interface) (*LocalModelConfig, error) { - configMap, err := clientset.CoreV1().ConfigMaps(constants.KServeNamespace).Get(context.TODO(), constants.InferenceServiceConfigMapName, metav1.GetOptions{}) - if err != nil { - return nil, err - } +func NewLocalModelConfig(isvcConfigMap *corev1.ConfigMap) (*LocalModelConfig, error) { localModelConfig := &LocalModelConfig{} - if localModel, ok := configMap.Data[LocalModelConfigName]; ok { + if localModel, ok := isvcConfigMap.Data[LocalModelConfigName]; ok { err := json.Unmarshal([]byte(localModel), &localModelConfig) if err != nil { return nil, err @@ -223,13 +295,9 @@ func NewLocalModelConfig(clientset kubernetes.Interface) (*LocalModelConfig, err return localModelConfig, nil } -func NewSecurityConfig(clientset kubernetes.Interface) (*SecurityConfig, error) { - configMap, err := clientset.CoreV1().ConfigMaps(constants.KServeNamespace).Get(context.TODO(), constants.InferenceServiceConfigMapName, metav1.GetOptions{}) - if err != nil { - return nil, err - } +func NewSecurityConfig(isvcConfigMap *corev1.ConfigMap) (*SecurityConfig, error) { securityConfig := &SecurityConfig{} - if security, ok := configMap.Data[SecurityConfigName]; ok { + if security, ok := isvcConfigMap.Data[SecurityConfigName]; ok { err := json.Unmarshal([]byte(security), &securityConfig) if err != nil { return nil, err @@ -238,14 +306,9 @@ func NewSecurityConfig(clientset kubernetes.Interface) (*SecurityConfig, error) return securityConfig, nil } -func NewServiceConfig(clientset kubernetes.Interface) (*ServiceConfig, error) { - configMap, err := clientset.CoreV1().ConfigMaps(constants.KServeNamespace).Get(context.TODO(), constants.InferenceServiceConfigMapName, metav1.GetOptions{}) - - if err != nil { - return nil, err - } +func NewServiceConfig(isvcConfigMap *corev1.ConfigMap) (*ServiceConfig, error) { serviceConfig := &ServiceConfig{} - if service, ok := configMap.Data[ServiceConfigName]; ok { + if service, ok := isvcConfigMap.Data[ServiceConfigName]; ok { err := json.Unmarshal([]byte(service), &serviceConfig) if err != nil { return nil, fmt.Errorf("unable to parse service config json: %w", err) diff --git a/pkg/apis/serving/v1beta1/configmap_test.go b/pkg/apis/serving/v1beta1/configmap_test.go index 71e1e53568d..942ad3c2410 100644 --- a/pkg/apis/serving/v1beta1/configmap_test.go +++ b/pkg/apis/serving/v1beta1/configmap_test.go @@ -17,17 +17,20 @@ limitations under the License. package v1beta1 import ( + "context" "fmt" "testing" - "github.com/kserve/kserve/pkg/constants" "github.com/onsi/gomega" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" fakeclientset "k8s.io/client-go/kubernetes/fake" + + "github.com/kserve/kserve/pkg/constants" ) var ( + KserveIngressGateway = "kserve/kserve-ingress-gateway" KnativeIngressGateway = "knative-serving/knative-ingress-gateway" KnativeLocalGatewayService = "test-destination" KnativeLocalGateway = "knative-serving/knative-local-gateway" @@ -37,6 +40,7 @@ var ( AdditionalDomain = "additional-example.com" AdditionalDomainExtra = "additional-example-extra.com" IngressConfigData = fmt.Sprintf(`{ + "kserveIngressGateway" : "%s", "ingressGateway" : "%s", "knativeLocalGatewayService" : "%s", "localGateway" : "%s", @@ -44,33 +48,48 @@ var ( "ingressDomain": "%s", "urlScheme": "https", "additionalIngressDomains": ["%s","%s"] - }`, KnativeIngressGateway, KnativeLocalGatewayService, KnativeLocalGateway, LocalGatewayService, IngressDomain, + }`, KserveIngressGateway, KnativeIngressGateway, KnativeLocalGatewayService, KnativeLocalGateway, LocalGatewayService, IngressDomain, AdditionalDomain, AdditionalDomainExtra) ServiceConfigData = fmt.Sprintf(`{ "serviceClusterIPNone" : %t }`, true) + + ISCVWithData = fmt.Sprintf(`{ + "serviceAnnotationDisallowedList": ["%s","%s"], + "serviceLabelDisallowedList": ["%s","%s"] + }`, "my.custom.annotation/1", "my.custom.annotation/2", + "my.custom.label.1", "my.custom.label.2") + + ISCVNoData = fmt.Sprintf(`{ + "serviceAnnotationDisallowedList": %s, + "serviceLabelDisallowedList": %s + }`, []string{}, []string{}) ) func TestNewInferenceServiceConfig(t *testing.T) { g := gomega.NewGomegaWithT(t) - clientset := fakeclientset.NewSimpleClientset(&v1.ConfigMap{ + clientset := fakeclientset.NewSimpleClientset(&corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{Name: constants.InferenceServiceConfigMapName, Namespace: constants.KServeNamespace}, }) - isvcConfig, err := NewInferenceServicesConfig(clientset) - g.Expect(err).Should(gomega.BeNil()) + isvcConfigMap, err := GetInferenceServiceConfigMap(context.Background(), clientset) + g.Expect(err).ShouldNot(gomega.HaveOccurred()) + isvcConfig, err := NewInferenceServicesConfig(isvcConfigMap) + g.Expect(err).ShouldNot(gomega.HaveOccurred()) g.Expect(isvcConfig).ShouldNot(gomega.BeNil()) } func TestNewIngressConfig(t *testing.T) { g := gomega.NewGomegaWithT(t) - clientset := fakeclientset.NewSimpleClientset(&v1.ConfigMap{ + clientset := fakeclientset.NewSimpleClientset(&corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{Name: constants.InferenceServiceConfigMapName, Namespace: constants.KServeNamespace}, Data: map[string]string{ IngressConfigKeyName: IngressConfigData, }, }) - ingressCfg, err := NewIngressConfig(clientset) - g.Expect(err).Should(gomega.BeNil()) + configMap, err := GetInferenceServiceConfigMap(context.Background(), clientset) + g.Expect(err).ShouldNot(gomega.HaveOccurred()) + ingressCfg, err := NewIngressConfig(configMap) + g.Expect(err).ShouldNot(gomega.HaveOccurred()) g.Expect(ingressCfg).ShouldNot(gomega.BeNil()) g.Expect(ingressCfg.IngressGateway).To(gomega.Equal(KnativeIngressGateway)) @@ -84,68 +103,174 @@ func TestNewIngressConfig(t *testing.T) { func TestNewIngressConfigDefaultKnativeService(t *testing.T) { g := gomega.NewGomegaWithT(t) - clientset := fakeclientset.NewSimpleClientset(&v1.ConfigMap{ + clientset := fakeclientset.NewSimpleClientset(&corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{Name: constants.InferenceServiceConfigMapName, Namespace: constants.KServeNamespace}, Data: map[string]string{ IngressConfigKeyName: fmt.Sprintf(`{ + "kserveIngressGateway" : "%s", "ingressGateway" : "%s", "localGateway" : "%s", "localGatewayService" : "%s", "ingressDomain": "%s", "urlScheme": "https", "additionalIngressDomains": ["%s","%s"] - }`, KnativeIngressGateway, KnativeLocalGateway, LocalGatewayService, IngressDomain, + }`, KserveIngressGateway, KnativeIngressGateway, KnativeLocalGateway, LocalGatewayService, IngressDomain, AdditionalDomain, AdditionalDomainExtra), }, }) - ingressCfg, err := NewIngressConfig(clientset) - g.Expect(err).Should(gomega.BeNil()) + configMap, err := GetInferenceServiceConfigMap(context.Background(), clientset) + g.Expect(err).ShouldNot(gomega.HaveOccurred()) + ingressCfg, err := NewIngressConfig(configMap) + g.Expect(err).ShouldNot(gomega.HaveOccurred()) g.Expect(ingressCfg).ShouldNot(gomega.BeNil()) g.Expect(ingressCfg.KnativeLocalGatewayService).To(gomega.Equal(LocalGatewayService)) } func TestNewDeployConfig(t *testing.T) { g := gomega.NewGomegaWithT(t) - clientset := fakeclientset.NewSimpleClientset(&v1.ConfigMap{ + clientset := fakeclientset.NewSimpleClientset(&corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{Name: constants.InferenceServiceConfigMapName, Namespace: constants.KServeNamespace}, }) - deployConfig, err := NewDeployConfig(clientset) - g.Expect(err).Should(gomega.BeNil()) + isvcConfigMap, err := GetInferenceServiceConfigMap(context.Background(), clientset) + g.Expect(err).ShouldNot(gomega.HaveOccurred()) + deployConfig, err := NewDeployConfig(isvcConfigMap) + g.Expect(err).ShouldNot(gomega.HaveOccurred()) g.Expect(deployConfig).ShouldNot(gomega.BeNil()) } func TestNewServiceConfig(t *testing.T) { g := gomega.NewGomegaWithT(t) // nothing declared - empty := fakeclientset.NewSimpleClientset(&v1.ConfigMap{ + empty := fakeclientset.NewSimpleClientset(&corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{Name: constants.InferenceServiceConfigMapName, Namespace: constants.KServeNamespace}, }) - emp, err := NewServiceConfig(empty) - g.Expect(err).Should(gomega.BeNil()) + isvcConfigMap, err := GetInferenceServiceConfigMap(context.Background(), empty) + g.Expect(err).ShouldNot(gomega.HaveOccurred()) + emp, err := NewServiceConfig(isvcConfigMap) + g.Expect(err).ShouldNot(gomega.HaveOccurred()) g.Expect(emp).ShouldNot(gomega.BeNil()) // with value - withTrue := fakeclientset.NewSimpleClientset(&v1.ConfigMap{ + withTrue := fakeclientset.NewSimpleClientset(&corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{Name: constants.InferenceServiceConfigMapName, Namespace: constants.KServeNamespace}, Data: map[string]string{ ServiceConfigName: ServiceConfigData, }, }) - wt, err := NewServiceConfig(withTrue) - g.Expect(err).Should(gomega.BeNil()) + isvcConfigMap, err = GetInferenceServiceConfigMap(context.Background(), withTrue) + g.Expect(err).ShouldNot(gomega.HaveOccurred()) + wt, err := NewServiceConfig(isvcConfigMap) + + g.Expect(err).ShouldNot(gomega.HaveOccurred()) g.Expect(wt).ShouldNot(gomega.BeNil()) g.Expect(wt.ServiceClusterIPNone).Should(gomega.BeTrue()) // no value, should be nil - noValue := fakeclientset.NewSimpleClientset(&v1.ConfigMap{ + noValue := fakeclientset.NewSimpleClientset(&corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{Name: constants.InferenceServiceConfigMapName, Namespace: constants.KServeNamespace}, Data: map[string]string{ ServiceConfigName: `{}`, }, }) - nv, err := NewServiceConfig(noValue) - g.Expect(err).Should(gomega.BeNil()) + isvcConfigMap, err = GetInferenceServiceConfigMap(context.Background(), noValue) + g.Expect(err).ShouldNot(gomega.HaveOccurred()) + nv, err := NewServiceConfig(isvcConfigMap) + g.Expect(err).ShouldNot(gomega.HaveOccurred()) g.Expect(nv).ShouldNot(gomega.BeNil()) g.Expect(nv.ServiceClusterIPNone).Should(gomega.BeFalse()) +} + +func TestInferenceServiceDisallowedLists(t *testing.T) { + g := gomega.NewGomegaWithT(t) + clientset := fakeclientset.NewSimpleClientset(&corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{Name: constants.InferenceServiceConfigMapName, Namespace: constants.KServeNamespace}, + Data: map[string]string{ + InferenceServiceConfigKeyName: ISCVWithData, + }, + }) + isvcConfigMap, err := GetInferenceServiceConfigMap(context.Background(), clientset) + g.Expect(err).ShouldNot(gomega.HaveOccurred()) + isvcConfigWithData, err := NewInferenceServicesConfig(isvcConfigMap) + g.Expect(err).ShouldNot(gomega.HaveOccurred()) + g.Expect(isvcConfigWithData).ShouldNot(gomega.BeNil()) + + //nolint:gocritic + annotations := append(constants.ServiceAnnotationDisallowedList, []string{"my.custom.annotation/1", "my.custom.annotation/2"}...) + g.Expect(isvcConfigWithData.ServiceAnnotationDisallowedList).To(gomega.Equal(annotations)) + //nolint:gocritic + labels := append(constants.RevisionTemplateLabelDisallowedList, []string{"my.custom.label.1", "my.custom.label.2"}...) + g.Expect(isvcConfigWithData.ServiceLabelDisallowedList).To(gomega.Equal(labels)) + + // with no data + clientsetWithoutData := fakeclientset.NewSimpleClientset(&corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{Name: constants.InferenceServiceConfigMapName, Namespace: constants.KServeNamespace}, + Data: map[string]string{ + InferenceServiceConfigKeyName: ISCVNoData, + }, + }) + isvcConfigMap, err = GetInferenceServiceConfigMap(context.Background(), clientsetWithoutData) + g.Expect(err).ShouldNot(gomega.HaveOccurred()) + isvcConfigWithoutData, err := NewInferenceServicesConfig(isvcConfigMap) + g.Expect(err).ShouldNot(gomega.HaveOccurred()) + g.Expect(isvcConfigWithoutData).ShouldNot(gomega.BeNil()) + g.Expect(isvcConfigWithoutData.ServiceAnnotationDisallowedList).To(gomega.Equal(constants.ServiceAnnotationDisallowedList)) + g.Expect(isvcConfigWithoutData.ServiceLabelDisallowedList).To(gomega.Equal(constants.RevisionTemplateLabelDisallowedList)) +} + +func TestValidateIngressGateway(t *testing.T) { + g := gomega.NewGomegaWithT(t) + + tests := []struct { + name string + ingressConfig *IngressConfig + expectedError string + }{ + { + name: "valid ingress gateway", + ingressConfig: &IngressConfig{ + KserveIngressGateway: "kserve/kserve-ingress-gateway", + }, + expectedError: "", + }, + { + name: "missing kserveIngressGateway", + ingressConfig: &IngressConfig{ + KserveIngressGateway: "", + }, + expectedError: ErrKserveIngressGatewayRequired, + }, + { + name: "invalid format for kserveIngressGateway", + ingressConfig: &IngressConfig{ + KserveIngressGateway: "invalid-format", + }, + expectedError: ErrInvalidKserveIngressGatewayFormat, + }, + { + name: "invalid namespace in kserveIngressGateway", + ingressConfig: &IngressConfig{ + KserveIngressGateway: "invalid_namespace/kserve-ingress-gateway", + }, + expectedError: ErrInvalidKserveIngressGatewayNamespace, + }, + { + name: "invalid name in kserveIngressGateway", + ingressConfig: &IngressConfig{ + KserveIngressGateway: "kserve/invalid_name", + }, + expectedError: ErrInvalidKserveIngressGatewayName, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := validateIngressGateway(tt.ingressConfig) + if tt.expectedError == "" { + g.Expect(err).ShouldNot(gomega.HaveOccurred()) + } else { + g.Expect(err).Should(gomega.HaveOccurred()) + g.Expect(err.Error()).Should(gomega.ContainSubstring(tt.expectedError)) + } + }) + } } diff --git a/pkg/apis/serving/v1beta1/explainer.go b/pkg/apis/serving/v1beta1/explainer.go index ba6402d2bf4..8b5e2b563df 100644 --- a/pkg/apis/serving/v1beta1/explainer.go +++ b/pkg/apis/serving/v1beta1/explainer.go @@ -17,8 +17,9 @@ limitations under the License. package v1beta1 import ( + corev1 "k8s.io/api/core/v1" + "github.com/kserve/kserve/pkg/utils" - v1 "k8s.io/api/core/v1" ) // ExplainerSpec defines the container spec for a model explanation server, @@ -47,7 +48,7 @@ type ExplainerExtensionSpec struct { // Container enables overrides for the predictor. // Each framework will have different defaults that are populated in the underlying container spec. // +optional - v1.Container `json:",inline"` + corev1.Container `json:",inline"` // Storage Spec for model location // +optional Storage *StorageSpec `json:"storage,omitempty"` diff --git a/pkg/apis/serving/v1beta1/explainer_art.go b/pkg/apis/serving/v1beta1/explainer_art.go index 1bd0b4b6bf4..596909d2bec 100644 --- a/pkg/apis/serving/v1beta1/explainer_art.go +++ b/pkg/apis/serving/v1beta1/explainer_art.go @@ -21,11 +21,12 @@ import ( "sort" "strconv" - "github.com/kserve/kserve/pkg/constants" - "github.com/kserve/kserve/pkg/utils" "google.golang.org/protobuf/proto" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/kserve/kserve/pkg/constants" + "github.com/kserve/kserve/pkg/utils" ) type ARTExplainerType string @@ -44,14 +45,15 @@ type ARTExplainerSpec struct { var _ ComponentImplementation = &ARTExplainerSpec{} -func (s *ARTExplainerSpec) GetResourceRequirements() *v1.ResourceRequirements { +func (s *ARTExplainerSpec) GetResourceRequirements() *corev1.ResourceRequirements { // return the ResourceRequirements value if set on the spec return &s.Resources } func (s *ARTExplainerSpec) GetContainer(metadata metav1.ObjectMeta, extensions *ComponentExtensionSpec, config *InferenceServicesConfig, - predictorHost ...string) *v1.Container { - var args = []string{ + predictorHost ...string, +) *corev1.Container { + args := []string{ constants.ArgumentModelName, metadata.Name, constants.ArgumentHttpPort, constants.InferenceServiceDefaultHttpPort, } @@ -95,7 +97,7 @@ func (s *ARTExplainerSpec) Default(config *InferenceServicesConfig) { if s.RuntimeVersion == nil { s.RuntimeVersion = proto.String(config.Explainers.ARTExplainer.DefaultImageVersion) } - setResourceRequirementDefaults(&s.Resources) + setResourceRequirementDefaults(config, &s.Resources) } func (s *ARTExplainerSpec) GetProtocol() constants.InferenceServiceProtocol { diff --git a/pkg/apis/serving/v1beta1/explainer_art_test.go b/pkg/apis/serving/v1beta1/explainer_art_test.go index 37ce5e34c06..275b4cd5bf1 100644 --- a/pkg/apis/serving/v1beta1/explainer_art_test.go +++ b/pkg/apis/serving/v1beta1/explainer_art_test.go @@ -19,13 +19,14 @@ package v1beta1 import ( "testing" - "github.com/kserve/kserve/pkg/constants" "github.com/onsi/gomega" "github.com/onsi/gomega/types" "google.golang.org/protobuf/proto" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/kserve/kserve/pkg/constants" ) func TestARTExplainer(t *testing.T) { @@ -64,14 +65,13 @@ func TestARTExplainer(t *testing.T) { } func TestCreateARTExplainerContainer(t *testing.T) { - - var requestedResource = v1.ResourceRequirements{ - Limits: v1.ResourceList{ + requestedResource := corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ "cpu": resource.Quantity{ Format: "100", }, }, - Requests: v1.ResourceList{ + Requests: corev1.ResourceList{ "cpu": resource.Quantity{ Format: "90", }, @@ -88,11 +88,11 @@ func TestCreateARTExplainerContainer(t *testing.T) { ComponentExtensionSpec := ComponentExtensionSpec{ MaxReplicas: 2, } - var spec = ARTExplainerSpec{ + spec := ARTExplainerSpec{ Type: "SquareAttack", ExplainerExtensionSpec: ExplainerExtensionSpec{ StorageURI: "gs://someUri", - Container: v1.Container{ + Container: corev1.Container{ Resources: requestedResource, }, RuntimeVersion: proto.String("0.5.0"), @@ -100,7 +100,7 @@ func TestCreateARTExplainerContainer(t *testing.T) { } g := gomega.NewGomegaWithT(t) - expectedContainer := &v1.Container{ + expectedContainer := &corev1.Container{ Image: "kfserving/art-server:0.5.0", Name: constants.InferenceServiceContainerName, Resources: requestedResource, @@ -125,14 +125,13 @@ func TestCreateARTExplainerContainer(t *testing.T) { } func TestCreateARTExplainerContainerWithConfig(t *testing.T) { - - var requestedResource = v1.ResourceRequirements{ - Limits: v1.ResourceList{ + requestedResource := corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ "cpu": resource.Quantity{ Format: "100", }, }, - Requests: v1.ResourceList{ + Requests: corev1.ResourceList{ "cpu": resource.Quantity{ Format: "90", }, @@ -149,11 +148,11 @@ func TestCreateARTExplainerContainerWithConfig(t *testing.T) { ComponentExtensionSpec := ComponentExtensionSpec{ MaxReplicas: 2, } - var spec = ARTExplainerSpec{ + spec := ARTExplainerSpec{ Type: "SquareAttack", ExplainerExtensionSpec: ExplainerExtensionSpec{ StorageURI: "gs://someUri", - Container: v1.Container{ + Container: corev1.Container{ Resources: requestedResource, }, RuntimeVersion: proto.String("0.5.0"), @@ -166,7 +165,7 @@ func TestCreateARTExplainerContainerWithConfig(t *testing.T) { } g := gomega.NewGomegaWithT(t) - expectedContainer := &v1.Container{ + expectedContainer := &corev1.Container{ Image: "kfserving/art-server:0.5.0", Name: constants.InferenceServiceContainerName, Resources: requestedResource, diff --git a/pkg/apis/serving/v1beta1/explainer_custom.go b/pkg/apis/serving/v1beta1/explainer_custom.go index 326d1c846ea..0f262877983 100644 --- a/pkg/apis/serving/v1beta1/explainer_custom.go +++ b/pkg/apis/serving/v1beta1/explainer_custom.go @@ -20,21 +20,22 @@ import ( "fmt" "strconv" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "github.com/kserve/kserve/pkg/constants" "github.com/kserve/kserve/pkg/utils" - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) // CustomExplainer defines arguments for configuring a custom explainer. type CustomExplainer struct { - v1.PodSpec `json:",inline"` + corev1.PodSpec `json:",inline"` } var _ ComponentImplementation = &CustomExplainer{} func NewCustomExplainer(podSpec *PodSpec) *CustomExplainer { - return &CustomExplainer{PodSpec: v1.PodSpec(*podSpec)} + return &CustomExplainer{PodSpec: corev1.PodSpec(*podSpec)} } // Validate the spec @@ -45,10 +46,10 @@ func (s *CustomExplainer) Validate() error { // Default sets defaults on the resource func (c *CustomExplainer) Default(config *InferenceServicesConfig) { if len(c.Containers) == 0 { - c.Containers = append(c.Containers, v1.Container{}) + c.Containers = append(c.Containers, corev1.Container{}) } c.Containers[0].Name = constants.InferenceServiceContainerName - setResourceRequirementDefaults(&c.Containers[0].Resources) + setResourceRequirementDefaults(config, &c.Containers[0].Resources) } func (c *CustomExplainer) GetStorageUri() *string { @@ -67,7 +68,8 @@ func (c *CustomExplainer) GetStorageSpec() *StorageSpec { // GetContainer transforms the resource into a container spec func (c *CustomExplainer) GetContainer(metadata metav1.ObjectMeta, extensions *ComponentExtensionSpec, config *InferenceServicesConfig, - predictorHost ...string) *v1.Container { + predictorHost ...string, +) *corev1.Container { container := &c.Containers[0] if !utils.IncludesArg(container.Args, constants.ArgumentModelName) { container.Args = append(container.Args, []string{ diff --git a/pkg/apis/serving/v1beta1/explainer_custom_test.go b/pkg/apis/serving/v1beta1/explainer_custom_test.go index 3389593f30d..7cc9546c93f 100644 --- a/pkg/apis/serving/v1beta1/explainer_custom_test.go +++ b/pkg/apis/serving/v1beta1/explainer_custom_test.go @@ -20,22 +20,29 @@ import ( "fmt" "testing" - "github.com/kserve/kserve/pkg/constants" "github.com/onsi/gomega" "google.golang.org/protobuf/proto" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/kserve/kserve/pkg/constants" ) func TestCustomExplainerDefaulter(t *testing.T) { g := gomega.NewGomegaWithT(t) config := InferenceServicesConfig{ Explainers: ExplainersConfig{}, + Resource: ResourceConfig{ + CPULimit: "1", + MemoryLimit: "2Gi", + CPURequest: "1", + MemoryRequest: "2Gi", + }, } - defaultResource = v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("1"), - v1.ResourceMemory: resource.MustParse("2Gi"), + defaultResource := corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("2Gi"), } scenarios := map[string]struct { spec ExplainerSpec @@ -44,9 +51,9 @@ func TestCustomExplainerDefaulter(t *testing.T) { "DefaultResources": { spec: ExplainerSpec{ PodSpec: PodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "STORAGE_URI", Value: "hdfs://modelzoo", @@ -58,16 +65,16 @@ func TestCustomExplainerDefaulter(t *testing.T) { }, expected: ExplainerSpec{ PodSpec: PodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "STORAGE_URI", Value: "hdfs://modelzoo", }, }, - Resources: v1.ResourceRequirements{ + Resources: corev1.ResourceRequirements{ Requests: defaultResource, Limits: defaultResource, }, @@ -80,7 +87,7 @@ func TestCustomExplainerDefaulter(t *testing.T) { for name, scenario := range scenarios { t.Run(name, func(t *testing.T) { - explainer := CustomExplainer{PodSpec: v1.PodSpec(scenario.spec.PodSpec)} + explainer := CustomExplainer{PodSpec: corev1.PodSpec(scenario.spec.PodSpec)} explainer.Default(&config) if !g.Expect(scenario.spec).To(gomega.Equal(scenario.expected)) { t.Errorf("got %v, want %v", scenario.spec, scenario.expected) @@ -90,26 +97,25 @@ func TestCustomExplainerDefaulter(t *testing.T) { } func TestCreateCustomExplainerContainer(t *testing.T) { - - var requestedResource = v1.ResourceRequirements{ - Limits: v1.ResourceList{ + requestedResource := corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ "cpu": resource.Quantity{ Format: "100", }, "memory": resource.MustParse("1Gi"), }, - Requests: v1.ResourceList{ + Requests: corev1.ResourceList{ "cpu": resource.Quantity{ Format: "90", }, "memory": resource.MustParse("1Gi"), }, } - var config = InferenceServicesConfig{} + config := InferenceServicesConfig{} g := gomega.NewGomegaWithT(t) scenarios := map[string]struct { isvc InferenceService - expectedContainerSpec *v1.Container + expectedContainerSpec *corev1.Container }{ "ContainerSpecWithCustomImage": { isvc: InferenceService{ @@ -121,7 +127,7 @@ func TestCreateCustomExplainerContainer(t *testing.T) { SKLearn: &SKLearnSpec{ PredictorExtensionSpec: PredictorExtensionSpec{ StorageURI: proto.String("gs://someUri"), - Container: v1.Container{ + Container: corev1.Container{ Image: "customImage:0.1.0", Resources: requestedResource, }, @@ -130,10 +136,10 @@ func TestCreateCustomExplainerContainer(t *testing.T) { }, Explainer: &ExplainerSpec{ PodSpec: PodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Image: "explainer:0.1.0", - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "STORAGE_URI", Value: "hdfs://modelzoo", @@ -146,7 +152,7 @@ func TestCreateCustomExplainerContainer(t *testing.T) { }, }, }, - expectedContainerSpec: &v1.Container{ + expectedContainerSpec: &corev1.Container{ Image: "explainer:0.1.0", Name: constants.InferenceServiceContainerName, Resources: requestedResource, @@ -158,7 +164,7 @@ func TestCreateCustomExplainerContainer(t *testing.T) { "--http_port", "8080", }, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "STORAGE_URI", Value: "hdfs://modelzoo", @@ -179,7 +185,7 @@ func TestCreateCustomExplainerContainer(t *testing.T) { SKLearn: &SKLearnSpec{ PredictorExtensionSpec: PredictorExtensionSpec{ StorageURI: proto.String("gs://someUri"), - Container: v1.Container{ + Container: corev1.Container{ Resources: requestedResource, }, }, @@ -190,10 +196,10 @@ func TestCreateCustomExplainerContainer(t *testing.T) { ContainerConcurrency: proto.Int64(2), }, PodSpec: PodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Image: "explainer:0.1.0", - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "STORAGE_URI", Value: "hdfs://modelzoo", @@ -206,7 +212,7 @@ func TestCreateCustomExplainerContainer(t *testing.T) { }, }, }, - expectedContainerSpec: &v1.Container{ + expectedContainerSpec: &corev1.Container{ Image: "explainer:0.1.0", Name: constants.InferenceServiceContainerName, Resources: requestedResource, @@ -220,7 +226,7 @@ func TestCreateCustomExplainerContainer(t *testing.T) { "--workers", "2", }, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "STORAGE_URI", Value: "hdfs://modelzoo", @@ -247,10 +253,6 @@ func TestCustomExplainerIsMMS(t *testing.T) { config := InferenceServicesConfig{ Explainers: ExplainersConfig{}, } - defaultResource = v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("1"), - v1.ResourceMemory: resource.MustParse("2Gi"), - } mmsCase := false scenarios := map[string]struct { spec ExplainerSpec @@ -259,9 +261,9 @@ func TestCustomExplainerIsMMS(t *testing.T) { "DefaultResources": { spec: ExplainerSpec{ PodSpec: PodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "STORAGE_URI", Value: "hdfs://modelzoo", @@ -277,7 +279,7 @@ func TestCustomExplainerIsMMS(t *testing.T) { for name, scenario := range scenarios { t.Run(name, func(t *testing.T) { - explainer := CustomExplainer{PodSpec: v1.PodSpec(scenario.spec.PodSpec)} + explainer := CustomExplainer{PodSpec: corev1.PodSpec(scenario.spec.PodSpec)} res := explainer.IsMMS(&config) if !g.Expect(res).To(gomega.Equal(scenario.expected)) { t.Errorf("got %t, want %t", res, scenario.expected) diff --git a/pkg/apis/serving/v1beta1/inference_service_defaults.go b/pkg/apis/serving/v1beta1/inference_service_defaults.go index 2f65c122c1b..403f6d914ad 100644 --- a/pkg/apis/serving/v1beta1/inference_service_defaults.go +++ b/pkg/apis/serving/v1beta1/inference_service_defaults.go @@ -24,7 +24,7 @@ import ( "strings" "google.golang.org/protobuf/proto" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/kubernetes" @@ -33,20 +33,15 @@ import ( logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/webhook" + "k8s.io/client-go/kubernetes/scheme" + "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" "github.com/kserve/kserve/pkg/constants" "github.com/kserve/kserve/pkg/utils" - "k8s.io/client-go/kubernetes/scheme" ) -var ( - defaultResource = v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("1"), - v1.ResourceMemory: resource.MustParse("2Gi"), - } - // logger for the mutating webhook. - mutatorLogger = logf.Log.WithName("inferenceservice-v1beta1-mutating-webhook") -) +// logger for the mutating webhook. +var mutatorLogger = logf.Log.WithName("inferenceservice-v1beta1-mutating-webhook") // +kubebuilder:object:generate=false // +k8s:openapi-gen=false @@ -55,30 +50,48 @@ var ( // // NOTE: The +kubebuilder:object:generate=false marker prevents controller-gen from generating DeepCopy methods, // as it is used only for temporary operations and does not need to be deeply copied. -type InferenceServiceDefaulter struct { -} +type InferenceServiceDefaulter struct{} // +kubebuilder:webhook:path=/mutate-inferenceservices,mutating=true,failurePolicy=fail,groups=serving.kserve.io,resources=inferenceservices,verbs=create;update,versions=v1beta1,name=inferenceservice.kserve-webhook-server.defaulter var _ webhook.CustomDefaulter = &InferenceServiceDefaulter{} -func setResourceRequirementDefaults(requirements *v1.ResourceRequirements) { +func setResourceRequirementDefaults(config *InferenceServicesConfig, requirements *corev1.ResourceRequirements) { + defaultResourceRequests := corev1.ResourceList{} + defaultResourceLimits := corev1.ResourceList{} + + if config != nil { + if config.Resource.CPURequest != "" { + defaultResourceRequests[corev1.ResourceCPU] = resource.MustParse(config.Resource.CPURequest) + } + if config.Resource.MemoryRequest != "" { + defaultResourceRequests[corev1.ResourceMemory] = resource.MustParse(config.Resource.MemoryRequest) + } + if config.Resource.CPULimit != "" { + defaultResourceLimits[corev1.ResourceCPU] = resource.MustParse(config.Resource.CPULimit) + } + if config.Resource.MemoryLimit != "" { + defaultResourceLimits[corev1.ResourceMemory] = resource.MustParse(config.Resource.MemoryLimit) + } + } if requirements.Requests == nil { - requirements.Requests = v1.ResourceList{} + requirements.Requests = corev1.ResourceList{} } - for k, v := range defaultResource { + for k, v := range defaultResourceRequests { if _, ok := requirements.Requests[k]; !ok { requirements.Requests[k] = v } } if requirements.Limits == nil { - requirements.Limits = v1.ResourceList{} + requirements.Limits = corev1.ResourceList{} } - for k, v := range defaultResource { + for k, v := range defaultResourceLimits { if _, ok := requirements.Limits[k]; !ok { requirements.Limits[k] = v } } + + logf.Log.Info("Setting default resource requirements ", "requests", requirements.Requests, "limits", requirements.Limits) } func (d *InferenceServiceDefaulter) Default(ctx context.Context, obj runtime.Object) error { @@ -98,20 +111,24 @@ func (d *InferenceServiceDefaulter) Default(ctx context.Context, obj runtime.Obj mutatorLogger.Error(err, "unable to create clientSet") return err } - // Todo: call api server only once to get all configs - configMap, err := NewInferenceServicesConfig(clientSet) + configMap, err := GetInferenceServiceConfigMap(ctx, clientSet) if err != nil { + mutatorLogger.Error(err, "unable to get configmap", "name", constants.InferenceServiceConfigMapName, "namespace", constants.KServeNamespace) return err } - deployConfig, err := NewDeployConfig(clientSet) + isvcConfig, err := NewInferenceServicesConfig(configMap) if err != nil { return err } - localModelConfig, err := NewLocalModelConfig(clientSet) + deployConfig, err := NewDeployConfig(configMap) if err != nil { return err } - securityConfig, err := NewSecurityConfig(clientSet) + localModelConfig, err := NewLocalModelConfig(configMap) + if err != nil { + return err + } + securityConfig, err := NewSecurityConfig(configMap) if err != nil { return err } @@ -132,7 +149,7 @@ func (d *InferenceServiceDefaulter) Default(ctx context.Context, obj runtime.Obj } // Pass a list of LocalModelCache resources to set the local model label if there is a match - isvc.DefaultInferenceService(configMap, deployConfig, securityConfig, models) + isvc.DefaultInferenceService(isvcConfig, deployConfig, securityConfig, models) return nil } @@ -363,18 +380,18 @@ func (isvc *InferenceService) SetMlServerDefaults() { // set environment variables based on storage uri if isvc.Spec.Predictor.Model.StorageURI == nil && isvc.Spec.Predictor.Model.Storage == nil { isvc.Spec.Predictor.Model.Env = utils.AppendEnvVarIfNotExists(isvc.Spec.Predictor.Model.Env, - v1.EnvVar{ + corev1.EnvVar{ Name: constants.MLServerLoadModelsStartupEnv, Value: strconv.FormatBool(false), }, ) } else { isvc.Spec.Predictor.Model.Env = utils.AppendEnvVarIfNotExists(isvc.Spec.Predictor.Model.Env, - v1.EnvVar{ + corev1.EnvVar{ Name: constants.MLServerModelNameEnv, Value: isvc.Name, }, - v1.EnvVar{ + corev1.EnvVar{ Name: constants.MLServerModelURIEnv, Value: constants.DefaultModelLocalMountPath, }, @@ -415,7 +432,7 @@ func (isvc *InferenceService) SetTorchServeDefaults() { // set torchserve env variable "PROTOCOL_VERSION" based on ProtocolVersion isvc.Spec.Predictor.Model.Env = append(isvc.Spec.Predictor.Model.Env, - v1.EnvVar{ + corev1.EnvVar{ Name: constants.ProtocolVersionENV, Value: string(*isvc.Spec.Predictor.Model.ProtocolVersion), }) @@ -468,7 +485,11 @@ func (isvc *InferenceService) setLocalModelLabel(models *v1alpha1.LocalModelCach } isvc.Labels[constants.LocalModelLabel] = localModel.Name isvc.Annotations[constants.LocalModelSourceUriAnnotationKey] = localModel.Spec.SourceModelUri - // TODO: node group needs to be retrieved from isvc node group annotation when we support multiple node groups - isvc.Annotations[constants.LocalModelPVCNameAnnotationKey] = localModel.Name + "-" + localModel.Spec.NodeGroups[0] + // Get node group from annotation when possible, otherwise fallback to use the first node group from localmodelcache + if nodeGroup, ok := isvc.Annotations[constants.NodeGroupAnnotationKey]; ok { + isvc.Annotations[constants.LocalModelPVCNameAnnotationKey] = localModel.Name + "-" + nodeGroup + } else { + isvc.Annotations[constants.LocalModelPVCNameAnnotationKey] = localModel.Name + "-" + localModel.Spec.NodeGroups[0] + } mutatorLogger.Info("LocalModelCache found", "model", localModel.Name, "namespace", isvc.Namespace, "isvc", isvc.Name) } diff --git a/pkg/apis/serving/v1beta1/inference_service_defaults_test.go b/pkg/apis/serving/v1beta1/inference_service_defaults_test.go index cc0e7ee2b74..7ba03387c93 100644 --- a/pkg/apis/serving/v1beta1/inference_service_defaults_test.go +++ b/pkg/apis/serving/v1beta1/inference_service_defaults_test.go @@ -20,19 +20,24 @@ import ( "strconv" "testing" + "google.golang.org/protobuf/proto" + "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" "github.com/kserve/kserve/pkg/constants" - "google.golang.org/protobuf/proto" "github.com/onsi/gomega" "github.com/onsi/gomega/types" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) func TestInferenceServiceDefaults(t *testing.T) { g := gomega.NewGomegaWithT(t) + defaultResource := corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("2Gi"), + } scenarios := map[string]struct { config *InferenceServicesConfig deployConfig *DeployConfig @@ -48,6 +53,12 @@ func TestInferenceServiceDefaults(t *testing.T) { DefaultImageVersion: "v0.4.0", }, }, + Resource: ResourceConfig{ + CPULimit: "1", + MemoryLimit: "2Gi", + CPURequest: "1", + MemoryRequest: "2Gi", + }, }, deployConfig: &DeployConfig{ DefaultDeploymentMode: "Serverless", @@ -67,9 +78,9 @@ func TestInferenceServiceDefaults(t *testing.T) { }, Transformer: &TransformerSpec{ PodSpec: PodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "STORAGE_URI", Value: "s3://transformer", @@ -100,6 +111,12 @@ func TestInferenceServiceDefaults(t *testing.T) { DefaultImageVersion: "v0.4.0", }, }, + Resource: ResourceConfig{ + CPULimit: "1", + MemoryLimit: "2Gi", + CPURequest: "1", + MemoryRequest: "2Gi", + }, }, deployConfig: &DeployConfig{ DefaultDeploymentMode: string(constants.RawDeployment), @@ -119,9 +136,9 @@ func TestInferenceServiceDefaults(t *testing.T) { }, Transformer: &TransformerSpec{ PodSpec: PodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "STORAGE_URI", Value: "s3://transformer", @@ -152,6 +169,12 @@ func TestInferenceServiceDefaults(t *testing.T) { DefaultImageVersion: "v0.4.0", }, }, + Resource: ResourceConfig{ + CPULimit: "1", + MemoryLimit: "2Gi", + CPURequest: "1", + MemoryRequest: "2Gi", + }, }, deployConfig: &DeployConfig{ DefaultDeploymentMode: "Serverless", @@ -171,9 +194,9 @@ func TestInferenceServiceDefaults(t *testing.T) { }, Transformer: &TransformerSpec{ PodSpec: PodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "STORAGE_URI", Value: "s3://transformer", @@ -204,6 +227,12 @@ func TestInferenceServiceDefaults(t *testing.T) { DefaultImageVersion: "v0.4.0", }, }, + Resource: ResourceConfig{ + CPULimit: "1", + MemoryLimit: "2Gi", + CPURequest: "1", + MemoryRequest: "2Gi", + }, }, deployConfig: &DeployConfig{ DefaultDeploymentMode: "Serverless", @@ -223,9 +252,9 @@ func TestInferenceServiceDefaults(t *testing.T) { }, Transformer: &TransformerSpec{ PodSpec: PodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "STORAGE_URI", Value: "s3://transformer", @@ -256,6 +285,12 @@ func TestInferenceServiceDefaults(t *testing.T) { DefaultImageVersion: "v0.4.0", }, }, + Resource: ResourceConfig{ + CPULimit: "1", + MemoryLimit: "2Gi", + CPURequest: "1", + MemoryRequest: "2Gi", + }, }, deployConfig: &DeployConfig{ DefaultDeploymentMode: "Serverless", @@ -275,9 +310,9 @@ func TestInferenceServiceDefaults(t *testing.T) { }, Transformer: &TransformerSpec{ PodSpec: PodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "STORAGE_URI", Value: "s3://transformer", @@ -303,7 +338,7 @@ func TestInferenceServiceDefaults(t *testing.T) { } for _, scenario := range scenarios { - resources := v1.ResourceRequirements{Requests: defaultResource, Limits: defaultResource} + resources := corev1.ResourceRequirements{Requests: defaultResource, Limits: defaultResource} scenario.isvc.Spec.DeepCopy() scenario.isvc.DefaultInferenceService(scenario.config, scenario.deployConfig, nil, nil) @@ -319,8 +354,63 @@ func TestInferenceServiceDefaults(t *testing.T) { } } +func TestCustomPredictorDefaultsConfig(t *testing.T) { + expectedResource := corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("2"), + corev1.ResourceMemory: resource.MustParse("4Gi"), + } + g := gomega.NewGomegaWithT(t) + config := &InferenceServicesConfig{ + Explainers: ExplainersConfig{ + ARTExplainer: ExplainerConfig{ + ContainerImage: "art", + DefaultImageVersion: "v0.4.0", + }, + }, + Resource: ResourceConfig{ + CPULimit: "2", + MemoryLimit: "4Gi", + CPURequest: "2", + MemoryRequest: "4Gi", + }, + } + deployConfig := &DeployConfig{ + DefaultDeploymentMode: "Serverless", + } + isvc := InferenceService{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "default", + }, + Spec: InferenceServiceSpec{ + Predictor: PredictorSpec{ + PodSpec: PodSpec{ + Containers: []corev1.Container{ + { + Env: []corev1.EnvVar{ + { + Name: "STORAGE_URI", + Value: "s3://transformer", + }, + }, + }, + }, + }, + }, + }, + } + resources := corev1.ResourceRequirements{Requests: expectedResource, Limits: expectedResource} + isvc.Spec.DeepCopy() + isvc.DefaultInferenceService(config, deployConfig, nil, nil) + g.Expect(isvc.Spec.Predictor.PodSpec.Containers[0].Resources).To(gomega.Equal(resources)) +} + func TestCustomPredictorDefaults(t *testing.T) { g := gomega.NewGomegaWithT(t) + defaultResource := corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("2Gi"), + } config := &InferenceServicesConfig{ Explainers: ExplainersConfig{ ARTExplainer: ExplainerConfig{ @@ -328,6 +418,12 @@ func TestCustomPredictorDefaults(t *testing.T) { DefaultImageVersion: "v0.4.0", }, }, + Resource: ResourceConfig{ + CPULimit: "1", + MemoryLimit: "2Gi", + CPURequest: "1", + MemoryRequest: "2Gi", + }, } deployConfig := &DeployConfig{ DefaultDeploymentMode: "Serverless", @@ -340,9 +436,9 @@ func TestCustomPredictorDefaults(t *testing.T) { Spec: InferenceServiceSpec{ Predictor: PredictorSpec{ PodSpec: PodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "STORAGE_URI", Value: "s3://transformer", @@ -354,7 +450,7 @@ func TestCustomPredictorDefaults(t *testing.T) { }, }, } - resources := v1.ResourceRequirements{Requests: defaultResource, Limits: defaultResource} + resources := corev1.ResourceRequirements{Requests: defaultResource, Limits: defaultResource} isvc.Spec.DeepCopy() isvc.DefaultInferenceService(config, deployConfig, nil, nil) g.Expect(isvc.Spec.Predictor.PodSpec.Containers[0].Resources).To(gomega.Equal(resources)) @@ -469,7 +565,6 @@ func TestRuntimeDefaults(t *testing.T) { scenario.isvc.SetRuntimeDefaults() g.Expect(scenario.isvc.Spec.Predictor.Model).ToNot(gomega.BeNil()) switch name { - case "PyTorch": g.Expect(scenario.isvc.Spec.Predictor.PyTorch).To(gomega.BeNil()) @@ -635,7 +730,7 @@ func TestMlServerDefaults(t *testing.T) { }, }, matcher: map[string]types.GomegaMatcher{ - "env": gomega.ContainElement(v1.EnvVar{ + "env": gomega.ContainElement(corev1.EnvVar{ Name: constants.MLServerLoadModelsStartupEnv, Value: strconv.FormatBool(false), }), @@ -662,11 +757,11 @@ func TestMlServerDefaults(t *testing.T) { }, matcher: map[string]types.GomegaMatcher{ "env": gomega.ContainElements( - v1.EnvVar{ + corev1.EnvVar{ Name: constants.MLServerModelNameEnv, Value: "foo", }, - v1.EnvVar{ + corev1.EnvVar{ Name: constants.MLServerModelURIEnv, Value: constants.DefaultModelLocalMountPath, }), @@ -693,11 +788,11 @@ func TestMlServerDefaults(t *testing.T) { }, matcher: map[string]types.GomegaMatcher{ "env": gomega.ContainElements( - v1.EnvVar{ + corev1.EnvVar{ Name: constants.MLServerModelNameEnv, Value: "foo", }, - v1.EnvVar{ + corev1.EnvVar{ Name: constants.MLServerModelURIEnv, Value: constants.DefaultModelLocalMountPath, }), @@ -727,11 +822,11 @@ func TestMlServerDefaults(t *testing.T) { }, matcher: map[string]types.GomegaMatcher{ "env": gomega.ContainElements( - v1.EnvVar{ + corev1.EnvVar{ Name: constants.MLServerModelNameEnv, Value: "foo", }, - v1.EnvVar{ + corev1.EnvVar{ Name: constants.MLServerModelURIEnv, Value: constants.DefaultModelLocalMountPath, }), @@ -760,17 +855,43 @@ func TestLocalModelAnnotation(t *testing.T) { } protocolVersion := constants.ProtocolV2 localModelName := "iris" + gpu1, gpu2 := "gpu1", "gpu2" scenarios := map[string]struct { - config *InferenceServicesConfig - isvc InferenceService - matcher types.GomegaMatcher + config *InferenceServicesConfig + isvc InferenceService + labelMatcher types.GomegaMatcher + annotationMatcher types.GomegaMatcher }{ - "isvc without LocalModelCache": { + "isvc without node group annotation with LocalModelCache": { + config: &InferenceServicesConfig{}, + isvc: InferenceService{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "default", + }, + Spec: InferenceServiceSpec{ + Predictor: PredictorSpec{ + PyTorch: &TorchServeSpec{ + PredictorExtensionSpec: PredictorExtensionSpec{ + StorageURI: proto.String("gs://testbucket/testmodel"), + ProtocolVersion: &protocolVersion, + }, + }, + }, + }, + }, + labelMatcher: gomega.HaveKeyWithValue(constants.LocalModelLabel, localModelName), + annotationMatcher: gomega.HaveKeyWithValue(constants.LocalModelPVCNameAnnotationKey, localModelName+"-"+gpu1), + }, + "isvc with node group annotation with LocalModelCache": { config: &InferenceServicesConfig{}, isvc: InferenceService{ ObjectMeta: metav1.ObjectMeta{ Name: "foo", Namespace: "default", + Annotations: map[string]string{ + constants.NodeGroupAnnotationKey: gpu2, // should append this to PVC name + }, }, Spec: InferenceServiceSpec{ Predictor: PredictorSpec{ @@ -783,9 +904,10 @@ func TestLocalModelAnnotation(t *testing.T) { }, }, }, - matcher: gomega.HaveKeyWithValue(constants.LocalModelLabel, localModelName), + labelMatcher: gomega.HaveKeyWithValue(constants.LocalModelLabel, localModelName), + annotationMatcher: gomega.HaveKeyWithValue(constants.LocalModelPVCNameAnnotationKey, localModelName+"-"+gpu2), }, - "isvc with LocalModelCache": { + "isvc without LocalModelCache": { config: &InferenceServicesConfig{}, isvc: InferenceService{ ObjectMeta: metav1.ObjectMeta{ @@ -805,7 +927,8 @@ func TestLocalModelAnnotation(t *testing.T) { }, }, }, - matcher: gomega.Not(gomega.HaveKeyWithValue(constants.LocalModelLabel, localModelName)), + labelMatcher: gomega.Not(gomega.HaveKeyWithValue(constants.LocalModelLabel, localModelName)), + annotationMatcher: gomega.Not(gomega.HaveKey(constants.LocalModelPVCNameAnnotationKey)), }, } localModel := &v1alpha1.LocalModelCache{ @@ -815,12 +938,13 @@ func TestLocalModelAnnotation(t *testing.T) { Spec: v1alpha1.LocalModelCacheSpec{ SourceModelUri: "gs://testbucket/testmodel", ModelSize: resource.MustParse("123Gi"), - NodeGroups: []string{"gpu"}, + NodeGroups: []string{gpu1, gpu2}, }, } localModels := &v1alpha1.LocalModelCacheList{Items: []v1alpha1.LocalModelCache{*localModel}} for _, scenario := range scenarios { scenario.isvc.DefaultInferenceService(scenario.config, deployConfig, nil, localModels) - g.Expect(scenario.isvc.ObjectMeta.Labels).To(scenario.matcher) + g.Expect(scenario.isvc.ObjectMeta.Labels).To(scenario.labelMatcher) + g.Expect(scenario.isvc.ObjectMeta.Annotations).To(scenario.annotationMatcher) } } diff --git a/pkg/apis/serving/v1beta1/inference_service_status.go b/pkg/apis/serving/v1beta1/inference_service_status.go index 0373c0e3d2f..c547541ee8e 100644 --- a/pkg/apis/serving/v1beta1/inference_service_status.go +++ b/pkg/apis/serving/v1beta1/inference_service_status.go @@ -20,13 +20,14 @@ import ( "reflect" "strings" - "github.com/kserve/kserve/pkg/constants" appsv1 "k8s.io/api/apps/v1" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "knative.dev/pkg/apis" duckv1 "knative.dev/pkg/apis/duck/v1" knservingv1 "knative.dev/serving/pkg/apis/serving/v1" + + "github.com/kserve/kserve/pkg/constants" ) // InferenceServiceStatus defines the observed state of InferenceService @@ -283,26 +284,27 @@ func (ss *InferenceServiceStatus) GetCondition(t apis.ConditionType) *apis.Condi // IsConditionReady returns the readiness for a given condition func (ss *InferenceServiceStatus) IsConditionReady(t apis.ConditionType) bool { condition := conditionSet.Manage(ss).GetCondition(t) - return condition != nil && condition.Status == v1.ConditionTrue + return condition != nil && condition.Status == corev1.ConditionTrue } // IsConditionFalse returns if a given condition is False func (ss *InferenceServiceStatus) IsConditionFalse(t apis.ConditionType) bool { condition := conditionSet.Manage(ss).GetCondition(t) - return condition != nil && condition.Status == v1.ConditionFalse + return condition != nil && condition.Status == corev1.ConditionFalse } // IsConditionUnknown returns if a given condition is Unknown func (ss *InferenceServiceStatus) IsConditionUnknown(t apis.ConditionType) bool { condition := conditionSet.Manage(ss).GetCondition(t) - return condition == nil || condition.Status == v1.ConditionUnknown + return condition == nil || condition.Status == corev1.ConditionUnknown } func (ss *InferenceServiceStatus) PropagateRawStatusWithMessages( component ComponentType, reason string, msg string, - targetStatus v1.ConditionStatus) { + targetStatus corev1.ConditionStatus, +) { if len(ss.Components) == 0 { ss.Components = make(map[ComponentType]ComponentStatusSpec) } @@ -326,7 +328,8 @@ func (ss *InferenceServiceStatus) PropagateRawStatusWithMessages( func (ss *InferenceServiceStatus) PropagateRawStatus( component ComponentType, deploymentList []*appsv1.Deployment, - url *apis.URL) { + url *apis.URL, +) { if len(ss.Components) == 0 { ss.Components = make(map[ComponentType]ComponentStatusSpec) } @@ -336,7 +339,7 @@ func (ss *InferenceServiceStatus) PropagateRawStatus( } condition := getDeploymentCondition(deploymentList, appsv1.DeploymentAvailable) - if condition != nil && condition.Status == v1.ConditionTrue { + if condition != nil && condition.Status == corev1.ConditionTrue { statusSpec.URL = url } readyCondition := readyConditionsMap[component] @@ -348,7 +351,7 @@ func (ss *InferenceServiceStatus) PropagateRawStatus( func getDeploymentCondition(deploymentList []*appsv1.Deployment, conditionType appsv1.DeploymentConditionType) *apis.Condition { condition := apis.Condition{} var messages, reasons []string - var statuses []v1.ConditionStatus + var statuses []corev1.ConditionStatus var lastTransitionTime []apis.VolatileTime // Multi Node case if len(deploymentList) > 1 { @@ -396,14 +399,14 @@ func getDeploymentCondition(deploymentList []*appsv1.Deployment, conditionType a } // allStatusesTrue check all status are true or not -func allStatusesTrue(statuses []v1.ConditionStatus) v1.ConditionStatus { +func allStatusesTrue(statuses []corev1.ConditionStatus) corev1.ConditionStatus { for _, status := range statuses { - if status != v1.ConditionTrue { - return v1.ConditionFalse + if status != corev1.ConditionTrue { + return corev1.ConditionFalse } } - return v1.ConditionTrue + return corev1.ConditionTrue } // PropagateCrossComponentStatus aggregates the RoutesReady or ConfigurationsReady condition across all available components @@ -415,13 +418,13 @@ func (ss *InferenceServiceStatus) PropagateCrossComponentStatus(componentList [] } crossComponentCondition := &apis.Condition{ Type: conditionType, - Status: v1.ConditionTrue, + Status: corev1.ConditionTrue, } for _, component := range componentList { if !ss.IsConditionReady(conditionsMap[component]) { - crossComponentCondition.Status = v1.ConditionFalse + crossComponentCondition.Status = corev1.ConditionFalse if ss.IsConditionUnknown(conditionsMap[component]) { // include check for nil condition - crossComponentCondition.Status = v1.ConditionUnknown + crossComponentCondition.Status = corev1.ConditionUnknown } crossComponentCondition.Reason = string(conditionsMap[component]) + " not ready" } @@ -475,7 +478,7 @@ func (ss *InferenceServiceStatus) PropagateStatus(component ComponentType, servi } // propagate overall ready condition for each component readyCondition := serviceStatus.GetCondition(knservingv1.ServiceConditionReady) - if readyCondition != nil && readyCondition.Status == v1.ConditionTrue { + if readyCondition != nil && readyCondition.Status == corev1.ConditionTrue { if serviceStatus.Address != nil { statusSpec.Address = serviceStatus.Address } @@ -503,11 +506,11 @@ func (ss *InferenceServiceStatus) PropagateStatus(component ComponentType, servi func (ss *InferenceServiceStatus) SetCondition(conditionType apis.ConditionType, condition *apis.Condition) { switch { case condition == nil: - case condition.Status == v1.ConditionUnknown: + case condition.Status == corev1.ConditionUnknown: conditionSet.Manage(ss).MarkUnknown(conditionType, condition.Reason, condition.Message) - case condition.Status == v1.ConditionTrue: + case condition.Status == corev1.ConditionTrue: conditionSet.Manage(ss).MarkTrue(conditionType) - case condition.Status == v1.ConditionFalse: + case condition.Status == corev1.ConditionFalse: conditionSet.Manage(ss).MarkFalse(conditionType, condition.Reason, condition.Message) } } @@ -565,7 +568,7 @@ func (ss *InferenceServiceStatus) SetModelFailureInfo(info *FailureInfo) bool { return true } -func (ss *InferenceServiceStatus) PropagateModelStatus(statusSpec ComponentStatusSpec, podList *v1.PodList, rawDeployment bool, serviceStatus *knservingv1.ServiceStatus) bool { +func (ss *InferenceServiceStatus) PropagateModelStatus(statusSpec ComponentStatusSpec, podList *corev1.PodList, rawDeployment bool, serviceStatus *knservingv1.ServiceStatus) bool { // Check at least one pod is running for the latest revision of inferenceservice totalCopies := len(podList.Items) if totalCopies == 0 { diff --git a/pkg/apis/serving/v1beta1/inference_service_status_test.go b/pkg/apis/serving/v1beta1/inference_service_status_test.go index 1306cbc174d..943fcbfa353 100644 --- a/pkg/apis/serving/v1beta1/inference_service_status_test.go +++ b/pkg/apis/serving/v1beta1/inference_service_status_test.go @@ -17,21 +17,22 @@ limitations under the License. package v1beta1 import ( - "github.com/kserve/kserve/pkg/constants" - "github.com/onsi/gomega" "net/url" "testing" "time" + "github.com/onsi/gomega" "google.golang.org/protobuf/proto" appsv1 "k8s.io/api/apps/v1" - "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "knative.dev/pkg/apis" "knative.dev/pkg/apis/duck" duckv1 "knative.dev/pkg/apis/duck/v1" duckv1beta1 "knative.dev/pkg/apis/duck/v1beta1" knservingv1 "knative.dev/serving/pkg/apis/serving/v1" + + "github.com/kserve/kserve/pkg/constants" ) func TestInferenceServiceDuckType(t *testing.T) { @@ -69,7 +70,7 @@ func TestInferenceServiceIsReady(t *testing.T) { Status: duckv1.Status{ Conditions: duckv1.Conditions{{ Type: "Foo", - Status: v1.ConditionTrue, + Status: corev1.ConditionTrue, }}, }, }, @@ -80,7 +81,7 @@ func TestInferenceServiceIsReady(t *testing.T) { Status: duckv1.Status{ Conditions: duckv1.Conditions{{ Type: knservingv1.ServiceConditionReady, - Status: v1.ConditionFalse, + Status: corev1.ConditionFalse, }}, }, }, @@ -91,7 +92,7 @@ func TestInferenceServiceIsReady(t *testing.T) { Status: duckv1.Status{ Conditions: duckv1.Conditions{{ Type: knservingv1.ServiceConditionReady, - Status: v1.ConditionUnknown, + Status: corev1.ConditionUnknown, }}, }, }, @@ -113,15 +114,15 @@ func TestInferenceServiceIsReady(t *testing.T) { Conditions: duckv1.Conditions{ { Type: "ConfigurationsReady", - Status: v1.ConditionTrue, + Status: corev1.ConditionTrue, }, { Type: "RoutesReady", - Status: v1.ConditionTrue, + Status: corev1.ConditionTrue, }, { Type: knservingv1.ServiceConditionReady, - Status: v1.ConditionTrue, + Status: corev1.ConditionTrue, }, }, }, @@ -134,19 +135,19 @@ func TestInferenceServiceIsReady(t *testing.T) { Conditions: duckv1.Conditions{ { Type: "Foo", - Status: v1.ConditionTrue, + Status: corev1.ConditionTrue, }, { Type: "RoutesReady", - Status: v1.ConditionTrue, + Status: corev1.ConditionTrue, }, { Type: "ConfigurationsReady", - Status: v1.ConditionTrue, + Status: corev1.ConditionTrue, }, { Type: knservingv1.ServiceConditionReady, - Status: v1.ConditionTrue, + Status: corev1.ConditionTrue, }, }, }, @@ -159,11 +160,11 @@ func TestInferenceServiceIsReady(t *testing.T) { Conditions: duckv1.Conditions{ { Type: "Foo", - Status: v1.ConditionTrue, + Status: corev1.ConditionTrue, }, { Type: knservingv1.ConfigurationConditionReady, - Status: v1.ConditionFalse, + Status: corev1.ConditionFalse, }, }, }, @@ -195,7 +196,7 @@ func TestPropagateRawStatus(t *testing.T) { Conditions: []appsv1.DeploymentCondition{ { Type: appsv1.DeploymentAvailable, - Status: v1.ConditionTrue, + Status: corev1.ConditionTrue, Reason: "MinimumReplicasAvailable", Message: "Deployment has minimum availability.", LastTransitionTime: metav1.Time{ @@ -225,7 +226,7 @@ func TestPropagateRawStatusWithMessages(t *testing.T) { errorMsg := "test message" reason := "test reason" - targetStatus := v1.ConditionFalse + targetStatus := corev1.ConditionFalse status := &InferenceServiceStatus{ Status: duckv1.Status{}, @@ -255,19 +256,19 @@ func TestPropagateStatus(t *testing.T) { Conditions: duckv1.Conditions{ { Type: "Foo", - Status: v1.ConditionTrue, + Status: corev1.ConditionTrue, }, { Type: "RoutesReady", - Status: v1.ConditionTrue, + Status: corev1.ConditionTrue, }, { Type: "ConfigurationsReady", - Status: v1.ConditionTrue, + Status: corev1.ConditionTrue, }, { Type: knservingv1.ServiceConditionReady, - Status: v1.ConditionTrue, + Status: corev1.ConditionTrue, }, }, }, @@ -296,19 +297,19 @@ func TestPropagateStatus(t *testing.T) { Conditions: duckv1.Conditions{ { Type: "Foo", - Status: v1.ConditionTrue, + Status: corev1.ConditionTrue, }, { Type: "RoutesReady", - Status: v1.ConditionTrue, + Status: corev1.ConditionTrue, }, { Type: "ConfigurationsReady", - Status: v1.ConditionTrue, + Status: corev1.ConditionTrue, }, { Type: knservingv1.ServiceConditionReady, - Status: v1.ConditionTrue, + Status: corev1.ConditionTrue, }, }, }, @@ -358,7 +359,7 @@ func TestInferenceServiceStatus_PropagateModelStatus(t *testing.T) { scenarios := map[string]struct { isvcStatus *InferenceServiceStatus statusSpec ComponentStatusSpec - podList *v1.PodList + podList *corev1.PodList rawDeployment bool serviceStatus *knservingv1.ServiceStatus expectedRevisionStates *ModelRevisionStates @@ -372,7 +373,7 @@ func TestInferenceServiceStatus_PropagateModelStatus(t *testing.T) { Conditions: duckv1.Conditions{ { Type: "Ready", - Status: v1.ConditionFalse, + Status: corev1.ConditionFalse, }, }, }, @@ -396,10 +397,10 @@ func TestInferenceServiceStatus_PropagateModelStatus(t *testing.T) { GrpcURL: nil, Address: nil, }, - podList: &v1.PodList{ + podList: &corev1.PodList{ TypeMeta: metav1.TypeMeta{}, ListMeta: metav1.ListMeta{}, - Items: []v1.Pod{}, + Items: []corev1.Pod{}, }, rawDeployment: false, serviceStatus: &knservingv1.ServiceStatus{ @@ -426,7 +427,7 @@ func TestInferenceServiceStatus_PropagateModelStatus(t *testing.T) { Conditions: duckv1.Conditions{ { Type: "Ready", - Status: v1.ConditionFalse, + Status: corev1.ConditionFalse, }, }, }, @@ -450,10 +451,10 @@ func TestInferenceServiceStatus_PropagateModelStatus(t *testing.T) { GrpcURL: nil, Address: nil, }, - podList: &v1.PodList{ // pod list is empty because the revision failed and scaled down to 0 + podList: &corev1.PodList{ // pod list is empty because the revision failed and scaled down to 0 TypeMeta: metav1.TypeMeta{}, ListMeta: metav1.ListMeta{}, - Items: []v1.Pod{}, + Items: []corev1.Pod{}, }, rawDeployment: false, serviceStatus: &knservingv1.ServiceStatus{ @@ -482,7 +483,7 @@ func TestInferenceServiceStatus_PropagateModelStatus(t *testing.T) { Conditions: duckv1.Conditions{ { Type: "Ready", - Status: v1.ConditionFalse, + Status: corev1.ConditionFalse, }, }, }, @@ -506,22 +507,22 @@ func TestInferenceServiceStatus_PropagateModelStatus(t *testing.T) { GrpcURL: nil, Address: nil, }, - podList: &v1.PodList{ + podList: &corev1.PodList{ TypeMeta: metav1.TypeMeta{}, ListMeta: metav1.ListMeta{}, - Items: []v1.Pod{ + Items: []corev1.Pod{ { TypeMeta: metav1.TypeMeta{}, ObjectMeta: metav1.ObjectMeta{ Name: constants.InferenceServiceContainerName, }, - Spec: v1.PodSpec{}, - Status: v1.PodStatus{ - ContainerStatuses: []v1.ContainerStatus{ + Spec: corev1.PodSpec{}, + Status: corev1.PodStatus{ + ContainerStatuses: []corev1.ContainerStatus{ { Name: constants.InferenceServiceContainerName, - State: v1.ContainerState{}, - LastTerminationState: v1.ContainerState{}, + State: corev1.ContainerState{}, + LastTerminationState: corev1.ContainerState{}, Ready: false, RestartCount: 0, Image: "", @@ -559,7 +560,7 @@ func TestInferenceServiceStatus_PropagateModelStatus(t *testing.T) { Conditions: duckv1.Conditions{ { Type: "Ready", - Status: v1.ConditionFalse, + Status: corev1.ConditionFalse, }, }, }, @@ -583,28 +584,28 @@ func TestInferenceServiceStatus_PropagateModelStatus(t *testing.T) { GrpcURL: nil, Address: nil, }, - podList: &v1.PodList{ + podList: &corev1.PodList{ TypeMeta: metav1.TypeMeta{}, ListMeta: metav1.ListMeta{}, - Items: []v1.Pod{ + Items: []corev1.Pod{ { TypeMeta: metav1.TypeMeta{}, ObjectMeta: metav1.ObjectMeta{ Name: constants.InferenceServiceContainerName, }, - Spec: v1.PodSpec{}, - Status: v1.PodStatus{ - ContainerStatuses: []v1.ContainerStatus{ + Spec: corev1.PodSpec{}, + Status: corev1.PodStatus{ + ContainerStatuses: []corev1.ContainerStatus{ { Name: constants.InferenceServiceContainerName, - State: v1.ContainerState{ - Terminated: &v1.ContainerStateTerminated{ + State: corev1.ContainerState{ + Terminated: &corev1.ContainerStateTerminated{ ExitCode: 1, Reason: constants.StateReasonError, Message: "For testing", }, }, - LastTerminationState: v1.ContainerState{}, + LastTerminationState: corev1.ContainerState{}, Ready: false, RestartCount: 0, Image: "", @@ -646,7 +647,7 @@ func TestInferenceServiceStatus_PropagateModelStatus(t *testing.T) { Conditions: duckv1.Conditions{ { Type: "Ready", - Status: v1.ConditionFalse, + Status: corev1.ConditionFalse, }, }, }, @@ -670,28 +671,28 @@ func TestInferenceServiceStatus_PropagateModelStatus(t *testing.T) { GrpcURL: nil, Address: nil, }, - podList: &v1.PodList{ + podList: &corev1.PodList{ TypeMeta: metav1.TypeMeta{}, ListMeta: metav1.ListMeta{}, - Items: []v1.Pod{ + Items: []corev1.Pod{ { TypeMeta: metav1.TypeMeta{}, ObjectMeta: metav1.ObjectMeta{ Name: constants.InferenceServiceContainerName, }, - Spec: v1.PodSpec{}, - Status: v1.PodStatus{ - ContainerStatuses: []v1.ContainerStatus{ + Spec: corev1.PodSpec{}, + Status: corev1.PodStatus{ + ContainerStatuses: []corev1.ContainerStatus{ { Name: constants.InferenceServiceContainerName, - State: v1.ContainerState{ - Waiting: &v1.ContainerStateWaiting{ + State: corev1.ContainerState{ + Waiting: &corev1.ContainerStateWaiting{ Reason: constants.StateReasonCrashLoopBackOff, Message: "For testing", }, }, - LastTerminationState: v1.ContainerState{ - Terminated: &v1.ContainerStateTerminated{ + LastTerminationState: corev1.ContainerState{ + Terminated: &corev1.ContainerStateTerminated{ Reason: constants.StateReasonCrashLoopBackOff, Message: "For testing", ExitCode: 1, @@ -738,7 +739,7 @@ func TestInferenceServiceStatus_PropagateModelStatus(t *testing.T) { Conditions: duckv1.Conditions{ { Type: "Ready", - Status: v1.ConditionFalse, + Status: corev1.ConditionFalse, }, }, }, @@ -762,28 +763,28 @@ func TestInferenceServiceStatus_PropagateModelStatus(t *testing.T) { GrpcURL: nil, Address: nil, }, - podList: &v1.PodList{ + podList: &corev1.PodList{ TypeMeta: metav1.TypeMeta{}, ListMeta: metav1.ListMeta{}, - Items: []v1.Pod{ + Items: []corev1.Pod{ { TypeMeta: metav1.TypeMeta{}, ObjectMeta: metav1.ObjectMeta{ Name: constants.StorageInitializerContainerName, }, - Spec: v1.PodSpec{}, - Status: v1.PodStatus{ - InitContainerStatuses: []v1.ContainerStatus{ + Spec: corev1.PodSpec{}, + Status: corev1.PodStatus{ + InitContainerStatuses: []corev1.ContainerStatus{ { Name: constants.StorageInitializerContainerName, - State: v1.ContainerState{ - Terminated: &v1.ContainerStateTerminated{ + State: corev1.ContainerState{ + Terminated: &corev1.ContainerStateTerminated{ ExitCode: 1, Reason: constants.StateReasonError, Message: "For testing", }, }, - LastTerminationState: v1.ContainerState{}, + LastTerminationState: corev1.ContainerState{}, Ready: false, RestartCount: 0, Image: "", @@ -825,7 +826,7 @@ func TestInferenceServiceStatus_PropagateModelStatus(t *testing.T) { Conditions: duckv1.Conditions{ { Type: "Ready", - Status: v1.ConditionFalse, + Status: corev1.ConditionFalse, }, }, }, @@ -849,28 +850,28 @@ func TestInferenceServiceStatus_PropagateModelStatus(t *testing.T) { GrpcURL: nil, Address: nil, }, - podList: &v1.PodList{ + podList: &corev1.PodList{ TypeMeta: metav1.TypeMeta{}, ListMeta: metav1.ListMeta{}, - Items: []v1.Pod{ + Items: []corev1.Pod{ { TypeMeta: metav1.TypeMeta{}, ObjectMeta: metav1.ObjectMeta{ Name: constants.StorageInitializerContainerName, }, - Spec: v1.PodSpec{}, - Status: v1.PodStatus{ - InitContainerStatuses: []v1.ContainerStatus{ + Spec: corev1.PodSpec{}, + Status: corev1.PodStatus{ + InitContainerStatuses: []corev1.ContainerStatus{ { Name: constants.StorageInitializerContainerName, - State: v1.ContainerState{ - Waiting: &v1.ContainerStateWaiting{ + State: corev1.ContainerState{ + Waiting: &corev1.ContainerStateWaiting{ Reason: constants.StateReasonCrashLoopBackOff, Message: "For testing", }, }, - LastTerminationState: v1.ContainerState{ - Terminated: &v1.ContainerStateTerminated{ + LastTerminationState: corev1.ContainerState{ + Terminated: &corev1.ContainerStateTerminated{ Reason: constants.StateReasonCrashLoopBackOff, Message: "For testing", ExitCode: 1, @@ -917,7 +918,7 @@ func TestInferenceServiceStatus_PropagateModelStatus(t *testing.T) { Conditions: duckv1.Conditions{ { Type: "Ready", - Status: v1.ConditionFalse, + Status: corev1.ConditionFalse, }, }, }, @@ -941,26 +942,26 @@ func TestInferenceServiceStatus_PropagateModelStatus(t *testing.T) { GrpcURL: nil, Address: nil, }, - podList: &v1.PodList{ + podList: &corev1.PodList{ TypeMeta: metav1.TypeMeta{}, ListMeta: metav1.ListMeta{}, - Items: []v1.Pod{ + Items: []corev1.Pod{ { TypeMeta: metav1.TypeMeta{}, ObjectMeta: metav1.ObjectMeta{ Name: constants.StorageInitializerContainerName, }, - Spec: v1.PodSpec{}, - Status: v1.PodStatus{ - InitContainerStatuses: []v1.ContainerStatus{ + Spec: corev1.PodSpec{}, + Status: corev1.PodStatus{ + InitContainerStatuses: []corev1.ContainerStatus{ { Name: constants.StorageInitializerContainerName, - State: v1.ContainerState{ - Running: &v1.ContainerStateRunning{ + State: corev1.ContainerState{ + Running: &corev1.ContainerStateRunning{ StartedAt: metav1.Time{}, }, }, - LastTerminationState: v1.ContainerState{}, + LastTerminationState: corev1.ContainerState{}, Ready: false, RestartCount: 0, Image: "", @@ -998,7 +999,7 @@ func TestInferenceServiceStatus_PropagateModelStatus(t *testing.T) { Conditions: duckv1.Conditions{ { Type: "Ready", - Status: v1.ConditionFalse, + Status: corev1.ConditionFalse, }, }, }, @@ -1022,27 +1023,27 @@ func TestInferenceServiceStatus_PropagateModelStatus(t *testing.T) { GrpcURL: nil, Address: nil, }, - podList: &v1.PodList{ + podList: &corev1.PodList{ TypeMeta: metav1.TypeMeta{}, ListMeta: metav1.ListMeta{}, - Items: []v1.Pod{ + Items: []corev1.Pod{ { TypeMeta: metav1.TypeMeta{}, ObjectMeta: metav1.ObjectMeta{ Name: constants.StorageInitializerContainerName, }, - Spec: v1.PodSpec{}, - Status: v1.PodStatus{ - InitContainerStatuses: []v1.ContainerStatus{ + Spec: corev1.PodSpec{}, + Status: corev1.PodStatus{ + InitContainerStatuses: []corev1.ContainerStatus{ { Name: constants.StorageInitializerContainerName, - State: v1.ContainerState{ - Running: &v1.ContainerStateRunning{ + State: corev1.ContainerState{ + Running: &corev1.ContainerStateRunning{ StartedAt: metav1.Time{}, }, }, - LastTerminationState: v1.ContainerState{ - Terminated: &v1.ContainerStateTerminated{ + LastTerminationState: corev1.ContainerState{ + Terminated: &corev1.ContainerStateTerminated{ Reason: constants.StateReasonCrashLoopBackOff, Message: "For testing", ExitCode: 1, @@ -1084,7 +1085,7 @@ func TestInferenceServiceStatus_PropagateModelStatus(t *testing.T) { Conditions: duckv1.Conditions{ { Type: "Ready", - Status: v1.ConditionTrue, + Status: corev1.ConditionTrue, }, }, }, @@ -1108,22 +1109,22 @@ func TestInferenceServiceStatus_PropagateModelStatus(t *testing.T) { GrpcURL: nil, Address: nil, }, - podList: &v1.PodList{ + podList: &corev1.PodList{ TypeMeta: metav1.TypeMeta{}, ListMeta: metav1.ListMeta{}, - Items: []v1.Pod{ + Items: []corev1.Pod{ { TypeMeta: metav1.TypeMeta{}, ObjectMeta: metav1.ObjectMeta{ Name: constants.InferenceServiceContainerName, }, - Spec: v1.PodSpec{}, - Status: v1.PodStatus{ - ContainerStatuses: []v1.ContainerStatus{ + Spec: corev1.PodSpec{}, + Status: corev1.PodStatus{ + ContainerStatuses: []corev1.ContainerStatus{ { Name: constants.InferenceServiceContainerName, - State: v1.ContainerState{ - Running: &v1.ContainerStateRunning{}, + State: corev1.ContainerState{ + Running: &corev1.ContainerStateRunning{}, }, Ready: true, RestartCount: 0, @@ -1162,7 +1163,7 @@ func TestInferenceServiceStatus_PropagateModelStatus(t *testing.T) { Conditions: duckv1.Conditions{ { Type: "Ready", - Status: v1.ConditionTrue, + Status: corev1.ConditionTrue, }, }, }, @@ -1186,22 +1187,22 @@ func TestInferenceServiceStatus_PropagateModelStatus(t *testing.T) { GrpcURL: nil, Address: nil, }, - podList: &v1.PodList{ + podList: &corev1.PodList{ TypeMeta: metav1.TypeMeta{}, ListMeta: metav1.ListMeta{}, - Items: []v1.Pod{ + Items: []corev1.Pod{ { TypeMeta: metav1.TypeMeta{}, ObjectMeta: metav1.ObjectMeta{ Name: constants.InferenceServiceContainerName, }, - Spec: v1.PodSpec{}, - Status: v1.PodStatus{ - ContainerStatuses: []v1.ContainerStatus{ + Spec: corev1.PodSpec{}, + Status: corev1.PodStatus{ + ContainerStatuses: []corev1.ContainerStatus{ { Name: constants.InferenceServiceContainerName, - State: v1.ContainerState{ - Running: &v1.ContainerStateRunning{}, + State: corev1.ContainerState{ + Running: &corev1.ContainerStateRunning{}, }, Ready: true, RestartCount: 0, @@ -1231,7 +1232,7 @@ func TestInferenceServiceStatus_PropagateModelStatus(t *testing.T) { Conditions: duckv1.Conditions{ { Type: "Ready", - Status: v1.ConditionFalse, + Status: corev1.ConditionFalse, }, }, }, @@ -1255,22 +1256,22 @@ func TestInferenceServiceStatus_PropagateModelStatus(t *testing.T) { GrpcURL: nil, Address: nil, }, - podList: &v1.PodList{ + podList: &corev1.PodList{ TypeMeta: metav1.TypeMeta{}, ListMeta: metav1.ListMeta{}, - Items: []v1.Pod{ + Items: []corev1.Pod{ { TypeMeta: metav1.TypeMeta{}, ObjectMeta: metav1.ObjectMeta{ Name: "test-container", }, - Spec: v1.PodSpec{}, - Status: v1.PodStatus{ - ContainerStatuses: []v1.ContainerStatus{ + Spec: corev1.PodSpec{}, + Status: corev1.PodStatus{ + ContainerStatuses: []corev1.ContainerStatus{ { Name: "test-container", - State: v1.ContainerState{}, - LastTerminationState: v1.ContainerState{}, + State: corev1.ContainerState{}, + LastTerminationState: corev1.ContainerState{}, Ready: false, RestartCount: 0, Image: "", @@ -1305,7 +1306,7 @@ func TestInferenceServiceStatus_PropagateModelStatus(t *testing.T) { Conditions: duckv1.Conditions{ { Type: "Ready", - Status: v1.ConditionFalse, + Status: corev1.ConditionFalse, }, }, }, @@ -1329,22 +1330,22 @@ func TestInferenceServiceStatus_PropagateModelStatus(t *testing.T) { GrpcURL: nil, Address: nil, }, - podList: &v1.PodList{ + podList: &corev1.PodList{ TypeMeta: metav1.TypeMeta{}, ListMeta: metav1.ListMeta{}, - Items: []v1.Pod{ + Items: []corev1.Pod{ { TypeMeta: metav1.TypeMeta{}, ObjectMeta: metav1.ObjectMeta{ Name: "test-container", }, - Spec: v1.PodSpec{}, - Status: v1.PodStatus{ - InitContainerStatuses: []v1.ContainerStatus{ + Spec: corev1.PodSpec{}, + Status: corev1.PodStatus{ + InitContainerStatuses: []corev1.ContainerStatus{ { Name: "test-container", - State: v1.ContainerState{}, - LastTerminationState: v1.ContainerState{}, + State: corev1.ContainerState{}, + LastTerminationState: corev1.ContainerState{}, Ready: false, RestartCount: 0, Image: "", @@ -1402,7 +1403,7 @@ func TestInferenceServiceStatus_UpdateModelRevisionStates(t *testing.T) { Conditions: duckv1.Conditions{ { Type: "Ready", - Status: v1.ConditionFalse, + Status: corev1.ConditionFalse, }, }, }, @@ -1428,7 +1429,7 @@ func TestInferenceServiceStatus_UpdateModelRevisionStates(t *testing.T) { Conditions: duckv1.Conditions{ { Type: "Ready", - Status: v1.ConditionFalse, + Status: corev1.ConditionFalse, }, }, }, @@ -1463,7 +1464,7 @@ func TestInferenceServiceStatus_UpdateModelRevisionStates(t *testing.T) { Conditions: duckv1.Conditions{ { Type: "Ready", - Status: v1.ConditionFalse, + Status: corev1.ConditionFalse, }, }, }, diff --git a/pkg/apis/serving/v1beta1/inference_service_validation.go b/pkg/apis/serving/v1beta1/inference_service_validation.go index f0ec1f02c2e..08197cfb0e7 100644 --- a/pkg/apis/serving/v1beta1/inference_service_validation.go +++ b/pkg/apis/serving/v1beta1/inference_service_validation.go @@ -21,19 +21,19 @@ import ( "errors" "fmt" "reflect" + "regexp" "strconv" "strings" "sigs.k8s.io/controller-runtime/pkg/webhook/admission" - "regexp" - - "github.com/kserve/kserve/pkg/constants" - "github.com/kserve/kserve/pkg/utils" "k8s.io/apimachinery/pkg/runtime" "knative.dev/serving/pkg/apis/autoscaling" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/webhook" + + "github.com/kserve/kserve/pkg/constants" + "github.com/kserve/kserve/pkg/utils" ) // regular expressions for validation of isvc name @@ -95,12 +95,6 @@ func (v *InferenceServiceValidator) ValidateDelete(ctx context.Context, obj runt return nil, nil } -// GetIntReference returns the pointer for the integer input -func GetIntReference(number int) *int { - num := number - return &num -} - func validateInferenceService(isvc *InferenceService) (admission.Warnings, error) { var allWarnings admission.Warnings annotations := isvc.Annotations @@ -268,9 +262,9 @@ func validateAutoscalerTargetUtilizationPercentage(isvc *InferenceService) error if value, ok := annotations[constants.TargetUtilizationPercentage]; ok { t, err := strconv.Atoi(value) if err != nil { - return fmt.Errorf("the target utilization percentage should be a [1-100] integer") + return errors.New("the target utilization percentage should be a [1-100] integer") } else if t < 1 || t > 100 { - return fmt.Errorf("the target utilization percentage should be a [1-100] integer") + return errors.New("the target utilization percentage should be a [1-100] integer") } } @@ -284,7 +278,6 @@ func validateScalingHPACompExtension(compExtSpec *ComponentExtensionSpec) error } err := validateHPAMetrics(metric) - if err != nil { return err } @@ -292,11 +285,11 @@ func validateScalingHPACompExtension(compExtSpec *ComponentExtensionSpec) error if compExtSpec.ScaleTarget != nil { target := *compExtSpec.ScaleTarget if metric == MetricCPU && target < 1 || target > 100 { - return fmt.Errorf("the target utilization percentage should be a [1-100] integer") + return errors.New("the target utilization percentage should be a [1-100] integer") } if metric == MetricMemory && target < 1 { - return fmt.Errorf("the target memory should be greater than 1 MiB") + return errors.New("the target memory should be greater than 1 MiB") } } @@ -314,7 +307,7 @@ func validateKPAMetrics(metric ScaleMetric) error { func validateScalingKPACompExtension(compExtSpec *ComponentExtensionSpec) error { if compExtSpec.DeploymentStrategy != nil { - return fmt.Errorf("customizing deploymentStrategy is only supported for raw deployment mode") + return errors.New("customizing deploymentStrategy is only supported for raw deployment mode") } metric := MetricConcurrency if compExtSpec.ScaleMetric != nil { @@ -322,7 +315,6 @@ func validateScalingKPACompExtension(compExtSpec *ComponentExtensionSpec) error } err := validateKPAMetrics(metric) - if err != nil { return err } @@ -331,7 +323,7 @@ func validateScalingKPACompExtension(compExtSpec *ComponentExtensionSpec) error target := *compExtSpec.ScaleTarget if metric == MetricRPS && target < 1 { - return fmt.Errorf("the target for rps should be greater than 1") + return errors.New("the target for rps should be greater than 1") } } diff --git a/pkg/apis/serving/v1beta1/inference_service_validation_test.go b/pkg/apis/serving/v1beta1/inference_service_validation_test.go index b145393652f..cf59d6c62c0 100644 --- a/pkg/apis/serving/v1beta1/inference_service_validation_test.go +++ b/pkg/apis/serving/v1beta1/inference_service_validation_test.go @@ -27,9 +27,10 @@ import ( "github.com/onsi/gomega" appsv1 "k8s.io/api/apps/v1" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" ) func makeTestRawInferenceService() InferenceService { @@ -198,48 +199,48 @@ func TestBadParallelismValues(t *testing.T) { func TestBadReplicaValues(t *testing.T) { g := gomega.NewGomegaWithT(t) isvc := makeTestInferenceService() - isvc.Spec.Predictor.MinReplicas = GetIntReference(-1) + isvc.Spec.Predictor.MinReplicas = ptr.To(int32(-1)) validator := InferenceServiceValidator{} warnings, err := validator.ValidateCreate(context.Background(), &isvc) g.Expect(err).Should(gomega.MatchError(MinReplicasLowerBoundExceededError)) g.Expect(warnings).Should(gomega.BeEmpty()) - isvc.Spec.Predictor.MinReplicas = GetIntReference(1) + isvc.Spec.Predictor.MinReplicas = ptr.To(int32(1)) isvc.Spec.Predictor.MaxReplicas = -1 warnings, err = validator.ValidateCreate(context.Background(), &isvc) g.Expect(err).Should(gomega.MatchError(MaxReplicasLowerBoundExceededError)) g.Expect(warnings).Should(gomega.BeEmpty()) - isvc.Spec.Predictor.MinReplicas = GetIntReference(2) + isvc.Spec.Predictor.MinReplicas = ptr.To(int32(2)) isvc.Spec.Predictor.MaxReplicas = 1 warnings, err = validator.ValidateCreate(context.Background(), &isvc) g.Expect(err).Should(gomega.MatchError(MinReplicasShouldBeLessThanMaxError)) g.Expect(warnings).Should(gomega.BeEmpty()) // Now test transformer and explainer, so set correct value for predictor - isvc.Spec.Predictor.MinReplicas = GetIntReference(0) + isvc.Spec.Predictor.MinReplicas = ptr.To(int32(0)) isvc.Spec.Predictor.MaxReplicas = 0 isvc.Spec.Transformer = &TransformerSpec{} isvc.Spec.Transformer.PodSpec = PodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Image: "some-image", }, }, } - isvc.Spec.Transformer.MinReplicas = GetIntReference(-1) + isvc.Spec.Transformer.MinReplicas = ptr.To(int32(-1)) warnings, err = validator.ValidateCreate(context.Background(), &isvc) g.Expect(err).Should(gomega.MatchError(MinReplicasLowerBoundExceededError)) g.Expect(warnings).Should(gomega.BeEmpty()) - isvc.Spec.Transformer.MinReplicas = GetIntReference(1) + isvc.Spec.Transformer.MinReplicas = ptr.To(int32(1)) isvc.Spec.Transformer.MaxReplicas = -1 warnings, err = validator.ValidateCreate(context.Background(), &isvc) g.Expect(err).Should(gomega.MatchError(MaxReplicasLowerBoundExceededError)) g.Expect(warnings).Should(gomega.BeEmpty()) - isvc.Spec.Transformer.MinReplicas = GetIntReference(2) + isvc.Spec.Transformer.MinReplicas = ptr.To(int32(2)) isvc.Spec.Transformer.MaxReplicas = 1 warnings, err = validator.ValidateCreate(context.Background(), &isvc) g.Expect(err).Should(gomega.MatchError(MinReplicasShouldBeLessThanMaxError)) @@ -255,18 +256,18 @@ func TestBadReplicaValues(t *testing.T) { }, }, } - isvc.Spec.Explainer.MinReplicas = GetIntReference(-1) + isvc.Spec.Explainer.MinReplicas = ptr.To(int32(-1)) warnings, err = validator.ValidateCreate(context.Background(), &isvc) g.Expect(err).Should(gomega.MatchError(MinReplicasLowerBoundExceededError)) g.Expect(warnings).Should(gomega.BeEmpty()) - isvc.Spec.Explainer.MinReplicas = GetIntReference(1) + isvc.Spec.Explainer.MinReplicas = ptr.To(int32(1)) isvc.Spec.Explainer.MaxReplicas = -1 warnings, err = validator.ValidateCreate(context.Background(), &isvc) g.Expect(err).Should(gomega.MatchError(MaxReplicasLowerBoundExceededError)) g.Expect(warnings).Should(gomega.BeEmpty()) - isvc.Spec.Explainer.MinReplicas = GetIntReference(2) + isvc.Spec.Explainer.MinReplicas = ptr.To(int32(2)) isvc.Spec.Explainer.MaxReplicas = 1 warnings, err = validator.ValidateCreate(context.Background(), &isvc) g.Expect(err).Should(gomega.MatchError(MinReplicasShouldBeLessThanMaxError)) @@ -278,7 +279,7 @@ func TestCustomOK(t *testing.T) { isvc := makeTestInferenceService() isvc.Spec.Predictor.Tensorflow = nil isvc.Spec.Predictor.PodSpec = PodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Image: "some-image", }, @@ -371,11 +372,11 @@ func TestValidateCollocationStorageURI(t *testing.T) { Spec: InferenceServiceSpec{ Predictor: PredictorSpec{ PodSpec: PodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, Image: "test/predictor:latest", - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: constants.CustomSpecStorageUriEnvVarKey, Value: "gs://test/model", @@ -385,7 +386,7 @@ func TestValidateCollocationStorageURI(t *testing.T) { { Name: constants.TransformerContainerName, Image: "test/transformer:latest", - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: constants.CustomSpecStorageUriEnvVarKey, Value: "gs://test/model", @@ -408,7 +409,7 @@ func TestValidateCollocationStorageURI(t *testing.T) { Spec: InferenceServiceSpec{ Predictor: PredictorSpec{ PodSpec: PodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, Image: "test/predictor:latest", @@ -433,11 +434,11 @@ func TestValidateCollocationStorageURI(t *testing.T) { Spec: InferenceServiceSpec{ Predictor: PredictorSpec{ PodSpec: PodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, Image: "test/predictor:latest", - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: constants.CustomSpecStorageUriEnvVarKey, Value: "gs://test/model", @@ -483,11 +484,11 @@ func TestValidateCollocationStorageURI(t *testing.T) { Spec: InferenceServiceSpec{ Predictor: PredictorSpec{ PodSpec: PodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, Image: "test/predictor:latest", - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: constants.CustomSpecStorageUriEnvVarKey, Value: "gs://test/model", @@ -509,7 +510,6 @@ func TestValidateCollocationStorageURI(t *testing.T) { g.Expect(err).Should(scenario.errMatcher) }) } - } func TestValidateMultiNodeVariables(t *testing.T) { @@ -537,8 +537,8 @@ func TestValidateMultiNodeVariables(t *testing.T) { }, PredictorExtensionSpec: PredictorExtensionSpec{ StorageURI: &pvcStorageUri, - Container: v1.Container{ - Env: []v1.EnvVar{ + Container: corev1.Container{ + Env: []corev1.EnvVar{ {Name: constants.TensorParallelSizeEnvName, Value: "2"}, }, }, @@ -567,8 +567,8 @@ func TestValidateMultiNodeVariables(t *testing.T) { }, PredictorExtensionSpec: PredictorExtensionSpec{ StorageURI: &pvcStorageUri, - Container: v1.Container{ - Env: []v1.EnvVar{ + Container: corev1.Container{ + Env: []corev1.EnvVar{ {Name: constants.PipelineParallelSizeEnvName, Value: "3"}, }, }, @@ -653,12 +653,12 @@ func TestValidateMultiNodeVariables(t *testing.T) { }, PredictorExtensionSpec: PredictorExtensionSpec{ StorageURI: &pvcStorageUri, - Container: v1.Container{ - Resources: v1.ResourceRequirements{ - Limits: v1.ResourceList{ + Container: corev1.Container{ + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ "unknownGPU.com/gpu": resource.MustParse("1"), }, - Requests: v1.ResourceList{ + Requests: corev1.ResourceList{ "unknownGPU.com/gpu": resource.MustParse("1"), }, }, @@ -692,13 +692,13 @@ func TestValidateMultiNodeVariables(t *testing.T) { }, WorkerSpec: &WorkerSpec{ PodSpec: PodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { - Resources: v1.ResourceRequirements{ - Limits: v1.ResourceList{ + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ "unknownGPU.com/gpu": resource.MustParse("1"), }, - Requests: v1.ResourceList{ + Requests: corev1.ResourceList{ "unknownGPU.com/gpu": resource.MustParse("1"), }, }, @@ -782,7 +782,7 @@ func TestValidateMultiNodeVariables(t *testing.T) { }, WorkerSpec: &WorkerSpec{ PodSpec: PodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ {}, {}, }, diff --git a/pkg/apis/serving/v1beta1/podspec.go b/pkg/apis/serving/v1beta1/podspec.go index 5b83e551657..a63c8a046ef 100644 --- a/pkg/apis/serving/v1beta1/podspec.go +++ b/pkg/apis/serving/v1beta1/podspec.go @@ -17,7 +17,7 @@ limitations under the License. package v1beta1 import ( - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" ) // ***This is a copy of kubernetes corev1.PodSpec to make field Containers optional*** @@ -31,7 +31,7 @@ type PodSpec struct { // +optional // +patchMergeKey=name // +patchStrategy=merge,retainKeys - Volumes []v1.Volume `json:"volumes,omitempty" patchStrategy:"merge,retainKeys" patchMergeKey:"name" protobuf:"bytes,1,rep,name=volumes"` + Volumes []corev1.Volume `json:"volumes,omitempty" patchStrategy:"merge,retainKeys" patchMergeKey:"name" protobuf:"bytes,1,rep,name=volumes"` // List of initialization containers belonging to the pod. // Init containers are executed in order prior to containers being started. If any // init container fails, the pod is considered to have failed and is handled according @@ -40,36 +40,35 @@ type PodSpec struct { // Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. // The resourceRequirements of an init container are taken into account during scheduling // by finding the highest request/limit for each resource type, and then using the max of - // of that value or the sum of the normal containers. Limits are applied to init containers + // that value or the sum of the normal containers. Limits are applied to init containers // in a similar fashion. // Init containers cannot currently be added or removed. // Cannot be updated. // More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ // +patchMergeKey=name // +patchStrategy=merge - InitContainers []v1.Container `json:"initContainers,omitempty" patchStrategy:"merge" patchMergeKey:"name" protobuf:"bytes,20,rep,name=initContainers"` + InitContainers []corev1.Container `json:"initContainers,omitempty" patchStrategy:"merge" patchMergeKey:"name" protobuf:"bytes,20,rep,name=initContainers"` // List of containers belonging to the pod. // Containers cannot currently be added or removed. // There must be at least one container in a Pod. // Cannot be updated. // +patchMergeKey=name // +patchStrategy=merge - Containers []v1.Container `json:"containers,omitempty" patchStrategy:"merge" patchMergeKey:"name" protobuf:"bytes,2,rep,name=containers"` + Containers []corev1.Container `json:"containers,omitempty" patchStrategy:"merge" patchMergeKey:"name" protobuf:"bytes,2,rep,name=containers"` // List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing // pod to perform user-initiated actions such as debugging. This list cannot be specified when // creating a pod, and it cannot be modified by updating the pod spec. In order to add an // ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource. - // This field is beta-level and available on clusters that haven't disabled the EphemeralContainers feature gate. // +optional // +patchMergeKey=name // +patchStrategy=merge - EphemeralContainers []v1.EphemeralContainer `json:"ephemeralContainers,omitempty" patchStrategy:"merge" patchMergeKey:"name" protobuf:"bytes,34,rep,name=ephemeralContainers"` + EphemeralContainers []corev1.EphemeralContainer `json:"ephemeralContainers,omitempty" patchStrategy:"merge" patchMergeKey:"name" protobuf:"bytes,34,rep,name=ephemeralContainers"` // Restart policy for all containers within the pod. - // One of Always, OnFailure, Never. + // One of Always, OnFailure, Never. In some contexts, only a subset of those values may be permitted. // Default to Always. // More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy // +optional - RestartPolicy v1.RestartPolicy `json:"restartPolicy,omitempty" protobuf:"bytes,3,opt,name=restartPolicy,casttype=RestartPolicy"` + RestartPolicy corev1.RestartPolicy `json:"restartPolicy,omitempty" protobuf:"bytes,3,opt,name=restartPolicy,casttype=RestartPolicy"` // Optional duration in seconds the pod needs to terminate gracefully. May be decreased in delete request. // Value must be non-negative integer. The value zero indicates stop immediately via // the kill signal (no opportunity to shut down). @@ -92,7 +91,7 @@ type PodSpec struct { // To have DNS options set along with hostNetwork, you have to specify DNS policy // explicitly to 'ClusterFirstWithHostNet'. // +optional - DNSPolicy v1.DNSPolicy `json:"dnsPolicy,omitempty" protobuf:"bytes,6,opt,name=dnsPolicy,casttype=DNSPolicy"` + DNSPolicy corev1.DNSPolicy `json:"dnsPolicy,omitempty" protobuf:"bytes,6,opt,name=dnsPolicy,casttype=DNSPolicy"` // NodeSelector is a selector which must be true for the pod to fit on a node. // Selector which must match a node's labels for the pod to be scheduled on that node. // More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ @@ -104,7 +103,7 @@ type PodSpec struct { // More info: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/ // +optional ServiceAccountName string `json:"serviceAccountName,omitempty" protobuf:"bytes,8,opt,name=serviceAccountName"` - // DeprecatedServiceAccount is a depreciated alias for ServiceAccountName. + // DeprecatedServiceAccount is a deprecated alias for ServiceAccountName. // Deprecated: Use serviceAccountName instead. // +k8s:conversion-gen=false // +optional @@ -113,9 +112,11 @@ type PodSpec struct { // +optional AutomountServiceAccountToken *bool `json:"automountServiceAccountToken,omitempty" protobuf:"varint,21,opt,name=automountServiceAccountToken"` - // NodeName is a request to schedule this pod onto a specific node. If it is non-empty, - // the scheduler simply schedules this pod onto that node, assuming that it fits resource - // requirements. + // NodeName indicates in which node this pod is scheduled. + // If empty, this pod is a candidate for scheduling by the scheduler defined in schedulerName. + // Once this field is set, the kubelet for this node becomes responsible for the lifecycle of this pod. + // This field should not be used to express a desire for the pod to be scheduled on a specific node. + // https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodename // +optional NodeName string `json:"nodeName,omitempty" protobuf:"bytes,10,opt,name=nodeName"` // Host networking requested for this pod. Use the host's network namespace. @@ -145,15 +146,14 @@ type PodSpec struct { // SecurityContext holds pod-level security attributes and common container settings. // Optional: Defaults to empty. See type description for default values of each field. // +optional - SecurityContext *v1.PodSecurityContext `json:"securityContext,omitempty" protobuf:"bytes,14,opt,name=securityContext"` + SecurityContext *corev1.PodSecurityContext `json:"securityContext,omitempty" protobuf:"bytes,14,opt,name=securityContext"` // ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. - // If specified, these secrets will be passed to individual puller implementations for them to use. For example, - // in the case of docker, only DockerConfig type secrets are honored. + // If specified, these secrets will be passed to individual puller implementations for them to use. // More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod // +optional // +patchMergeKey=name // +patchStrategy=merge - ImagePullSecrets []v1.LocalObjectReference `json:"imagePullSecrets,omitempty" patchStrategy:"merge" patchMergeKey:"name" protobuf:"bytes,15,rep,name=imagePullSecrets"` + ImagePullSecrets []corev1.LocalObjectReference `json:"imagePullSecrets,omitempty" patchStrategy:"merge" patchMergeKey:"name" protobuf:"bytes,15,rep,name=imagePullSecrets"` // Specifies the hostname of the Pod // If not specified, the pod's hostname will be set to a system-defined value. // +optional @@ -164,20 +164,20 @@ type PodSpec struct { Subdomain string `json:"subdomain,omitempty" protobuf:"bytes,17,opt,name=subdomain"` // If specified, the pod's scheduling constraints // +optional - Affinity *v1.Affinity `json:"affinity,omitempty" protobuf:"bytes,18,opt,name=affinity"` + Affinity *corev1.Affinity `json:"affinity,omitempty" protobuf:"bytes,18,opt,name=affinity"` // If specified, the pod will be dispatched by specified scheduler. // If not specified, the pod will be dispatched by default scheduler. // +optional SchedulerName string `json:"schedulerName,omitempty" protobuf:"bytes,19,opt,name=schedulerName"` // If specified, the pod's tolerations. // +optional - Tolerations []v1.Toleration `json:"tolerations,omitempty" protobuf:"bytes,22,opt,name=tolerations"` + Tolerations []corev1.Toleration `json:"tolerations,omitempty" protobuf:"bytes,22,opt,name=tolerations"` // HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts - // file if specified. This is only valid for non-hostNetwork pods. + // file if specified. // +optional // +patchMergeKey=ip // +patchStrategy=merge - HostAliases []v1.HostAlias `json:"hostAliases,omitempty" patchStrategy:"merge" patchMergeKey:"ip" protobuf:"bytes,23,rep,name=hostAliases"` + HostAliases []corev1.HostAlias `json:"hostAliases,omitempty" patchStrategy:"merge" patchMergeKey:"ip" protobuf:"bytes,23,rep,name=hostAliases"` // If specified, indicates the pod's priority. "system-node-critical" and // "system-cluster-critical" are two special keywords which indicate the // highest priorities with the former being the highest priority. Any other @@ -197,19 +197,18 @@ type PodSpec struct { // Parameters specified here will be merged to the generated DNS // configuration based on DNSPolicy. // +optional - DNSConfig *v1.PodDNSConfig `json:"dnsConfig,omitempty" protobuf:"bytes,26,opt,name=dnsConfig"` + DNSConfig *corev1.PodDNSConfig `json:"dnsConfig,omitempty" protobuf:"bytes,26,opt,name=dnsConfig"` // If specified, all readiness gates will be evaluated for pod readiness. // A pod is ready when all its containers are ready AND // all conditions specified in the readiness gates have status equal to "True" // More info: https://git.k8s.io/enhancements/keps/sig-network/580-pod-readiness-gates // +optional - ReadinessGates []v1.PodReadinessGate `json:"readinessGates,omitempty" protobuf:"bytes,28,opt,name=readinessGates"` + ReadinessGates []corev1.PodReadinessGate `json:"readinessGates,omitempty" protobuf:"bytes,28,opt,name=readinessGates"` // RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used // to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. // If unset or empty, the "legacy" RuntimeClass will be used, which is an implicit class with an // empty definition that uses the default runtime handler. // More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class - // This is a beta feature as of Kubernetes v1.14. // +optional RuntimeClassName *string `json:"runtimeClassName,omitempty" protobuf:"bytes,29,opt,name=runtimeClassName"` // EnableServiceLinks indicates whether information about services should be injected into pod's @@ -220,9 +219,8 @@ type PodSpec struct { // PreemptionPolicy is the Policy for preempting pods with lower priority. // One of Never, PreemptLowerPriority. // Defaults to PreemptLowerPriority if unset. - // This field is beta-level, gated by the NonPreemptingPriority feature-gate. // +optional - PreemptionPolicy *v1.PreemptionPolicy `json:"preemptionPolicy,omitempty" protobuf:"bytes,31,opt,name=preemptionPolicy"` + PreemptionPolicy *corev1.PreemptionPolicy `json:"preemptionPolicy,omitempty" protobuf:"bytes,31,opt,name=preemptionPolicy"` // Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. // This field will be autopopulated at admission time by the RuntimeClass admission controller. If // the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. @@ -230,9 +228,8 @@ type PodSpec struct { // set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value // defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. // More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md - // This field is beta-level as of Kubernetes v1.18, and is only honored by servers that enable the PodOverhead feature. // +optional - Overhead v1.ResourceList `json:"overhead,omitempty" protobuf:"bytes,32,opt,name=overhead"` + Overhead corev1.ResourceList `json:"overhead,omitempty" protobuf:"bytes,32,opt,name=overhead"` // TopologySpreadConstraints describes how a group of pods ought to spread across topology // domains. Scheduler will schedule pods in a way which abides by the constraints. // All topologySpreadConstraints are ANDed. @@ -242,7 +239,7 @@ type PodSpec struct { // +listType=map // +listMapKey=topologyKey // +listMapKey=whenUnsatisfiable - TopologySpreadConstraints []v1.TopologySpreadConstraint `json:"topologySpreadConstraints,omitempty" patchStrategy:"merge" patchMergeKey:"topologyKey" protobuf:"bytes,33,opt,name=topologySpreadConstraints"` + TopologySpreadConstraints []corev1.TopologySpreadConstraint `json:"topologySpreadConstraints,omitempty" patchStrategy:"merge" patchMergeKey:"topologyKey" protobuf:"bytes,33,opt,name=topologySpreadConstraints"` // If true the pod's hostname will be configured as the pod's FQDN, rather than the leaf name (the default). // In Linux containers, this means setting the FQDN in the hostname field of the kernel (the nodename field of struct utsname). // In Windows containers, this means setting the registry value of hostname for the registry key HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters to FQDN. @@ -259,6 +256,8 @@ type PodSpec struct { // If the OS field is set to windows, following fields must be unset: // - spec.hostPID // - spec.hostIPC + // - spec.hostUsers + // - spec.securityContext.appArmorProfile // - spec.securityContext.seLinuxOptions // - spec.securityContext.seccompProfile // - spec.securityContext.fsGroup @@ -268,6 +267,8 @@ type PodSpec struct { // - spec.securityContext.runAsUser // - spec.securityContext.runAsGroup // - spec.securityContext.supplementalGroups + // - spec.securityContext.supplementalGroupsPolicy + // - spec.containers[*].securityContext.appArmorProfile // - spec.containers[*].securityContext.seLinuxOptions // - spec.containers[*].securityContext.seccompProfile // - spec.containers[*].securityContext.capabilities @@ -278,8 +279,7 @@ type PodSpec struct { // - spec.containers[*].securityContext.runAsUser // - spec.containers[*].securityContext.runAsGroup // +optional - // This is an alpha field and requires the IdentifyPodOS feature - OS *v1.PodOS `json:"os,omitempty" protobuf:"bytes,36,opt,name=os"` + OS *corev1.PodOS `json:"os,omitempty" protobuf:"bytes,36,opt,name=os"` // Use the host's user namespace. // Optional: Default to true. // If set to true or not present, the pod will be run in the host user namespace, useful @@ -292,21 +292,19 @@ type PodSpec struct { // +k8s:conversion-gen=false // +optional HostUsers *bool `json:"hostUsers,omitempty" protobuf:"bytes,37,opt,name=hostUsers"` + // SchedulingGates is an opaque list of values that if specified will block scheduling the pod. // If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the // scheduler will not attempt to schedule the pod. // // SchedulingGates can only be set at pod creation time, and be removed only afterwards. // - // This is a beta feature enabled by the PodSchedulingReadiness feature gate. - // // +patchMergeKey=name // +patchStrategy=merge // +listType=map // +listMapKey=name - // +featureGate=PodSchedulingReadiness // +optional - SchedulingGates []v1.PodSchedulingGate `json:"schedulingGates,omitempty" patchStrategy:"merge" patchMergeKey:"name" protobuf:"bytes,38,opt,name=schedulingGates"` + SchedulingGates []corev1.PodSchedulingGate `json:"schedulingGates,omitempty" patchStrategy:"merge" patchMergeKey:"name" protobuf:"bytes,38,opt,name=schedulingGates"` // ResourceClaims defines which ResourceClaims must be allocated // and reserved before the Pod is allowed to start. The resources // will be made available to those containers which consume them @@ -323,5 +321,19 @@ type PodSpec struct { // +listMapKey=name // +featureGate=DynamicResourceAllocation // +optional - ResourceClaims []v1.PodResourceClaim `json:"resourceClaims,omitempty" patchStrategy:"merge,retainKeys" patchMergeKey:"name" protobuf:"bytes,39,rep,name=resourceClaims"` + ResourceClaims []corev1.PodResourceClaim `json:"resourceClaims,omitempty" patchStrategy:"merge,retainKeys" patchMergeKey:"name" protobuf:"bytes,39,rep,name=resourceClaims"` + // Resources is the total amount of CPU and Memory resources required by all + // containers in the pod. It supports specifying Requests and Limits for + // "cpu" and "memory" resource names only. ResourceClaims are not supported. + // + // This field enables fine-grained control over resource allocation for the + // entire pod, allowing resource sharing among containers in a pod. + // TODO: For beta graduation, expand this comment with a detailed explanation. + // + // This is an alpha field and requires enabling the PodLevelResources feature + // gate. + // + // +featureGate=PodLevelResources + // +optional + Resources *corev1.ResourceRequirements `json:"resources,omitempty" protobuf:"bytes,40,opt,name=resources"` } diff --git a/pkg/apis/serving/v1beta1/predictor.go b/pkg/apis/serving/v1beta1/predictor.go index 749084fe819..8b7828b4be3 100644 --- a/pkg/apis/serving/v1beta1/predictor.go +++ b/pkg/apis/serving/v1beta1/predictor.go @@ -19,15 +19,15 @@ package v1beta1 import ( "reflect" + corev1 "k8s.io/api/core/v1" + "github.com/kserve/kserve/pkg/constants" "github.com/kserve/kserve/pkg/utils" - v1 "k8s.io/api/core/v1" ) // PredictorImplementation defines common functions for all predictors e.g Tensorflow, Triton, etc // +kubebuilder:object:generate=false -type PredictorImplementation interface { -} +type PredictorImplementation interface{} // PredictorSpec defines the configuration for a predictor, // The following fields follow a "1-of" semantic. Users must specify exactly one spec. @@ -99,7 +99,7 @@ type PredictorExtensionSpec struct { // Container enables overrides for the predictor. // Each framework will have different defaults that are populated in the underlying container spec. // +optional - v1.Container `json:",inline"` + corev1.Container `json:",inline"` // Storage Spec for model location // +optional Storage *StorageSpec `json:"storage,omitempty"` diff --git a/pkg/apis/serving/v1beta1/predictor_custom.go b/pkg/apis/serving/v1beta1/predictor_custom.go index c0dff08620e..4e5626aba9d 100644 --- a/pkg/apis/serving/v1beta1/predictor_custom.go +++ b/pkg/apis/serving/v1beta1/predictor_custom.go @@ -20,15 +20,16 @@ import ( "fmt" "strings" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "github.com/kserve/kserve/pkg/constants" "github.com/kserve/kserve/pkg/utils" - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) // CustomPredictor defines arguments for configuring a custom server. type CustomPredictor struct { - v1.PodSpec `json:",inline"` + corev1.PodSpec `json:",inline"` } var ( @@ -37,7 +38,7 @@ var ( ) func NewCustomPredictor(podSpec *PodSpec) *CustomPredictor { - return &CustomPredictor{PodSpec: v1.PodSpec(*podSpec)} + return &CustomPredictor{PodSpec: corev1.PodSpec(*podSpec)} } // Validate returns an error if invalid @@ -63,10 +64,10 @@ func (c *CustomPredictor) validateCustomProtocol() error { // Default sets defaults on the resource func (c *CustomPredictor) Default(config *InferenceServicesConfig) { if len(c.Containers) == 0 { - c.Containers = append(c.Containers, v1.Container{}) + c.Containers = append(c.Containers, corev1.Container{}) } c.Containers[0].Name = constants.InferenceServiceContainerName - setResourceRequirementDefaults(&c.Containers[0].Resources) + setResourceRequirementDefaults(config, &c.Containers[0].Resources) } func (c *CustomPredictor) GetStorageUri() *string { @@ -90,7 +91,8 @@ func (c *CustomPredictor) GetStorageSpec() *StorageSpec { // GetContainer transforms the resource into a container spec func (c *CustomPredictor) GetContainer(metadata metav1.ObjectMeta, extensions *ComponentExtensionSpec, config *InferenceServicesConfig, - predictorHost ...string) *v1.Container { + predictorHost ...string, +) *corev1.Container { for _, container := range c.Containers { if container.Name == constants.InferenceServiceContainerName { return &container diff --git a/pkg/apis/serving/v1beta1/predictor_custom_test.go b/pkg/apis/serving/v1beta1/predictor_custom_test.go index 7a151a3c6c0..c3282de59d2 100644 --- a/pkg/apis/serving/v1beta1/predictor_custom_test.go +++ b/pkg/apis/serving/v1beta1/predictor_custom_test.go @@ -19,13 +19,15 @@ package v1beta1 import ( "testing" - "github.com/kserve/kserve/pkg/constants" "github.com/onsi/gomega" "github.com/onsi/gomega/types" "google.golang.org/protobuf/proto" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" + + "github.com/kserve/kserve/pkg/constants" ) func TestCustomPredictorValidation(t *testing.T) { @@ -38,13 +40,13 @@ func TestCustomPredictorValidation(t *testing.T) { "ValidProtocolV1": { spec: PredictorSpec{ ComponentExtensionSpec: ComponentExtensionSpec{ - MinReplicas: GetIntReference(3), + MinReplicas: ptr.To(int32(3)), ContainerConcurrency: proto.Int64(-1), }, PodSpec: PodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "PROTOCOL", Value: "v1", @@ -59,13 +61,13 @@ func TestCustomPredictorValidation(t *testing.T) { "ValidProtocolV2": { spec: PredictorSpec{ ComponentExtensionSpec: ComponentExtensionSpec{ - MinReplicas: GetIntReference(3), + MinReplicas: ptr.To(int32(3)), ContainerConcurrency: proto.Int64(-1), }, PodSpec: PodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "PROTOCOL", Value: "v2", @@ -80,13 +82,13 @@ func TestCustomPredictorValidation(t *testing.T) { "InvalidValidProtocol": { spec: PredictorSpec{ ComponentExtensionSpec: ComponentExtensionSpec{ - MinReplicas: GetIntReference(3), + MinReplicas: ptr.To(int32(3)), ContainerConcurrency: proto.Int64(-1), }, PodSpec: PodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "PROTOCOL", Value: "unknown", @@ -113,10 +115,19 @@ func TestCustomPredictorValidation(t *testing.T) { func TestCustomPredictorDefaulter(t *testing.T) { g := gomega.NewGomegaWithT(t) - defaultResource = v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("1"), - v1.ResourceMemory: resource.MustParse("2Gi"), + defaultResource := corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("2Gi"), } + config := &InferenceServicesConfig{ + Resource: ResourceConfig{ + CPULimit: "1", + MemoryLimit: "2Gi", + CPURequest: "1", + MemoryRequest: "2Gi", + }, + } + scenarios := map[string]struct { spec PredictorSpec expected PredictorSpec @@ -124,9 +135,9 @@ func TestCustomPredictorDefaulter(t *testing.T) { "DefaultResources": { spec: PredictorSpec{ PodSpec: PodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "STORAGE_URI", Value: "hdfs://modelzoo", @@ -138,16 +149,16 @@ func TestCustomPredictorDefaulter(t *testing.T) { }, expected: PredictorSpec{ PodSpec: PodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "STORAGE_URI", Value: "hdfs://modelzoo", }, }, - Resources: v1.ResourceRequirements{ + Resources: corev1.ResourceRequirements{ Requests: defaultResource, Limits: defaultResource, }, @@ -161,7 +172,7 @@ func TestCustomPredictorDefaulter(t *testing.T) { for name, scenario := range scenarios { t.Run(name, func(t *testing.T) { customPredictor := NewCustomPredictor(&scenario.spec.PodSpec) - customPredictor.Default(nil) + customPredictor.Default(config) if !g.Expect(scenario.spec).To(gomega.Equal(scenario.expected)) { t.Errorf("got %v, want %v", scenario.spec, scenario.expected) } @@ -170,26 +181,25 @@ func TestCustomPredictorDefaulter(t *testing.T) { } func TestCreateCustomPredictorContainer(t *testing.T) { - - var requestedResource = v1.ResourceRequirements{ - Limits: v1.ResourceList{ + requestedResource := corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ "cpu": resource.Quantity{ Format: "100", }, "memory": resource.MustParse("1Gi"), }, - Requests: v1.ResourceList{ + Requests: corev1.ResourceList{ "cpu": resource.Quantity{ Format: "90", }, "memory": resource.MustParse("1Gi"), }, } - var config = InferenceServicesConfig{} + config := InferenceServicesConfig{} g := gomega.NewGomegaWithT(t) scenarios := map[string]struct { isvc InferenceService - expectedContainerSpec *v1.Container + expectedContainerSpec *corev1.Container }{ "ContainerSpecWithCustomImage": { isvc: InferenceService{ @@ -199,7 +209,7 @@ func TestCreateCustomPredictorContainer(t *testing.T) { Spec: InferenceServiceSpec{ Predictor: PredictorSpec{ PodSpec: PodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Image: "custom-predictor:0.1.0", Args: []string{ @@ -208,7 +218,7 @@ func TestCreateCustomPredictorContainer(t *testing.T) { "--http_port", "8080", }, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "STORAGE_URI", Value: "hdfs://modelzoo", @@ -221,7 +231,7 @@ func TestCreateCustomPredictorContainer(t *testing.T) { }, }, }, - expectedContainerSpec: &v1.Container{ + expectedContainerSpec: &corev1.Container{ Image: "custom-predictor:0.1.0", Name: constants.InferenceServiceContainerName, Resources: requestedResource, @@ -231,7 +241,7 @@ func TestCreateCustomPredictorContainer(t *testing.T) { "--http_port", "8080", }, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "STORAGE_URI", Value: "hdfs://modelzoo", @@ -261,9 +271,9 @@ func TestCustomPredictorGetProtocol(t *testing.T) { "Default protocol": { spec: PredictorSpec{ PodSpec: PodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "STORAGE_URI", Value: "s3://modelzoo", @@ -278,9 +288,9 @@ func TestCustomPredictorGetProtocol(t *testing.T) { "protocol v2": { spec: PredictorSpec{ PodSpec: PodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "STORAGE_URI", Value: "s3://modelzoo", @@ -299,11 +309,11 @@ func TestCustomPredictorGetProtocol(t *testing.T) { "Collocation With Protocol Specified": { spec: PredictorSpec{ PodSpec: PodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, Image: "kserve/testImage:1.0", - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "STORAGE_URI", Value: "s3://modelzoo", @@ -313,7 +323,7 @@ func TestCustomPredictorGetProtocol(t *testing.T) { { Name: constants.TransformerContainerName, Image: "kserve/transformer:1.0", - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "STORAGE_URI", Value: "s3://modelzoo", @@ -332,11 +342,11 @@ func TestCustomPredictorGetProtocol(t *testing.T) { "Collocation Default Protocol": { spec: PredictorSpec{ PodSpec: PodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, Image: "kserve/testImage:1.0", - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "STORAGE_URI", Value: "s3://modelzoo", @@ -346,7 +356,7 @@ func TestCustomPredictorGetProtocol(t *testing.T) { { Name: constants.TransformerContainerName, Image: "kserve/transformer:1.0", - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "STORAGE_URI", Value: "s3://modelzoo", diff --git a/pkg/apis/serving/v1beta1/predictor_huggingfaceruntime.go b/pkg/apis/serving/v1beta1/predictor_huggingfaceruntime.go index bc95fbed395..0fe68d73d90 100644 --- a/pkg/apis/serving/v1beta1/predictor_huggingfaceruntime.go +++ b/pkg/apis/serving/v1beta1/predictor_huggingfaceruntime.go @@ -17,10 +17,11 @@ limitations under the License. package v1beta1 import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "github.com/kserve/kserve/pkg/constants" "github.com/kserve/kserve/pkg/utils" - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) // HuggingFaceRuntimeSpec defines arguments for configuring HuggingFace model serving. @@ -29,9 +30,7 @@ type HuggingFaceRuntimeSpec struct { PredictorExtensionSpec `json:",inline"` } -var ( - _ ComponentImplementation = &HuggingFaceRuntimeSpec{} -) +var _ ComponentImplementation = &HuggingFaceRuntimeSpec{} // Validate returns an error if invalid func (o *HuggingFaceRuntimeSpec) Validate() error { @@ -43,11 +42,11 @@ func (o *HuggingFaceRuntimeSpec) Validate() error { // Default sets defaults on the resource func (o *HuggingFaceRuntimeSpec) Default(config *InferenceServicesConfig) { o.Container.Name = constants.InferenceServiceContainerName - setResourceRequirementDefaults(&o.Resources) + setResourceRequirementDefaults(config, &o.Resources) } // GetContainer transforms the resource into a container spec -func (o *HuggingFaceRuntimeSpec) GetContainer(metadata metav1.ObjectMeta, extensions *ComponentExtensionSpec, config *InferenceServicesConfig, predictorHost ...string) *v1.Container { +func (o *HuggingFaceRuntimeSpec) GetContainer(metadata metav1.ObjectMeta, extensions *ComponentExtensionSpec, config *InferenceServicesConfig, predictorHost ...string) *corev1.Container { return &o.Container } diff --git a/pkg/apis/serving/v1beta1/predictor_huggingfaceruntime_test.go b/pkg/apis/serving/v1beta1/predictor_huggingfaceruntime_test.go index 357fbff56cc..10214745156 100644 --- a/pkg/apis/serving/v1beta1/predictor_huggingfaceruntime_test.go +++ b/pkg/apis/serving/v1beta1/predictor_huggingfaceruntime_test.go @@ -21,12 +21,13 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "github.com/kserve/kserve/pkg/constants" "github.com/onsi/gomega" "github.com/onsi/gomega/types" "google.golang.org/protobuf/proto" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" + + "github.com/kserve/kserve/pkg/constants" ) func TestHuggingFaceRuntimeValidation(t *testing.T) { @@ -60,10 +61,19 @@ func TestHuggingFaceRuntimeValidation(t *testing.T) { func TestHuggingFaceRuntimeDefaulter(t *testing.T) { g := gomega.NewGomegaWithT(t) - defaultResource = v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("1"), - v1.ResourceMemory: resource.MustParse("2Gi"), + defaultResource := corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("2Gi"), } + config := &InferenceServicesConfig{ + Resource: ResourceConfig{ + CPULimit: "1", + MemoryLimit: "2Gi", + CPURequest: "1", + MemoryRequest: "2Gi", + }, + } + scenarios := map[string]struct { spec PredictorSpec expected PredictorSpec @@ -80,9 +90,9 @@ func TestHuggingFaceRuntimeDefaulter(t *testing.T) { HuggingFace: &HuggingFaceRuntimeSpec{ PredictorExtensionSpec: PredictorExtensionSpec{ RuntimeVersion: proto.String("v2.0.0"), - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, - Resources: v1.ResourceRequirements{ + Resources: corev1.ResourceRequirements{ Requests: defaultResource, Limits: defaultResource, }, @@ -95,7 +105,7 @@ func TestHuggingFaceRuntimeDefaulter(t *testing.T) { for name, scenario := range scenarios { t.Run(name, func(t *testing.T) { - scenario.spec.HuggingFace.Default(nil) + scenario.spec.HuggingFace.Default(config) if !g.Expect(scenario.spec).To(gomega.Equal(scenario.expected)) { t.Errorf("got %v, want %v", scenario.spec, scenario.expected) } @@ -115,12 +125,12 @@ func TestHuggingFaceRuntimeSpec_GetContainer(t *testing.T) { HuggingFace: &HuggingFaceRuntimeSpec{ PredictorExtensionSpec: PredictorExtensionSpec{ StorageURI: proto.String("s3://modelzoo"), - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, Image: "image:0.1", Args: nil, Env: nil, - Resources: v1.ResourceRequirements{}, + Resources: corev1.ResourceRequirements{}, }, }, }, @@ -151,11 +161,11 @@ func TestHuggingFaceRuntimeSpec_GetProtocol(t *testing.T) { HuggingFace: &HuggingFaceRuntimeSpec{ PredictorExtensionSpec: PredictorExtensionSpec{ StorageURI: proto.String("s3://modelzoo"), - Container: v1.Container{ + Container: corev1.Container{ Image: "image:0.1", Args: nil, Env: nil, - Resources: v1.ResourceRequirements{}, + Resources: corev1.ResourceRequirements{}, }, }, }, @@ -169,11 +179,11 @@ func TestHuggingFaceRuntimeSpec_GetProtocol(t *testing.T) { PredictorExtensionSpec: PredictorExtensionSpec{ ProtocolVersion: (*constants.InferenceServiceProtocol)(proto.String(string(constants.ProtocolV2))), StorageURI: proto.String("s3://modelzoo"), - Container: v1.Container{ + Container: corev1.Container{ Image: "image:0.1", Args: nil, Env: nil, - Resources: v1.ResourceRequirements{}, + Resources: corev1.ResourceRequirements{}, }, }, }, diff --git a/pkg/apis/serving/v1beta1/predictor_lightgbm.go b/pkg/apis/serving/v1beta1/predictor_lightgbm.go index a672f65ae25..58f418fe5c1 100644 --- a/pkg/apis/serving/v1beta1/predictor_lightgbm.go +++ b/pkg/apis/serving/v1beta1/predictor_lightgbm.go @@ -17,9 +17,10 @@ limitations under the License. package v1beta1 import ( - "github.com/kserve/kserve/pkg/constants" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/kserve/kserve/pkg/constants" ) // LightGBMSpec defines arguments for configuring LightGBMSpec model serving. @@ -28,17 +29,15 @@ type LightGBMSpec struct { PredictorExtensionSpec `json:",inline"` } -var ( - _ ComponentImplementation = &LightGBMSpec{} -) +var _ ComponentImplementation = &LightGBMSpec{} // Default sets defaults on the resource func (x *LightGBMSpec) Default(config *InferenceServicesConfig) { x.Container.Name = constants.InferenceServiceContainerName - setResourceRequirementDefaults(&x.Resources) + setResourceRequirementDefaults(config, &x.Resources) } -func (x *LightGBMSpec) GetContainer(metadata metav1.ObjectMeta, extensions *ComponentExtensionSpec, config *InferenceServicesConfig, predictorHost ...string) *v1.Container { +func (x *LightGBMSpec) GetContainer(metadata metav1.ObjectMeta, extensions *ComponentExtensionSpec, config *InferenceServicesConfig, predictorHost ...string) *corev1.Container { return &x.Container } diff --git a/pkg/apis/serving/v1beta1/predictor_lightgbm_test.go b/pkg/apis/serving/v1beta1/predictor_lightgbm_test.go index 1aa19952d66..f7122b6a99b 100644 --- a/pkg/apis/serving/v1beta1/predictor_lightgbm_test.go +++ b/pkg/apis/serving/v1beta1/predictor_lightgbm_test.go @@ -22,11 +22,12 @@ import ( "google.golang.org/protobuf/proto" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "github.com/kserve/kserve/pkg/constants" "github.com/onsi/gomega" "github.com/onsi/gomega/types" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" + + "github.com/kserve/kserve/pkg/constants" ) func TestLightGBMValidation(t *testing.T) { @@ -60,10 +61,19 @@ func TestLightGBMValidation(t *testing.T) { func TestLightGBMDefaulter(t *testing.T) { g := gomega.NewGomegaWithT(t) - defaultResource = v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("1"), - v1.ResourceMemory: resource.MustParse("2Gi"), + defaultResource := corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("2Gi"), + } + config := &InferenceServicesConfig{ + Resource: ResourceConfig{ + CPULimit: "1", + MemoryLimit: "2Gi", + CPURequest: "1", + MemoryRequest: "2Gi", + }, } + scenarios := map[string]struct { spec PredictorSpec expected PredictorSpec @@ -80,9 +90,9 @@ func TestLightGBMDefaulter(t *testing.T) { LightGBM: &LightGBMSpec{ PredictorExtensionSpec: PredictorExtensionSpec{ RuntimeVersion: proto.String("v0.3.0"), - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, - Resources: v1.ResourceRequirements{ + Resources: corev1.ResourceRequirements{ Requests: defaultResource, Limits: defaultResource, }, @@ -95,7 +105,7 @@ func TestLightGBMDefaulter(t *testing.T) { for name, scenario := range scenarios { t.Run(name, func(t *testing.T) { - scenario.spec.LightGBM.Default(nil) + scenario.spec.LightGBM.Default(config) if !g.Expect(scenario.spec).To(gomega.Equal(scenario.expected)) { t.Errorf("got %v, want %v", scenario.spec, scenario.expected) } @@ -104,20 +114,19 @@ func TestLightGBMDefaulter(t *testing.T) { } func TestCreateLightGBMModelServingContainer(t *testing.T) { - - var requestedResource = v1.ResourceRequirements{ - Limits: v1.ResourceList{ + requestedResource := corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ "cpu": resource.MustParse("100m"), }, - Requests: v1.ResourceList{ + Requests: corev1.ResourceList{ "cpu": resource.MustParse("90m"), }, } - var config = InferenceServicesConfig{} + config := InferenceServicesConfig{} g := gomega.NewGomegaWithT(t) scenarios := map[string]struct { isvc InferenceService - expectedContainerSpec *v1.Container + expectedContainerSpec *corev1.Container }{ "ContainerSpecWithDefaultImage": { isvc: InferenceService{ @@ -130,7 +139,7 @@ func TestCreateLightGBMModelServingContainer(t *testing.T) { PredictorExtensionSpec: PredictorExtensionSpec{ StorageURI: proto.String("gs://someUri"), RuntimeVersion: proto.String("0.1.0"), - Container: v1.Container{ + Container: corev1.Container{ Resources: requestedResource, }, }, @@ -138,7 +147,7 @@ func TestCreateLightGBMModelServingContainer(t *testing.T) { }, }, }, - expectedContainerSpec: &v1.Container{ + expectedContainerSpec: &corev1.Container{ Name: constants.InferenceServiceContainerName, Resources: requestedResource, }, @@ -153,7 +162,7 @@ func TestCreateLightGBMModelServingContainer(t *testing.T) { LightGBM: &LightGBMSpec{ PredictorExtensionSpec: PredictorExtensionSpec{ StorageURI: proto.String("gs://someUri"), - Container: v1.Container{ + Container: corev1.Container{ Image: "customImage:0.1.0", Resources: requestedResource, }, @@ -162,7 +171,7 @@ func TestCreateLightGBMModelServingContainer(t *testing.T) { }, }, }, - expectedContainerSpec: &v1.Container{ + expectedContainerSpec: &corev1.Container{ Image: "customImage:0.1.0", Name: constants.InferenceServiceContainerName, Resources: requestedResource, @@ -182,7 +191,7 @@ func TestCreateLightGBMModelServingContainer(t *testing.T) { PredictorExtensionSpec: PredictorExtensionSpec{ StorageURI: proto.String("gs://someUri"), RuntimeVersion: proto.String("0.1.0"), - Container: v1.Container{ + Container: corev1.Container{ Resources: requestedResource, }, }, @@ -190,7 +199,7 @@ func TestCreateLightGBMModelServingContainer(t *testing.T) { }, }, }, - expectedContainerSpec: &v1.Container{ + expectedContainerSpec: &corev1.Container{ Name: constants.InferenceServiceContainerName, Resources: requestedResource, }, @@ -209,7 +218,7 @@ func TestCreateLightGBMModelServingContainer(t *testing.T) { PredictorExtensionSpec: PredictorExtensionSpec{ StorageURI: proto.String("gs://someUri"), RuntimeVersion: proto.String("0.1.0"), - Container: v1.Container{ + Container: corev1.Container{ Resources: requestedResource, Args: []string{ constants.ArgumentWorkers + "=1", @@ -220,7 +229,7 @@ func TestCreateLightGBMModelServingContainer(t *testing.T) { }, }, }, - expectedContainerSpec: &v1.Container{ + expectedContainerSpec: &corev1.Container{ Name: constants.InferenceServiceContainerName, Resources: requestedResource, Args: []string{ diff --git a/pkg/apis/serving/v1beta1/predictor_model.go b/pkg/apis/serving/v1beta1/predictor_model.go index 0900d703dba..a6713f336f5 100644 --- a/pkg/apis/serving/v1beta1/predictor_model.go +++ b/pkg/apis/serving/v1beta1/predictor_model.go @@ -20,11 +20,12 @@ import ( "context" "sort" - "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" - "github.com/kserve/kserve/pkg/constants" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" + "github.com/kserve/kserve/pkg/constants" ) type ModelFormat struct { @@ -50,16 +51,14 @@ type ModelSpec struct { PredictorExtensionSpec `json:",inline"` } -var ( - _ ComponentImplementation = &ModelSpec{} -) +var _ ComponentImplementation = &ModelSpec{} // Here, the ComponentImplementation interface is implemented in order to maintain the // component validation logic. This will probably be refactored out eventually. func (m *ModelSpec) Default(config *InferenceServicesConfig) {} -func (m *ModelSpec) GetContainer(metadata metav1.ObjectMeta, extensions *ComponentExtensionSpec, config *InferenceServicesConfig, predictorHost ...string) *v1.Container { +func (m *ModelSpec) GetContainer(metadata metav1.ObjectMeta, extensions *ComponentExtensionSpec, config *InferenceServicesConfig, predictorHost ...string) *corev1.Container { return &m.Container } @@ -85,12 +84,12 @@ func (ss stringSet) contains(s string) bool { // support the given model. If the `isMMS` argument is true, this function will only return ServingRuntimes that are // ModelMesh compatible, otherwise only single-model serving compatible runtimes will be returned. // If `isMultinode` is true, this function will only return ServingRuntimes configured with workers. -func (m *ModelSpec) GetSupportingRuntimes(cl client.Client, namespace string, isMMS bool, isMultinode bool) ([]v1alpha1.SupportedRuntime, error) { +func (m *ModelSpec) GetSupportingRuntimes(ctx context.Context, cl client.Client, namespace string, isMMS bool, isMultinode bool) ([]v1alpha1.SupportedRuntime, error) { modelProtocolVersion := m.GetProtocol() // List all namespace-scoped runtimes. runtimes := &v1alpha1.ServingRuntimeList{} - if err := cl.List(context.TODO(), runtimes, client.InNamespace(namespace)); err != nil { + if err := cl.List(ctx, runtimes, client.InNamespace(namespace)); err != nil { return nil, err } // Sort namespace-scoped runtimes by created timestamp desc and name asc. @@ -98,7 +97,7 @@ func (m *ModelSpec) GetSupportingRuntimes(cl client.Client, namespace string, is // List all cluster-scoped runtimes. clusterRuntimes := &v1alpha1.ClusterServingRuntimeList{} - if err := cl.List(context.TODO(), clusterRuntimes); err != nil { + if err := cl.List(ctx, clusterRuntimes); err != nil { return nil, err } // Sort cluster-scoped runtimes by created timestamp desc and name asc. diff --git a/pkg/apis/serving/v1beta1/predictor_model_test.go b/pkg/apis/serving/v1beta1/predictor_model_test.go index df658671e4a..70b0d51676f 100644 --- a/pkg/apis/serving/v1beta1/predictor_model_test.go +++ b/pkg/apis/serving/v1beta1/predictor_model_test.go @@ -17,17 +17,20 @@ limitations under the License. package v1beta1 import ( + "context" "testing" - "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" - "github.com/kserve/kserve/pkg/constants" "github.com/onsi/gomega" "github.com/onsi/gomega/types" "google.golang.org/protobuf/proto" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/client/fake" + + "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" + "github.com/kserve/kserve/pkg/constants" ) func TestGetSupportingRuntimes(t *testing.T) { @@ -59,7 +62,7 @@ func TestGetSupportingRuntimes(t *testing.T) { }, ProtocolVersions: []constants.InferenceServiceProtocol{constants.ProtocolV1, constants.ProtocolV2}, ServingRuntimePodSpec: v1alpha1.ServingRuntimePodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Name: "kserve-container", Image: tfRuntime + "-image:latest", @@ -79,7 +82,7 @@ func TestGetSupportingRuntimes(t *testing.T) { }, ProtocolVersions: []constants.InferenceServiceProtocol{constants.ProtocolV1, constants.ProtocolV2}, ServingRuntimePodSpec: v1alpha1.ServingRuntimePodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Name: "kserve-container", Image: sklearnRuntime + "-image:latest", @@ -98,7 +101,7 @@ func TestGetSupportingRuntimes(t *testing.T) { }, ProtocolVersions: []constants.InferenceServiceProtocol{constants.ProtocolV1, constants.ProtocolV2}, ServingRuntimePodSpec: v1alpha1.ServingRuntimePodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Name: "kserve-container", Image: pmmlRuntime + "-image:latest", @@ -130,7 +133,7 @@ func TestGetSupportingRuntimes(t *testing.T) { }, ProtocolVersions: []constants.InferenceServiceProtocol{constants.ProtocolV2}, ServingRuntimePodSpec: v1alpha1.ServingRuntimePodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Name: "kserve-container", Image: pmmlRuntime + "-image:latest", @@ -158,7 +161,7 @@ func TestGetSupportingRuntimes(t *testing.T) { }, ProtocolVersions: []constants.InferenceServiceProtocol{constants.ProtocolV2}, ServingRuntimePodSpec: v1alpha1.ServingRuntimePodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Name: "kserve-container", Image: mlserverRuntime + "-image:latest", @@ -179,7 +182,7 @@ func TestGetSupportingRuntimes(t *testing.T) { }, ProtocolVersions: []constants.InferenceServiceProtocol{constants.ProtocolV2}, ServingRuntimePodSpec: v1alpha1.ServingRuntimePodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Name: "kserve-container", Image: sklearnRuntime + "-image:latest", @@ -209,7 +212,7 @@ func TestGetSupportingRuntimes(t *testing.T) { }, ProtocolVersions: []constants.InferenceServiceProtocol{constants.ProtocolV2}, ServingRuntimePodSpec: v1alpha1.ServingRuntimePodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Name: "kserve-container", Image: mlserverRuntime + "-image:latest", @@ -229,7 +232,7 @@ func TestGetSupportingRuntimes(t *testing.T) { }, ProtocolVersions: []constants.InferenceServiceProtocol{constants.ProtocolV2}, ServingRuntimePodSpec: v1alpha1.ServingRuntimePodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Name: "kserve-container", Image: mlserverRuntime + "-image:latest", @@ -250,7 +253,7 @@ func TestGetSupportingRuntimes(t *testing.T) { }, ProtocolVersions: []constants.InferenceServiceProtocol{constants.ProtocolV1, constants.ProtocolV2}, ServingRuntimePodSpec: v1alpha1.ServingRuntimePodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Name: "kserve-container", Image: huggingfaceMultinodeRuntime + "-image:latest", @@ -340,7 +343,7 @@ func TestGetSupportingRuntimes(t *testing.T) { }, } - var storageUri = "s3://test/model" + storageUri := "s3://test/model" scenarios := map[string]struct { spec *ModelSpec isMMS bool @@ -487,30 +490,29 @@ func TestGetSupportingRuntimes(t *testing.T) { mockClient := fake.NewClientBuilder().WithLists(runtimes, clusterRuntimes).WithScheme(s).Build() for name, scenario := range scenarios { t.Run(name, func(t *testing.T) { - res, _ := scenario.spec.GetSupportingRuntimes(mockClient, namespace, scenario.isMMS, scenario.isMultinode) + res, _ := scenario.spec.GetSupportingRuntimes(context.Background(), mockClient, namespace, scenario.isMMS, scenario.isMultinode) if !g.Expect(res).To(gomega.Equal(scenario.expected)) { t.Errorf("got %v, want %v", res, scenario.expected) } }) } - } func TestModelPredictorGetContainer(t *testing.T) { g := gomega.NewGomegaWithT(t) - var storageUri = "s3://test/model" + storageUri := "s3://test/model" isvcConfig := &InferenceServicesConfig{} objectMeta := metav1.ObjectMeta{ Name: "foo", Namespace: "default", } componentSpec := &ComponentExtensionSpec{ - MinReplicas: GetIntReference(3), + MinReplicas: ptr.To(int32(3)), MaxReplicas: 2, } scenarios := map[string]struct { spec *ModelSpec - expected v1.Container + expected corev1.Container }{ "ContainerSpecified": { spec: &ModelSpec{ @@ -519,9 +521,9 @@ func TestModelPredictorGetContainer(t *testing.T) { }, PredictorExtensionSpec: PredictorExtensionSpec{ StorageURI: &storageUri, - Container: v1.Container{ + Container: corev1.Container{ Name: "foo", - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "STORAGE_URI", Value: storageUri, @@ -530,9 +532,9 @@ func TestModelPredictorGetContainer(t *testing.T) { }, }, }, - expected: v1.Container{ + expected: corev1.Container{ Name: "foo", - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "STORAGE_URI", Value: storageUri, diff --git a/pkg/apis/serving/v1beta1/predictor_onnxruntime.go b/pkg/apis/serving/v1beta1/predictor_onnxruntime.go index 1dd2c80e3d4..ca3a702bffb 100644 --- a/pkg/apis/serving/v1beta1/predictor_onnxruntime.go +++ b/pkg/apis/serving/v1beta1/predictor_onnxruntime.go @@ -20,15 +20,14 @@ import ( "fmt" "path" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "github.com/kserve/kserve/pkg/constants" "github.com/kserve/kserve/pkg/utils" - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -var ( - ONNXFileExt = ".onnx" -) +var ONNXFileExt = ".onnx" // ONNXRuntimeSpec defines arguments for configuring ONNX model serving. type ONNXRuntimeSpec struct { @@ -36,9 +35,7 @@ type ONNXRuntimeSpec struct { PredictorExtensionSpec `json:",inline"` } -var ( - _ ComponentImplementation = &ONNXRuntimeSpec{} -) +var _ ComponentImplementation = &ONNXRuntimeSpec{} // Validate returns an error if invalid func (o *ONNXRuntimeSpec) Validate() error { @@ -56,11 +53,11 @@ func (o *ONNXRuntimeSpec) Validate() error { // Default sets defaults on the resource func (o *ONNXRuntimeSpec) Default(config *InferenceServicesConfig) { o.Container.Name = constants.InferenceServiceContainerName - setResourceRequirementDefaults(&o.Resources) + setResourceRequirementDefaults(config, &o.Resources) } // GetContainers transforms the resource into a container spec -func (o *ONNXRuntimeSpec) GetContainer(metadata metav1.ObjectMeta, extensions *ComponentExtensionSpec, config *InferenceServicesConfig, predictorHost ...string) *v1.Container { +func (o *ONNXRuntimeSpec) GetContainer(metadata metav1.ObjectMeta, extensions *ComponentExtensionSpec, config *InferenceServicesConfig, predictorHost ...string) *corev1.Container { return &o.Container } diff --git a/pkg/apis/serving/v1beta1/predictor_onnxruntime_test.go b/pkg/apis/serving/v1beta1/predictor_onnxruntime_test.go index b7976517273..b0eafbfe0f8 100644 --- a/pkg/apis/serving/v1beta1/predictor_onnxruntime_test.go +++ b/pkg/apis/serving/v1beta1/predictor_onnxruntime_test.go @@ -21,12 +21,13 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "github.com/kserve/kserve/pkg/constants" "github.com/onsi/gomega" "github.com/onsi/gomega/types" "google.golang.org/protobuf/proto" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" + + "github.com/kserve/kserve/pkg/constants" ) func TestOnnxRuntimeValidation(t *testing.T) { @@ -80,10 +81,19 @@ func TestOnnxRuntimeValidation(t *testing.T) { func TestONNXRuntimeDefaulter(t *testing.T) { g := gomega.NewGomegaWithT(t) - defaultResource = v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("1"), - v1.ResourceMemory: resource.MustParse("2Gi"), + defaultResource := corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("2Gi"), } + config := &InferenceServicesConfig{ + Resource: ResourceConfig{ + CPULimit: "1", + MemoryLimit: "2Gi", + CPURequest: "1", + MemoryRequest: "2Gi", + }, + } + scenarios := map[string]struct { spec PredictorSpec expected PredictorSpec @@ -100,9 +110,9 @@ func TestONNXRuntimeDefaulter(t *testing.T) { ONNX: &ONNXRuntimeSpec{ PredictorExtensionSpec: PredictorExtensionSpec{ RuntimeVersion: proto.String("v1.0.0"), - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, - Resources: v1.ResourceRequirements{ + Resources: corev1.ResourceRequirements{ Requests: defaultResource, Limits: defaultResource, }, @@ -115,7 +125,7 @@ func TestONNXRuntimeDefaulter(t *testing.T) { for name, scenario := range scenarios { t.Run(name, func(t *testing.T) { - scenario.spec.ONNX.Default(nil) + scenario.spec.ONNX.Default(config) if !g.Expect(scenario.spec).To(gomega.Equal(scenario.expected)) { t.Errorf("got %v, want %v", scenario.spec, scenario.expected) } @@ -135,12 +145,12 @@ func TestONNXRuntimeSpec_GetContainer(t *testing.T) { ONNX: &ONNXRuntimeSpec{ PredictorExtensionSpec: PredictorExtensionSpec{ StorageURI: proto.String("s3://modelzoo"), - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, Image: "image:0.1", Args: nil, Env: nil, - Resources: v1.ResourceRequirements{}, + Resources: corev1.ResourceRequirements{}, }, }, }, @@ -171,11 +181,11 @@ func TestONNXRuntimeSpec_GetProtocol(t *testing.T) { ONNX: &ONNXRuntimeSpec{ PredictorExtensionSpec: PredictorExtensionSpec{ StorageURI: proto.String("s3://modelzoo"), - Container: v1.Container{ + Container: corev1.Container{ Image: "image:0.1", Args: nil, Env: nil, - Resources: v1.ResourceRequirements{}, + Resources: corev1.ResourceRequirements{}, }, }, }, @@ -189,11 +199,11 @@ func TestONNXRuntimeSpec_GetProtocol(t *testing.T) { PredictorExtensionSpec: PredictorExtensionSpec{ ProtocolVersion: (*constants.InferenceServiceProtocol)(proto.String(string(constants.ProtocolV2))), StorageURI: proto.String("s3://modelzoo"), - Container: v1.Container{ + Container: corev1.Container{ Image: "image:0.1", Args: nil, Env: nil, - Resources: v1.ResourceRequirements{}, + Resources: corev1.ResourceRequirements{}, }, }, }, diff --git a/pkg/apis/serving/v1beta1/predictor_paddle.go b/pkg/apis/serving/v1beta1/predictor_paddle.go index 6e0000e1658..37432ce1717 100644 --- a/pkg/apis/serving/v1beta1/predictor_paddle.go +++ b/pkg/apis/serving/v1beta1/predictor_paddle.go @@ -17,9 +17,10 @@ limitations under the License. package v1beta1 import ( - "github.com/kserve/kserve/pkg/constants" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/kserve/kserve/pkg/constants" ) type PaddleServerSpec struct { @@ -29,10 +30,10 @@ type PaddleServerSpec struct { func (p *PaddleServerSpec) Default(config *InferenceServicesConfig) { // TODO: add GPU support p.Container.Name = constants.InferenceServiceContainerName - setResourceRequirementDefaults(&p.Resources) + setResourceRequirementDefaults(config, &p.Resources) } -func (p *PaddleServerSpec) GetContainer(metadata metav1.ObjectMeta, extensions *ComponentExtensionSpec, config *InferenceServicesConfig, predictorHost ...string) *v1.Container { +func (p *PaddleServerSpec) GetContainer(metadata metav1.ObjectMeta, extensions *ComponentExtensionSpec, config *InferenceServicesConfig, predictorHost ...string) *corev1.Container { return &p.Container } diff --git a/pkg/apis/serving/v1beta1/predictor_paddle_test.go b/pkg/apis/serving/v1beta1/predictor_paddle_test.go index 1aa7c9ebba8..2d8b687ce2d 100644 --- a/pkg/apis/serving/v1beta1/predictor_paddle_test.go +++ b/pkg/apis/serving/v1beta1/predictor_paddle_test.go @@ -19,13 +19,15 @@ package v1beta1 import ( "testing" + "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "github.com/kserve/kserve/pkg/constants" "github.com/onsi/gomega" "github.com/onsi/gomega/types" "google.golang.org/protobuf/proto" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" + + "github.com/kserve/kserve/pkg/constants" ) func TestPaddleValidation(t *testing.T) { @@ -59,6 +61,18 @@ func TestPaddleValidation(t *testing.T) { func TestPaddleDefaulter(t *testing.T) { g := gomega.NewGomegaWithT(t) + defaultResource := corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("2Gi"), + } + config := &InferenceServicesConfig{ + Resource: ResourceConfig{ + CPULimit: "1", + MemoryLimit: "2Gi", + CPURequest: "1", + MemoryRequest: "2Gi", + }, + } scenarios := map[string]struct { spec PredictorSpec @@ -73,9 +87,9 @@ func TestPaddleDefaulter(t *testing.T) { expected: PredictorSpec{ Paddle: &PaddleServerSpec{ PredictorExtensionSpec: PredictorExtensionSpec{ - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, - Resources: v1.ResourceRequirements{ + Resources: corev1.ResourceRequirements{ Requests: defaultResource, Limits: defaultResource, }, @@ -88,7 +102,7 @@ func TestPaddleDefaulter(t *testing.T) { for name, scenario := range scenarios { t.Run(name, func(t *testing.T) { - scenario.spec.Paddle.Default(nil) + scenario.spec.Paddle.Default(config) if !g.Expect(scenario.spec).To(gomega.Equal(scenario.expected)) { t.Errorf("got %v, want %v", scenario.spec, scenario.expected) } @@ -108,12 +122,12 @@ func TestPaddleServerSpec_GetContainer(t *testing.T) { Paddle: &PaddleServerSpec{ PredictorExtensionSpec: PredictorExtensionSpec{ StorageURI: proto.String("s3://modelzoo"), - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, Image: "image:0.1", Args: nil, Env: nil, - Resources: v1.ResourceRequirements{}, + Resources: corev1.ResourceRequirements{}, }, }, }, @@ -144,11 +158,11 @@ func TestPaddleServerSpec_GetProtocol(t *testing.T) { Paddle: &PaddleServerSpec{ PredictorExtensionSpec: PredictorExtensionSpec{ StorageURI: proto.String("s3://modelzoo"), - Container: v1.Container{ + Container: corev1.Container{ Image: "image:0.1", Args: nil, Env: nil, - Resources: v1.ResourceRequirements{}, + Resources: corev1.ResourceRequirements{}, }, }, }, @@ -162,11 +176,11 @@ func TestPaddleServerSpec_GetProtocol(t *testing.T) { PredictorExtensionSpec: PredictorExtensionSpec{ ProtocolVersion: (*constants.InferenceServiceProtocol)(proto.String(string(constants.ProtocolV2))), StorageURI: proto.String("s3://modelzoo"), - Container: v1.Container{ + Container: corev1.Container{ Image: "image:0.1", Args: nil, Env: nil, - Resources: v1.ResourceRequirements{}, + Resources: corev1.ResourceRequirements{}, }, }, }, diff --git a/pkg/apis/serving/v1beta1/predictor_pmml.go b/pkg/apis/serving/v1beta1/predictor_pmml.go index 6ee0943cece..0a5055575a9 100644 --- a/pkg/apis/serving/v1beta1/predictor_pmml.go +++ b/pkg/apis/serving/v1beta1/predictor_pmml.go @@ -17,10 +17,11 @@ limitations under the License. package v1beta1 import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "github.com/kserve/kserve/pkg/constants" "github.com/kserve/kserve/pkg/utils" - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) // PMMLSpec defines arguments for configuring PMML model serving. @@ -29,9 +30,7 @@ type PMMLSpec struct { PredictorExtensionSpec `json:",inline"` } -var ( - _ ComponentImplementation = &PMMLSpec{} -) +var _ ComponentImplementation = &PMMLSpec{} // Validate returns an error if invalid func (p *PMMLSpec) Validate() error { @@ -43,10 +42,10 @@ func (p *PMMLSpec) Validate() error { // Default sets defaults on the resource func (p *PMMLSpec) Default(config *InferenceServicesConfig) { p.Container.Name = constants.InferenceServiceContainerName - setResourceRequirementDefaults(&p.Resources) + setResourceRequirementDefaults(config, &p.Resources) } -func (p *PMMLSpec) GetContainer(metadata metav1.ObjectMeta, extensions *ComponentExtensionSpec, config *InferenceServicesConfig, predictorHost ...string) *v1.Container { +func (p *PMMLSpec) GetContainer(metadata metav1.ObjectMeta, extensions *ComponentExtensionSpec, config *InferenceServicesConfig, predictorHost ...string) *corev1.Container { return &p.Container } diff --git a/pkg/apis/serving/v1beta1/predictor_pmml_test.go b/pkg/apis/serving/v1beta1/predictor_pmml_test.go index 3819a707dcb..72ba4f87d1f 100644 --- a/pkg/apis/serving/v1beta1/predictor_pmml_test.go +++ b/pkg/apis/serving/v1beta1/predictor_pmml_test.go @@ -23,11 +23,12 @@ import ( "google.golang.org/protobuf/proto" - "github.com/kserve/kserve/pkg/constants" "github.com/onsi/gomega" "github.com/onsi/gomega/types" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" + + "github.com/kserve/kserve/pkg/constants" ) func TestPMMLValidation(t *testing.T) { @@ -61,10 +62,19 @@ func TestPMMLValidation(t *testing.T) { func TestPMMLDefaulter(t *testing.T) { g := gomega.NewGomegaWithT(t) - defaultResource = v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("1"), - v1.ResourceMemory: resource.MustParse("2Gi"), + defaultResource := corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("2Gi"), } + config := &InferenceServicesConfig{ + Resource: ResourceConfig{ + CPULimit: "1", + MemoryLimit: "2Gi", + CPURequest: "1", + MemoryRequest: "2Gi", + }, + } + scenarios := map[string]struct { spec PredictorSpec expected PredictorSpec @@ -81,9 +91,9 @@ func TestPMMLDefaulter(t *testing.T) { PMML: &PMMLSpec{ PredictorExtensionSpec: PredictorExtensionSpec{ RuntimeVersion: proto.String("v0.3.0"), - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, - Resources: v1.ResourceRequirements{ + Resources: corev1.ResourceRequirements{ Requests: defaultResource, Limits: defaultResource, }, @@ -96,7 +106,7 @@ func TestPMMLDefaulter(t *testing.T) { for name, scenario := range scenarios { t.Run(name, func(t *testing.T) { - scenario.spec.PMML.Default(nil) + scenario.spec.PMML.Default(config) if !g.Expect(scenario.spec).To(gomega.Equal(scenario.expected)) { t.Errorf("got %v, want %v", scenario.spec, scenario.expected) } @@ -116,11 +126,11 @@ func TestPMMLSpec_GetProtocol(t *testing.T) { PMML: &PMMLSpec{ PredictorExtensionSpec: PredictorExtensionSpec{ StorageURI: proto.String("s3://modelzoo"), - Container: v1.Container{ + Container: corev1.Container{ Image: "image:0.1", Args: nil, Env: nil, - Resources: v1.ResourceRequirements{}, + Resources: corev1.ResourceRequirements{}, }, }, }, @@ -134,11 +144,11 @@ func TestPMMLSpec_GetProtocol(t *testing.T) { PredictorExtensionSpec: PredictorExtensionSpec{ ProtocolVersion: (*constants.InferenceServiceProtocol)(proto.String(string(constants.ProtocolV2))), StorageURI: proto.String("s3://modelzoo"), - Container: v1.Container{ + Container: corev1.Container{ Image: "image:0.1", Args: nil, Env: nil, - Resources: v1.ResourceRequirements{}, + Resources: corev1.ResourceRequirements{}, }, }, }, @@ -170,12 +180,12 @@ func TestPMMLSpec_GetContainer(t *testing.T) { PMML: &PMMLSpec{ PredictorExtensionSpec: PredictorExtensionSpec{ StorageURI: proto.String("s3://modelzoo"), - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, Image: "image:0.1", Args: nil, Env: nil, - Resources: v1.ResourceRequirements{}, + Resources: corev1.ResourceRequirements{}, }, }, }, diff --git a/pkg/apis/serving/v1beta1/predictor_sklearn.go b/pkg/apis/serving/v1beta1/predictor_sklearn.go index 291107fc635..10ffe74c82e 100644 --- a/pkg/apis/serving/v1beta1/predictor_sklearn.go +++ b/pkg/apis/serving/v1beta1/predictor_sklearn.go @@ -17,11 +17,10 @@ limitations under the License. package v1beta1 import ( - "strconv" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/kserve/kserve/pkg/constants" - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) // SKLearnSpec defines arguments for configuring SKLearn model serving. @@ -30,9 +29,7 @@ type SKLearnSpec struct { PredictorExtensionSpec `json:",inline"` } -var ( - _ ComponentImplementation = &SKLearnSpec{} -) +var _ ComponentImplementation = &SKLearnSpec{} // Default sets defaults on the resource func (k *SKLearnSpec) Default(config *InferenceServicesConfig) { @@ -43,71 +40,10 @@ func (k *SKLearnSpec) Default(config *InferenceServicesConfig) { k.ProtocolVersion = &defaultProtocol } - setResourceRequirementDefaults(&k.Resources) -} - -// nolint: unused -func (k *SKLearnSpec) getEnvVarsV2() []v1.EnvVar { - vars := []v1.EnvVar{ - { - Name: constants.MLServerHTTPPortEnv, - Value: strconv.Itoa(int(constants.MLServerISRestPort)), - }, - { - Name: constants.MLServerGRPCPortEnv, - Value: strconv.Itoa(int(constants.MLServerISGRPCPort)), - }, - { - Name: constants.MLServerModelsDirEnv, - Value: constants.DefaultModelLocalMountPath, - }, - } - - if k.StorageURI == nil { - vars = append( - vars, - v1.EnvVar{ - Name: constants.MLServerLoadModelsStartupEnv, - Value: strconv.FormatBool(false), - }, - ) - } - - return vars -} - -// nolint: unused -func (k *SKLearnSpec) getDefaultsV2(metadata metav1.ObjectMeta) []v1.EnvVar { - // These env vars set default parameters that can always be overridden - // individually through `model-settings.json` config files. - // These will be used as fallbacks for any missing properties and / or to run - // without a `model-settings.json` file in place. - vars := []v1.EnvVar{ - { - Name: constants.MLServerModelImplementationEnv, - Value: constants.MLServerSKLearnImplementation, - }, - } - - if k.StorageURI != nil { - // These env vars only make sense as a default for non-MMS servers - vars = append( - vars, - v1.EnvVar{ - Name: constants.MLServerModelNameEnv, - Value: metadata.Name, - }, - v1.EnvVar{ - Name: constants.MLServerModelURIEnv, - Value: constants.DefaultModelLocalMountPath, - }, - ) - } - - return vars + setResourceRequirementDefaults(config, &k.Resources) } -func (k *SKLearnSpec) GetContainer(metadata metav1.ObjectMeta, extensions *ComponentExtensionSpec, config *InferenceServicesConfig, predictorHost ...string) *v1.Container { +func (k *SKLearnSpec) GetContainer(metadata metav1.ObjectMeta, extensions *ComponentExtensionSpec, config *InferenceServicesConfig, predictorHost ...string) *corev1.Container { return &k.Container } diff --git a/pkg/apis/serving/v1beta1/predictor_sklearn_test.go b/pkg/apis/serving/v1beta1/predictor_sklearn_test.go index c2bd41e1b1e..7f6f6104a78 100644 --- a/pkg/apis/serving/v1beta1/predictor_sklearn_test.go +++ b/pkg/apis/serving/v1beta1/predictor_sklearn_test.go @@ -17,17 +17,17 @@ limitations under the License. package v1beta1 import ( - "strconv" "testing" "google.golang.org/protobuf/proto" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "github.com/kserve/kserve/pkg/constants" "github.com/onsi/gomega" "github.com/onsi/gomega/types" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" + + "github.com/kserve/kserve/pkg/constants" ) func TestSKLearnValidation(t *testing.T) { @@ -65,10 +65,19 @@ func TestSKLearnDefaulter(t *testing.T) { protocolV1 := constants.ProtocolV1 protocolV2 := constants.ProtocolV2 - defaultResource = v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("1"), - v1.ResourceMemory: resource.MustParse("2Gi"), + defaultResource := corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("2Gi"), + } + config := &InferenceServicesConfig{ + Resource: ResourceConfig{ + CPULimit: "1", + MemoryLimit: "2Gi", + CPURequest: "1", + MemoryRequest: "2Gi", + }, } + scenarios := map[string]struct { spec PredictorSpec expected PredictorSpec @@ -85,9 +94,9 @@ func TestSKLearnDefaulter(t *testing.T) { SKLearn: &SKLearnSpec{ PredictorExtensionSpec: PredictorExtensionSpec{ ProtocolVersion: &protocolV2, - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, - Resources: v1.ResourceRequirements{ + Resources: corev1.ResourceRequirements{ Requests: defaultResource, Limits: defaultResource, }, @@ -109,9 +118,9 @@ func TestSKLearnDefaulter(t *testing.T) { PredictorExtensionSpec: PredictorExtensionSpec{ RuntimeVersion: proto.String("v0.3.0"), ProtocolVersion: &protocolV1, - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, - Resources: v1.ResourceRequirements{ + Resources: corev1.ResourceRequirements{ Requests: defaultResource, Limits: defaultResource, }, @@ -124,7 +133,7 @@ func TestSKLearnDefaulter(t *testing.T) { for name, scenario := range scenarios { t.Run(name, func(t *testing.T) { - scenario.spec.SKLearn.Default(nil) + scenario.spec.SKLearn.Default(config) if !g.Expect(scenario.spec).To(gomega.Equal(scenario.expected)) { t.Errorf("got %v, want %v", scenario.spec, scenario.expected) } @@ -133,23 +142,23 @@ func TestSKLearnDefaulter(t *testing.T) { } func TestCreateSKLearnModelServingContainerV1(t *testing.T) { - var requestedResource = v1.ResourceRequirements{ - Limits: v1.ResourceList{ + requestedResource := corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ "cpu": resource.Quantity{ Format: "100", }, }, - Requests: v1.ResourceList{ + Requests: corev1.ResourceList{ "cpu": resource.Quantity{ Format: "90", }, }, } - var config = InferenceServicesConfig{} + config := InferenceServicesConfig{} g := gomega.NewGomegaWithT(t) scenarios := map[string]struct { isvc InferenceService - expectedContainerSpec *v1.Container + expectedContainerSpec *corev1.Container }{ "ContainerSpecWithoutRuntime": { isvc: InferenceService{ @@ -161,7 +170,7 @@ func TestCreateSKLearnModelServingContainerV1(t *testing.T) { SKLearn: &SKLearnSpec{ PredictorExtensionSpec: PredictorExtensionSpec{ StorageURI: proto.String("gs://someUri"), - Container: v1.Container{ + Container: corev1.Container{ Resources: requestedResource, }, }, @@ -169,7 +178,7 @@ func TestCreateSKLearnModelServingContainerV1(t *testing.T) { }, }, }, - expectedContainerSpec: &v1.Container{ + expectedContainerSpec: &corev1.Container{ Name: constants.InferenceServiceContainerName, Resources: requestedResource, }, @@ -185,7 +194,7 @@ func TestCreateSKLearnModelServingContainerV1(t *testing.T) { PredictorExtensionSpec: PredictorExtensionSpec{ StorageURI: proto.String("gs://someUri"), RuntimeVersion: proto.String("0.1.0"), - Container: v1.Container{ + Container: corev1.Container{ Resources: requestedResource, }, }, @@ -193,7 +202,7 @@ func TestCreateSKLearnModelServingContainerV1(t *testing.T) { }, }, }, - expectedContainerSpec: &v1.Container{ + expectedContainerSpec: &corev1.Container{ Name: constants.InferenceServiceContainerName, Resources: requestedResource, }, @@ -208,7 +217,7 @@ func TestCreateSKLearnModelServingContainerV1(t *testing.T) { SKLearn: &SKLearnSpec{ PredictorExtensionSpec: PredictorExtensionSpec{ StorageURI: proto.String("gs://someUri"), - Container: v1.Container{ + Container: corev1.Container{ Image: "customImage:0.1.0", Resources: requestedResource, }, @@ -217,7 +226,7 @@ func TestCreateSKLearnModelServingContainerV1(t *testing.T) { }, }, }, - expectedContainerSpec: &v1.Container{ + expectedContainerSpec: &corev1.Container{ Image: "customImage:0.1.0", Name: constants.InferenceServiceContainerName, Resources: requestedResource, @@ -237,7 +246,7 @@ func TestCreateSKLearnModelServingContainerV1(t *testing.T) { PredictorExtensionSpec: PredictorExtensionSpec{ StorageURI: proto.String("gs://someUri"), RuntimeVersion: proto.String("0.1.0"), - Container: v1.Container{ + Container: corev1.Container{ Resources: requestedResource, }, }, @@ -245,7 +254,7 @@ func TestCreateSKLearnModelServingContainerV1(t *testing.T) { }, }, }, - expectedContainerSpec: &v1.Container{ + expectedContainerSpec: &corev1.Container{ Name: constants.InferenceServiceContainerName, Resources: requestedResource, }, @@ -264,7 +273,7 @@ func TestCreateSKLearnModelServingContainerV1(t *testing.T) { PredictorExtensionSpec: PredictorExtensionSpec{ StorageURI: proto.String("gs://someUri"), RuntimeVersion: proto.String("0.1.0"), - Container: v1.Container{ + Container: corev1.Container{ Resources: requestedResource, Args: []string{ "--workers=1", @@ -275,7 +284,7 @@ func TestCreateSKLearnModelServingContainerV1(t *testing.T) { }, }, }, - expectedContainerSpec: &v1.Container{ + expectedContainerSpec: &corev1.Container{ Name: constants.InferenceServiceContainerName, Resources: requestedResource, Args: []string{ @@ -299,23 +308,23 @@ func TestCreateSKLearnModelServingContainerV1(t *testing.T) { func TestCreateSKLearnModelServingContainerV2(t *testing.T) { protocolV2 := constants.ProtocolV2 - var requestedResource = v1.ResourceRequirements{ - Limits: v1.ResourceList{ + requestedResource := corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ "cpu": resource.Quantity{ Format: "100", }, }, - Requests: v1.ResourceList{ + Requests: corev1.ResourceList{ "cpu": resource.Quantity{ Format: "90", }, }, } - var config = InferenceServicesConfig{} + config := InferenceServicesConfig{} g := gomega.NewGomegaWithT(t) scenarios := map[string]struct { isvc InferenceService - expectedContainerSpec *v1.Container + expectedContainerSpec *corev1.Container }{ "ContainerSpecWithDefaultImage": { isvc: InferenceService{ @@ -329,7 +338,7 @@ func TestCreateSKLearnModelServingContainerV2(t *testing.T) { StorageURI: proto.String("gs://someUri"), RuntimeVersion: proto.String("0.1.0"), ProtocolVersion: &protocolV2, - Container: v1.Container{ + Container: corev1.Container{ Resources: requestedResource, }, }, @@ -337,7 +346,7 @@ func TestCreateSKLearnModelServingContainerV2(t *testing.T) { }, }, }, - expectedContainerSpec: &v1.Container{ + expectedContainerSpec: &corev1.Container{ Name: constants.InferenceServiceContainerName, Resources: requestedResource, }, @@ -353,7 +362,7 @@ func TestCreateSKLearnModelServingContainerV2(t *testing.T) { PredictorExtensionSpec: PredictorExtensionSpec{ StorageURI: proto.String("gs://someUri"), ProtocolVersion: &protocolV2, - Container: v1.Container{ + Container: corev1.Container{ Image: "customImage:0.1.0", Resources: requestedResource, }, @@ -362,7 +371,7 @@ func TestCreateSKLearnModelServingContainerV2(t *testing.T) { }, }, }, - expectedContainerSpec: &v1.Container{ + expectedContainerSpec: &corev1.Container{ Image: "customImage:0.1.0", Name: constants.InferenceServiceContainerName, Resources: requestedResource, @@ -378,7 +387,7 @@ func TestCreateSKLearnModelServingContainerV2(t *testing.T) { SKLearn: &SKLearnSpec{ PredictorExtensionSpec: PredictorExtensionSpec{ ProtocolVersion: &protocolV2, - Container: v1.Container{ + Container: corev1.Container{ Resources: requestedResource, }, }, @@ -386,7 +395,7 @@ func TestCreateSKLearnModelServingContainerV2(t *testing.T) { }, }, }, - expectedContainerSpec: &v1.Container{ + expectedContainerSpec: &corev1.Container{ Name: constants.InferenceServiceContainerName, Resources: requestedResource, }, @@ -434,128 +443,3 @@ func TestSKLearnGetProtocol(t *testing.T) { g.Expect(protocol).To(scenario.matcher) } } - -func TestSKLearnSpec_GetDefaultsV2(t *testing.T) { - g := gomega.NewGomegaWithT(t) - - metadata := metav1.ObjectMeta{ - Name: "test", - } - scenarios := map[string]struct { - spec PredictorSpec - matcher types.GomegaMatcher - }{ - "storage uri is nil": { - spec: PredictorSpec{ - SKLearn: &SKLearnSpec{ - PredictorExtensionSpec: PredictorExtensionSpec{}, - }, - }, - matcher: gomega.Equal([]v1.EnvVar{ - { - Name: constants.MLServerModelImplementationEnv, - Value: constants.MLServerSKLearnImplementation, - }, - }), - }, - "storage uri is not nil": { - spec: PredictorSpec{ - SKLearn: &SKLearnSpec{ - PredictorExtensionSpec: PredictorExtensionSpec{ - StorageURI: proto.String("gs://kserve/model"), - }, - }, - }, - matcher: gomega.Equal([]v1.EnvVar{ - { - Name: constants.MLServerModelImplementationEnv, - Value: constants.MLServerSKLearnImplementation, - }, - { - Name: constants.MLServerModelNameEnv, - Value: metadata.Name, - }, - { - Name: constants.MLServerModelURIEnv, - Value: constants.DefaultModelLocalMountPath, - }, - }), - }, - } - - for name, scenario := range scenarios { - t.Run(name, func(t *testing.T) { - res := scenario.spec.SKLearn.getDefaultsV2(metadata) - if !g.Expect(res).To(scenario.matcher) { - t.Errorf("got %q, want %q", res, scenario.matcher) - } - }) - } -} - -func TestSKLearnSpec_GetEnvVarsV2(t *testing.T) { - g := gomega.NewGomegaWithT(t) - - scenarios := map[string]struct { - spec PredictorSpec - matcher types.GomegaMatcher - }{ - "storage uri is nil": { - spec: PredictorSpec{ - SKLearn: &SKLearnSpec{ - PredictorExtensionSpec: PredictorExtensionSpec{}, - }, - }, - matcher: gomega.Equal([]v1.EnvVar{ - { - Name: constants.MLServerHTTPPortEnv, - Value: strconv.Itoa(int(constants.MLServerISRestPort)), - }, - { - Name: constants.MLServerGRPCPortEnv, - Value: strconv.Itoa(int(constants.MLServerISGRPCPort)), - }, - { - Name: constants.MLServerModelsDirEnv, - Value: constants.DefaultModelLocalMountPath, - }, - { - Name: constants.MLServerLoadModelsStartupEnv, - Value: strconv.FormatBool(false), - }, - }), - }, - "storage uri is not nil": { - spec: PredictorSpec{ - SKLearn: &SKLearnSpec{ - PredictorExtensionSpec: PredictorExtensionSpec{ - StorageURI: proto.String("gs://kserve/model"), - }, - }, - }, - matcher: gomega.Equal([]v1.EnvVar{ - { - Name: constants.MLServerHTTPPortEnv, - Value: strconv.Itoa(int(constants.MLServerISRestPort)), - }, - { - Name: constants.MLServerGRPCPortEnv, - Value: strconv.Itoa(int(constants.MLServerISGRPCPort)), - }, - { - Name: constants.MLServerModelsDirEnv, - Value: constants.DefaultModelLocalMountPath, - }, - }), - }, - } - - for name, scenario := range scenarios { - t.Run(name, func(t *testing.T) { - res := scenario.spec.SKLearn.getEnvVarsV2() - if !g.Expect(res).To(scenario.matcher) { - t.Errorf("got %q, want %q", res, scenario.matcher) - } - }) - } -} diff --git a/pkg/apis/serving/v1beta1/predictor_test.go b/pkg/apis/serving/v1beta1/predictor_test.go index e4e2f97aab2..64fc602dc86 100644 --- a/pkg/apis/serving/v1beta1/predictor_test.go +++ b/pkg/apis/serving/v1beta1/predictor_test.go @@ -21,7 +21,7 @@ import ( "github.com/onsi/gomega" "google.golang.org/protobuf/proto" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" ) func makeTestPredictorSpec() *PredictorSpec { @@ -38,21 +38,21 @@ func TestGetPredictorImplementations(t *testing.T) { g := gomega.NewGomegaWithT(t) spec := makeTestPredictorSpec() implementations := spec.GetPredictorImplementations() - g.Expect(len(implementations)).ShouldNot(gomega.BeZero()) + g.Expect(implementations).ShouldNot(gomega.BeEmpty()) g.Expect(implementations[0]).Should(gomega.Equal(spec.PyTorch)) spec.PyTorch = nil implementations = spec.GetPredictorImplementations() - g.Expect(len(implementations)).Should(gomega.BeZero()) + g.Expect(implementations).Should(gomega.BeEmpty()) - spec.PodSpec.Containers = []v1.Container{ + spec.PodSpec.Containers = []corev1.Container{ { Name: "Test-Container", Image: "test/predictor", }, } implementations = spec.GetPredictorImplementations() - g.Expect(len(implementations)).ShouldNot(gomega.BeZero()) + g.Expect(implementations).ShouldNot(gomega.BeEmpty()) g.Expect(implementations[0]).Should(gomega.Equal(NewCustomPredictor(&spec.PodSpec))) } diff --git a/pkg/apis/serving/v1beta1/predictor_tfserving.go b/pkg/apis/serving/v1beta1/predictor_tfserving.go index 80ed651934d..7a83c5d4020 100644 --- a/pkg/apis/serving/v1beta1/predictor_tfserving.go +++ b/pkg/apis/serving/v1beta1/predictor_tfserving.go @@ -17,10 +17,10 @@ limitations under the License. package v1beta1 import ( - "fmt" + "errors" "strings" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/kserve/kserve/pkg/constants" @@ -42,9 +42,7 @@ type TFServingSpec struct { PredictorExtensionSpec `json:",inline"` } -var ( - _ ComponentImplementation = &TFServingSpec{} -) +var _ ComponentImplementation = &TFServingSpec{} // Validate returns an error if invalid func (t *TFServingSpec) Validate() error { @@ -59,11 +57,11 @@ func (t *TFServingSpec) validateGPU() error { return nil } if utils.IsGPUEnabled(t.Resources) && !strings.Contains(*t.RuntimeVersion, TensorflowServingGPUSuffix) { - return fmt.Errorf(InvalidTensorflowRuntimeIncludesGPU) + return errors.New(InvalidTensorflowRuntimeIncludesGPU) } if !utils.IsGPUEnabled(t.Resources) && strings.Contains(*t.RuntimeVersion, TensorflowServingGPUSuffix) { - return fmt.Errorf(InvalidTensorflowRuntimeExcludesGPU) + return errors.New(InvalidTensorflowRuntimeExcludesGPU) } return nil } @@ -71,10 +69,10 @@ func (t *TFServingSpec) validateGPU() error { // Default sets defaults on the resource func (t *TFServingSpec) Default(config *InferenceServicesConfig) { t.Container.Name = constants.InferenceServiceContainerName - setResourceRequirementDefaults(&t.Resources) + setResourceRequirementDefaults(config, &t.Resources) } -func (t *TFServingSpec) GetContainer(metadata metav1.ObjectMeta, extensions *ComponentExtensionSpec, config *InferenceServicesConfig, predictorHost ...string) *v1.Container { +func (t *TFServingSpec) GetContainer(metadata metav1.ObjectMeta, extensions *ComponentExtensionSpec, config *InferenceServicesConfig, predictorHost ...string) *corev1.Container { return &t.Container } diff --git a/pkg/apis/serving/v1beta1/predictor_tfserving_test.go b/pkg/apis/serving/v1beta1/predictor_tfserving_test.go index b0ad2897c50..3634078954c 100644 --- a/pkg/apis/serving/v1beta1/predictor_tfserving_test.go +++ b/pkg/apis/serving/v1beta1/predictor_tfserving_test.go @@ -21,12 +21,13 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "github.com/kserve/kserve/pkg/constants" "github.com/onsi/gomega" "github.com/onsi/gomega/types" "google.golang.org/protobuf/proto" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" + + "github.com/kserve/kserve/pkg/constants" ) func TestTensorflowValidation(t *testing.T) { @@ -60,9 +61,9 @@ func TestTensorflowValidation(t *testing.T) { Tensorflow: &TFServingSpec{ PredictorExtensionSpec: PredictorExtensionSpec{ RuntimeVersion: proto.String("latest"), - Container: v1.Container{ - Resources: v1.ResourceRequirements{ - Limits: v1.ResourceList{constants.NvidiaGPUResourceType: resource.MustParse("1")}, + Container: corev1.Container{ + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{constants.NvidiaGPUResourceType: resource.MustParse("1")}, }, }, }, @@ -86,9 +87,17 @@ func TestTensorflowValidation(t *testing.T) { func TestTensorflowDefaulter(t *testing.T) { g := gomega.NewGomegaWithT(t) - defaultResource = v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("1"), - v1.ResourceMemory: resource.MustParse("2Gi"), + defaultResource := corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("2Gi"), + } + config := &InferenceServicesConfig{ + Resource: ResourceConfig{ + CPULimit: "1", + MemoryLimit: "2Gi", + CPURequest: "1", + MemoryRequest: "2Gi", + }, } scenarios := map[string]struct { @@ -107,9 +116,9 @@ func TestTensorflowDefaulter(t *testing.T) { Tensorflow: &TFServingSpec{ PredictorExtensionSpec: PredictorExtensionSpec{ RuntimeVersion: proto.String("1.14.0"), - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, - Resources: v1.ResourceRequirements{ + Resources: corev1.ResourceRequirements{ Requests: defaultResource, Limits: defaultResource, }, @@ -122,7 +131,7 @@ func TestTensorflowDefaulter(t *testing.T) { for name, scenario := range scenarios { t.Run(name, func(t *testing.T) { - scenario.spec.Tensorflow.Default(nil) + scenario.spec.Tensorflow.Default(config) if !g.Expect(scenario.spec).To(gomega.Equal(scenario.expected)) { t.Errorf("got %v, want %v", scenario.spec, scenario.expected) } @@ -142,12 +151,12 @@ func TestTFServingSpec_GetContainer(t *testing.T) { Tensorflow: &TFServingSpec{ PredictorExtensionSpec: PredictorExtensionSpec{ StorageURI: proto.String("s3://modelzoo"), - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, Image: "image:0.1", Args: nil, Env: nil, - Resources: v1.ResourceRequirements{}, + Resources: corev1.ResourceRequirements{}, }, }, }, @@ -178,11 +187,11 @@ func TestTFServingSpec_GetProtocol(t *testing.T) { Tensorflow: &TFServingSpec{ PredictorExtensionSpec: PredictorExtensionSpec{ StorageURI: proto.String("s3://modelzoo"), - Container: v1.Container{ + Container: corev1.Container{ Image: "image:0.1", Args: nil, Env: nil, - Resources: v1.ResourceRequirements{}, + Resources: corev1.ResourceRequirements{}, }, }, }, @@ -196,11 +205,11 @@ func TestTFServingSpec_GetProtocol(t *testing.T) { PredictorExtensionSpec: PredictorExtensionSpec{ ProtocolVersion: (*constants.InferenceServiceProtocol)(proto.String(string(constants.ProtocolV2))), StorageURI: proto.String("s3://modelzoo"), - Container: v1.Container{ + Container: corev1.Container{ Image: "image:0.1", Args: nil, Env: nil, - Resources: v1.ResourceRequirements{}, + Resources: corev1.ResourceRequirements{}, }, }, }, diff --git a/pkg/apis/serving/v1beta1/predictor_torchserve.go b/pkg/apis/serving/v1beta1/predictor_torchserve.go index 19dcbbc36f4..1b1442f905c 100644 --- a/pkg/apis/serving/v1beta1/predictor_torchserve.go +++ b/pkg/apis/serving/v1beta1/predictor_torchserve.go @@ -17,10 +17,10 @@ limitations under the License. package v1beta1 import ( - "fmt" + "errors" "strings" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/kserve/kserve/pkg/constants" @@ -41,9 +41,7 @@ type TorchServeSpec struct { PredictorExtensionSpec `json:",inline"` } -var ( - _ ComponentImplementation = &TorchServeSpec{} -) +var _ ComponentImplementation = &TorchServeSpec{} // Validate returns an error if invalid func (t *TorchServeSpec) Validate() error { @@ -58,11 +56,11 @@ func (t *TorchServeSpec) validateGPU() error { return nil } if utils.IsGPUEnabled(t.Resources) && !strings.Contains(*t.RuntimeVersion, PyTorchServingGPUSuffix) { - return fmt.Errorf(InvalidPyTorchRuntimeIncludesGPU) + return errors.New(InvalidPyTorchRuntimeIncludesGPU) } if !utils.IsGPUEnabled(t.Resources) && strings.Contains(*t.RuntimeVersion, PyTorchServingGPUSuffix) { - return fmt.Errorf(InvalidPyTorchRuntimeExcludesGPU) + return errors.New(InvalidPyTorchRuntimeExcludesGPU) } return nil } @@ -74,10 +72,10 @@ func (t *TorchServeSpec) Default(config *InferenceServicesConfig) { defaultProtocol := constants.ProtocolV1 t.ProtocolVersion = &defaultProtocol } - setResourceRequirementDefaults(&t.Resources) + setResourceRequirementDefaults(config, &t.Resources) } -func (t *TorchServeSpec) GetContainer(metadata metav1.ObjectMeta, extensions *ComponentExtensionSpec, config *InferenceServicesConfig, predictorHost ...string) *v1.Container { +func (t *TorchServeSpec) GetContainer(metadata metav1.ObjectMeta, extensions *ComponentExtensionSpec, config *InferenceServicesConfig, predictorHost ...string) *corev1.Container { return &t.Container } diff --git a/pkg/apis/serving/v1beta1/predictor_torchserve_test.go b/pkg/apis/serving/v1beta1/predictor_torchserve_test.go index 8d905f670d6..00cd491bef2 100644 --- a/pkg/apis/serving/v1beta1/predictor_torchserve_test.go +++ b/pkg/apis/serving/v1beta1/predictor_torchserve_test.go @@ -22,7 +22,7 @@ import ( "github.com/onsi/gomega" "github.com/onsi/gomega/types" "google.golang.org/protobuf/proto" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -60,9 +60,9 @@ func TestTorchServeValidation(t *testing.T) { PyTorch: &TorchServeSpec{ PredictorExtensionSpec: PredictorExtensionSpec{ RuntimeVersion: proto.String("0.7.0"), - Container: v1.Container{ - Resources: v1.ResourceRequirements{ - Limits: v1.ResourceList{constants.NvidiaGPUResourceType: resource.MustParse("1")}, + Container: corev1.Container{ + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{constants.NvidiaGPUResourceType: resource.MustParse("1")}, }, }, }, @@ -88,9 +88,17 @@ func TestTorchServeDefaulter(t *testing.T) { protocolV1 := constants.ProtocolV1 - defaultResource = v1.ResourceList{ - v1.ResourceMemory: resource.MustParse("2Gi"), - v1.ResourceCPU: resource.MustParse("1"), + defaultResource := corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("2Gi"), + corev1.ResourceCPU: resource.MustParse("1"), + } + config := &InferenceServicesConfig{ + Resource: ResourceConfig{ + CPULimit: "1", + MemoryLimit: "2Gi", + CPURequest: "1", + MemoryRequest: "2Gi", + }, } scenarios := map[string]struct { spec PredictorSpec @@ -106,9 +114,9 @@ func TestTorchServeDefaulter(t *testing.T) { PyTorch: &TorchServeSpec{ PredictorExtensionSpec: PredictorExtensionSpec{ ProtocolVersion: &protocolV1, - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, - Resources: v1.ResourceRequirements{ + Resources: corev1.ResourceRequirements{ Requests: defaultResource, Limits: defaultResource, }, @@ -131,9 +139,9 @@ func TestTorchServeDefaulter(t *testing.T) { PredictorExtensionSpec: PredictorExtensionSpec{ RuntimeVersion: proto.String("0.7.0"), ProtocolVersion: &protocolV1, - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, - Resources: v1.ResourceRequirements{ + Resources: corev1.ResourceRequirements{ Requests: defaultResource, Limits: defaultResource, }, @@ -146,7 +154,7 @@ func TestTorchServeDefaulter(t *testing.T) { for name, scenario := range scenarios { t.Run(name, func(t *testing.T) { - scenario.spec.PyTorch.Default(nil) + scenario.spec.PyTorch.Default(config) if !g.Expect(scenario.spec).To(gomega.Equal(scenario.expected)) { t.Errorf("got %v, want %v", scenario.spec, scenario.expected) } @@ -205,12 +213,12 @@ func TestTorchServeSpec_GetContainer(t *testing.T) { PyTorch: &TorchServeSpec{ PredictorExtensionSpec: PredictorExtensionSpec{ StorageURI: proto.String("s3://modelzoo"), - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, Image: "image:0.1", Args: nil, Env: nil, - Resources: v1.ResourceRequirements{}, + Resources: corev1.ResourceRequirements{}, }, }, }, diff --git a/pkg/apis/serving/v1beta1/predictor_triton.go b/pkg/apis/serving/v1beta1/predictor_triton.go index 7f4dd9d78a8..1dfb5ca00ab 100644 --- a/pkg/apis/serving/v1beta1/predictor_triton.go +++ b/pkg/apis/serving/v1beta1/predictor_triton.go @@ -17,9 +17,10 @@ limitations under the License. package v1beta1 import ( - "github.com/kserve/kserve/pkg/constants" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/kserve/kserve/pkg/constants" ) // TritonSpec defines arguments for configuring Triton model serving. @@ -28,17 +29,15 @@ type TritonSpec struct { PredictorExtensionSpec `json:",inline"` } -var ( - _ ComponentImplementation = &TritonSpec{} -) +var _ ComponentImplementation = &TritonSpec{} // Default sets defaults on the resource func (t *TritonSpec) Default(config *InferenceServicesConfig) { t.Container.Name = constants.InferenceServiceContainerName - setResourceRequirementDefaults(&t.Resources) + setResourceRequirementDefaults(config, &t.Resources) } -func (t *TritonSpec) GetContainer(metadata metav1.ObjectMeta, extensions *ComponentExtensionSpec, config *InferenceServicesConfig, predictorHost ...string) *v1.Container { +func (t *TritonSpec) GetContainer(metadata metav1.ObjectMeta, extensions *ComponentExtensionSpec, config *InferenceServicesConfig, predictorHost ...string) *corev1.Container { return &t.Container } diff --git a/pkg/apis/serving/v1beta1/predictor_triton_test.go b/pkg/apis/serving/v1beta1/predictor_triton_test.go index f39744f10fa..386c199e1b4 100644 --- a/pkg/apis/serving/v1beta1/predictor_triton_test.go +++ b/pkg/apis/serving/v1beta1/predictor_triton_test.go @@ -23,11 +23,12 @@ import ( "google.golang.org/protobuf/proto" - "github.com/kserve/kserve/pkg/constants" "github.com/onsi/gomega" "github.com/onsi/gomega/types" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" + + "github.com/kserve/kserve/pkg/constants" ) func TestTritonValidation(t *testing.T) { @@ -62,9 +63,17 @@ func TestTritonValidation(t *testing.T) { func TestTritonDefaulter(t *testing.T) { g := gomega.NewGomegaWithT(t) - defaultResource = v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("1"), - v1.ResourceMemory: resource.MustParse("2Gi"), + defaultResource := corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("2Gi"), + } + config := &InferenceServicesConfig{ + Resource: ResourceConfig{ + CPULimit: "1", + MemoryLimit: "2Gi", + CPURequest: "1", + MemoryRequest: "2Gi", + }, } scenarios := map[string]struct { spec PredictorSpec @@ -82,9 +91,9 @@ func TestTritonDefaulter(t *testing.T) { Triton: &TritonSpec{ PredictorExtensionSpec: PredictorExtensionSpec{ RuntimeVersion: proto.String("20.05-py3"), - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, - Resources: v1.ResourceRequirements{ + Resources: corev1.ResourceRequirements{ Requests: defaultResource, Limits: defaultResource, }, @@ -97,7 +106,7 @@ func TestTritonDefaulter(t *testing.T) { for name, scenario := range scenarios { t.Run(name, func(t *testing.T) { - scenario.spec.Triton.Default(nil) + scenario.spec.Triton.Default(config) if !g.Expect(scenario.spec).To(gomega.Equal(scenario.expected)) { t.Errorf("got %v, want %v", scenario.spec, scenario.expected) } @@ -117,12 +126,12 @@ func TestTritonSpec_GetContainer(t *testing.T) { Triton: &TritonSpec{ PredictorExtensionSpec: PredictorExtensionSpec{ StorageURI: proto.String("s3://modelzoo"), - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, Image: "image:0.1", Args: nil, Env: nil, - Resources: v1.ResourceRequirements{}, + Resources: corev1.ResourceRequirements{}, }, }, }, @@ -144,6 +153,14 @@ func TestTritonSpec_GetContainer(t *testing.T) { func TestTritonSpec_Default(t *testing.T) { g := gomega.NewGomegaWithT(t) + config := &InferenceServicesConfig{ + Resource: ResourceConfig{ + CPULimit: "1", + MemoryLimit: "2Gi", + CPURequest: "1", + MemoryRequest: "2Gi", + }, + } scenarios := map[string]struct { spec PredictorSpec expected *TritonSpec @@ -153,11 +170,11 @@ func TestTritonSpec_Default(t *testing.T) { Triton: &TritonSpec{ PredictorExtensionSpec: PredictorExtensionSpec{ StorageURI: proto.String("s3://modelzoo"), - Container: v1.Container{ + Container: corev1.Container{ Image: "image:0.1", Args: nil, Env: nil, - Resources: v1.ResourceRequirements{}, + Resources: corev1.ResourceRequirements{}, }, }, }, @@ -166,17 +183,17 @@ func TestTritonSpec_Default(t *testing.T) { expected: &TritonSpec{ PredictorExtensionSpec: PredictorExtensionSpec{ StorageURI: proto.String("s3://modelzoo"), - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, Image: "image:0.1", Args: nil, Env: nil, - Resources: v1.ResourceRequirements{ - Limits: v1.ResourceList{ + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ "cpu": resource.MustParse("1"), "memory": resource.MustParse("2Gi"), }, - Requests: v1.ResourceList{ + Requests: corev1.ResourceList{ "memory": resource.MustParse("2Gi"), "cpu": resource.MustParse("1"), }, @@ -189,7 +206,7 @@ func TestTritonSpec_Default(t *testing.T) { for name, scenario := range scenarios { t.Run(name, func(t *testing.T) { - scenario.spec.Triton.Default(nil) + scenario.spec.Triton.Default(config) if !g.Expect(scenario.spec.Triton).To(gomega.Equal(scenario.expected)) { t.Errorf("got %v, want %v", scenario.spec.Triton, scenario.expected) } @@ -209,11 +226,11 @@ func TestTritonSpec_GetProtocol(t *testing.T) { Triton: &TritonSpec{ PredictorExtensionSpec: PredictorExtensionSpec{ StorageURI: proto.String("s3://modelzoo"), - Container: v1.Container{ + Container: corev1.Container{ Image: "image:0.1", Args: nil, Env: nil, - Resources: v1.ResourceRequirements{}, + Resources: corev1.ResourceRequirements{}, }, }, }, @@ -227,11 +244,11 @@ func TestTritonSpec_GetProtocol(t *testing.T) { PredictorExtensionSpec: PredictorExtensionSpec{ ProtocolVersion: (*constants.InferenceServiceProtocol)(proto.String(string(constants.ProtocolV2))), StorageURI: proto.String("s3://modelzoo"), - Container: v1.Container{ + Container: corev1.Container{ Image: "image:0.1", Args: nil, Env: nil, - Resources: v1.ResourceRequirements{}, + Resources: corev1.ResourceRequirements{}, }, }, }, diff --git a/pkg/apis/serving/v1beta1/predictor_xgboost.go b/pkg/apis/serving/v1beta1/predictor_xgboost.go index 1172568d449..46ce379b14f 100644 --- a/pkg/apis/serving/v1beta1/predictor_xgboost.go +++ b/pkg/apis/serving/v1beta1/predictor_xgboost.go @@ -17,11 +17,10 @@ limitations under the License. package v1beta1 import ( - "strconv" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/kserve/kserve/pkg/constants" - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) // XGBoostSpec defines arguments for configuring XGBoost model serving. @@ -30,9 +29,7 @@ type XGBoostSpec struct { PredictorExtensionSpec `json:",inline"` } -var ( - _ ComponentImplementation = &XGBoostSpec{} -) +var _ ComponentImplementation = &XGBoostSpec{} // Default sets defaults on the resource func (x *XGBoostSpec) Default(config *InferenceServicesConfig) { @@ -43,71 +40,10 @@ func (x *XGBoostSpec) Default(config *InferenceServicesConfig) { x.ProtocolVersion = &defaultProtocol } - setResourceRequirementDefaults(&x.Resources) -} - -// nolint: unused -func (x *XGBoostSpec) getEnvVarsV2() []v1.EnvVar { - vars := []v1.EnvVar{ - { - Name: constants.MLServerHTTPPortEnv, - Value: strconv.Itoa(int(constants.MLServerISRestPort)), - }, - { - Name: constants.MLServerGRPCPortEnv, - Value: strconv.Itoa(int(constants.MLServerISGRPCPort)), - }, - { - Name: constants.MLServerModelsDirEnv, - Value: constants.DefaultModelLocalMountPath, - }, - } - - if x.StorageURI == nil { - vars = append( - vars, - v1.EnvVar{ - Name: constants.MLServerLoadModelsStartupEnv, - Value: strconv.FormatBool(false), - }, - ) - } - - return vars -} - -// nolint: unused -func (x *XGBoostSpec) getDefaultsV2(metadata metav1.ObjectMeta) []v1.EnvVar { - // These env vars set default parameters that can always be overridden - // individually through `model-settings.json` config files. - // These will be used as fallbacks for any missing properties and / or to run - // without a `model-settings.json` file in place. - vars := []v1.EnvVar{ - { - Name: constants.MLServerModelImplementationEnv, - Value: constants.MLServerXGBoostImplementation, - }, - } - - if x.StorageURI != nil { - // These env vars only make sense as a default for non-MMS servers - vars = append( - vars, - v1.EnvVar{ - Name: constants.MLServerModelNameEnv, - Value: metadata.Name, - }, - v1.EnvVar{ - Name: constants.MLServerModelURIEnv, - Value: constants.DefaultModelLocalMountPath, - }, - ) - } - - return vars + setResourceRequirementDefaults(config, &x.Resources) } -func (x *XGBoostSpec) GetContainer(metadata metav1.ObjectMeta, extensions *ComponentExtensionSpec, config *InferenceServicesConfig, predictorHost ...string) *v1.Container { +func (x *XGBoostSpec) GetContainer(metadata metav1.ObjectMeta, extensions *ComponentExtensionSpec, config *InferenceServicesConfig, predictorHost ...string) *corev1.Container { return &x.Container } diff --git a/pkg/apis/serving/v1beta1/predictor_xgboost_test.go b/pkg/apis/serving/v1beta1/predictor_xgboost_test.go index b8739a00735..1fe41439339 100644 --- a/pkg/apis/serving/v1beta1/predictor_xgboost_test.go +++ b/pkg/apis/serving/v1beta1/predictor_xgboost_test.go @@ -17,17 +17,17 @@ limitations under the License. package v1beta1 import ( - "strconv" "testing" "google.golang.org/protobuf/proto" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "github.com/kserve/kserve/pkg/constants" "github.com/onsi/gomega" "github.com/onsi/gomega/types" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" + + "github.com/kserve/kserve/pkg/constants" ) func TestXGBoostValidation(t *testing.T) { @@ -65,9 +65,18 @@ func TestXGBoostDefaulter(t *testing.T) { protocolV1 := constants.ProtocolV1 protocolV2 := constants.ProtocolV2 - defaultResource = v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("1"), - v1.ResourceMemory: resource.MustParse("2Gi"), + defaultResource := corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("2Gi"), + } + + config := &InferenceServicesConfig{ + Resource: ResourceConfig{ + CPULimit: "1", + MemoryLimit: "2Gi", + CPURequest: "1", + MemoryRequest: "2Gi", + }, } scenarios := map[string]struct { @@ -83,11 +92,11 @@ func TestXGBoostDefaulter(t *testing.T) { expected: PredictorSpec{ XGBoost: &XGBoostSpec{ PredictorExtensionSpec: PredictorExtensionSpec{ - //RuntimeVersion: proto.String("v0.4.0"), + // RuntimeVersion: proto.String("v0.4.0"), ProtocolVersion: &protocolV1, - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, - Resources: v1.ResourceRequirements{ + Resources: corev1.ResourceRequirements{ Requests: defaultResource, Limits: defaultResource, }, @@ -107,11 +116,11 @@ func TestXGBoostDefaulter(t *testing.T) { expected: PredictorSpec{ XGBoost: &XGBoostSpec{ PredictorExtensionSpec: PredictorExtensionSpec{ - //RuntimeVersion: proto.String("v0.1.2"), + // RuntimeVersion: proto.String("v0.1.2"), ProtocolVersion: &protocolV2, - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, - Resources: v1.ResourceRequirements{ + Resources: corev1.ResourceRequirements{ Requests: defaultResource, Limits: defaultResource, }, @@ -133,9 +142,9 @@ func TestXGBoostDefaulter(t *testing.T) { PredictorExtensionSpec: PredictorExtensionSpec{ RuntimeVersion: proto.String("v0.3.0"), ProtocolVersion: &protocolV1, - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, - Resources: v1.ResourceRequirements{ + Resources: corev1.ResourceRequirements{ Requests: defaultResource, Limits: defaultResource, }, @@ -148,7 +157,7 @@ func TestXGBoostDefaulter(t *testing.T) { for name, scenario := range scenarios { t.Run(name, func(t *testing.T) { - scenario.spec.XGBoost.Default(nil) + scenario.spec.XGBoost.Default(config) if !g.Expect(scenario.spec).To(gomega.Equal(scenario.expected)) { t.Errorf("got %v, want %v", scenario.spec, scenario.expected) } @@ -157,20 +166,19 @@ func TestXGBoostDefaulter(t *testing.T) { } func TestCreateXGBoostModelServingContainerV1(t *testing.T) { - - var requestedResource = v1.ResourceRequirements{ - Limits: v1.ResourceList{ + requestedResource := corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ "cpu": resource.MustParse("100m"), }, - Requests: v1.ResourceList{ + Requests: corev1.ResourceList{ "cpu": resource.MustParse("90m"), }, } - var config = InferenceServicesConfig{} + config := InferenceServicesConfig{} g := gomega.NewGomegaWithT(t) scenarios := map[string]struct { isvc InferenceService - expectedContainerSpec *v1.Container + expectedContainerSpec *corev1.Container }{ "ContainerSpecWithDefaultImage": { isvc: InferenceService{ @@ -183,7 +191,7 @@ func TestCreateXGBoostModelServingContainerV1(t *testing.T) { PredictorExtensionSpec: PredictorExtensionSpec{ StorageURI: proto.String("gs://someUri"), RuntimeVersion: proto.String("0.1.0"), - Container: v1.Container{ + Container: corev1.Container{ Resources: requestedResource, }, }, @@ -191,7 +199,7 @@ func TestCreateXGBoostModelServingContainerV1(t *testing.T) { }, }, }, - expectedContainerSpec: &v1.Container{ + expectedContainerSpec: &corev1.Container{ Name: constants.InferenceServiceContainerName, Resources: requestedResource, }, @@ -206,7 +214,7 @@ func TestCreateXGBoostModelServingContainerV1(t *testing.T) { XGBoost: &XGBoostSpec{ PredictorExtensionSpec: PredictorExtensionSpec{ StorageURI: proto.String("gs://someUri"), - Container: v1.Container{ + Container: corev1.Container{ Image: "customImage:0.1.0", Resources: requestedResource, }, @@ -215,7 +223,7 @@ func TestCreateXGBoostModelServingContainerV1(t *testing.T) { }, }, }, - expectedContainerSpec: &v1.Container{ + expectedContainerSpec: &corev1.Container{ Image: "customImage:0.1.0", Name: constants.InferenceServiceContainerName, Resources: requestedResource, @@ -235,7 +243,7 @@ func TestCreateXGBoostModelServingContainerV1(t *testing.T) { PredictorExtensionSpec: PredictorExtensionSpec{ StorageURI: proto.String("gs://someUri"), RuntimeVersion: proto.String("0.1.0"), - Container: v1.Container{ + Container: corev1.Container{ Resources: requestedResource, }, }, @@ -243,7 +251,7 @@ func TestCreateXGBoostModelServingContainerV1(t *testing.T) { }, }, }, - expectedContainerSpec: &v1.Container{ + expectedContainerSpec: &corev1.Container{ Name: constants.InferenceServiceContainerName, Resources: requestedResource, }, @@ -262,7 +270,7 @@ func TestCreateXGBoostModelServingContainerV1(t *testing.T) { PredictorExtensionSpec: PredictorExtensionSpec{ StorageURI: proto.String("gs://someUri"), RuntimeVersion: proto.String("0.1.0"), - Container: v1.Container{ + Container: corev1.Container{ Resources: requestedResource, Args: []string{ constants.ArgumentWorkers + "=1", @@ -273,7 +281,7 @@ func TestCreateXGBoostModelServingContainerV1(t *testing.T) { }, }, }, - expectedContainerSpec: &v1.Container{ + expectedContainerSpec: &corev1.Container{ Name: constants.InferenceServiceContainerName, Resources: requestedResource, Args: []string{ @@ -297,23 +305,23 @@ func TestCreateXGBoostModelServingContainerV1(t *testing.T) { func TestCreateXGBoostModelServingContainerV2(t *testing.T) { protocolV2 := constants.ProtocolV2 - var requestedResource = v1.ResourceRequirements{ - Limits: v1.ResourceList{ + requestedResource := corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ "cpu": resource.Quantity{ Format: "100", }, }, - Requests: v1.ResourceList{ + Requests: corev1.ResourceList{ "cpu": resource.Quantity{ Format: "90", }, }, } - var config = InferenceServicesConfig{} + config := InferenceServicesConfig{} g := gomega.NewGomegaWithT(t) scenarios := map[string]struct { isvc InferenceService - expectedContainerSpec *v1.Container + expectedContainerSpec *corev1.Container }{ "ContainerSpecWithDefaultImage": { isvc: InferenceService{ @@ -327,7 +335,7 @@ func TestCreateXGBoostModelServingContainerV2(t *testing.T) { StorageURI: proto.String("gs://someUri"), RuntimeVersion: proto.String("0.1.0"), ProtocolVersion: &protocolV2, - Container: v1.Container{ + Container: corev1.Container{ Resources: requestedResource, }, }, @@ -335,7 +343,7 @@ func TestCreateXGBoostModelServingContainerV2(t *testing.T) { }, }, }, - expectedContainerSpec: &v1.Container{ + expectedContainerSpec: &corev1.Container{ Name: constants.InferenceServiceContainerName, Resources: requestedResource, }, @@ -351,7 +359,7 @@ func TestCreateXGBoostModelServingContainerV2(t *testing.T) { PredictorExtensionSpec: PredictorExtensionSpec{ StorageURI: proto.String("gs://someUri"), ProtocolVersion: &protocolV2, - Container: v1.Container{ + Container: corev1.Container{ Image: "customImage:0.1.0", Resources: requestedResource, }, @@ -360,7 +368,7 @@ func TestCreateXGBoostModelServingContainerV2(t *testing.T) { }, }, }, - expectedContainerSpec: &v1.Container{ + expectedContainerSpec: &corev1.Container{ Image: "customImage:0.1.0", Name: constants.InferenceServiceContainerName, Resources: requestedResource, @@ -376,7 +384,7 @@ func TestCreateXGBoostModelServingContainerV2(t *testing.T) { XGBoost: &XGBoostSpec{ PredictorExtensionSpec: PredictorExtensionSpec{ ProtocolVersion: &protocolV2, - Container: v1.Container{ + Container: corev1.Container{ Resources: requestedResource, }, }, @@ -384,7 +392,7 @@ func TestCreateXGBoostModelServingContainerV2(t *testing.T) { }, }, }, - expectedContainerSpec: &v1.Container{ + expectedContainerSpec: &corev1.Container{ Name: constants.InferenceServiceContainerName, Resources: requestedResource, }, @@ -432,128 +440,3 @@ func TestXGBoostGetProtocol(t *testing.T) { g.Expect(protocol).To(scenario.matcher) } } - -func TestXGBoostSpec_GetEnvVarsV2(t *testing.T) { - g := gomega.NewGomegaWithT(t) - - scenarios := map[string]struct { - spec PredictorSpec - matcher types.GomegaMatcher - }{ - "storage uri is nil": { - spec: PredictorSpec{ - XGBoost: &XGBoostSpec{ - PredictorExtensionSpec: PredictorExtensionSpec{}, - }, - }, - matcher: gomega.Equal([]v1.EnvVar{ - { - Name: constants.MLServerHTTPPortEnv, - Value: strconv.Itoa(int(constants.MLServerISRestPort)), - }, - { - Name: constants.MLServerGRPCPortEnv, - Value: strconv.Itoa(int(constants.MLServerISGRPCPort)), - }, - { - Name: constants.MLServerModelsDirEnv, - Value: constants.DefaultModelLocalMountPath, - }, - { - Name: constants.MLServerLoadModelsStartupEnv, - Value: strconv.FormatBool(false), - }, - }), - }, - "storage uri is not nil": { - spec: PredictorSpec{ - XGBoost: &XGBoostSpec{ - PredictorExtensionSpec: PredictorExtensionSpec{ - StorageURI: proto.String("gs://kserve/model"), - }, - }, - }, - matcher: gomega.Equal([]v1.EnvVar{ - { - Name: constants.MLServerHTTPPortEnv, - Value: strconv.Itoa(int(constants.MLServerISRestPort)), - }, - { - Name: constants.MLServerGRPCPortEnv, - Value: strconv.Itoa(int(constants.MLServerISGRPCPort)), - }, - { - Name: constants.MLServerModelsDirEnv, - Value: constants.DefaultModelLocalMountPath, - }, - }), - }, - } - - for name, scenario := range scenarios { - t.Run(name, func(t *testing.T) { - res := scenario.spec.XGBoost.getEnvVarsV2() - if !g.Expect(res).To(scenario.matcher) { - t.Errorf("got %q, want %q", res, scenario.matcher) - } - }) - } -} - -func TestXGBoostSpec_GetDefaultsV2(t *testing.T) { - g := gomega.NewGomegaWithT(t) - - metadata := metav1.ObjectMeta{ - Name: "test", - } - scenarios := map[string]struct { - spec PredictorSpec - matcher types.GomegaMatcher - }{ - "storage uri is nil": { - spec: PredictorSpec{ - XGBoost: &XGBoostSpec{ - PredictorExtensionSpec: PredictorExtensionSpec{}, - }, - }, - matcher: gomega.Equal([]v1.EnvVar{ - { - Name: constants.MLServerModelImplementationEnv, - Value: constants.MLServerXGBoostImplementation, - }, - }), - }, - "storage uri is not nil": { - spec: PredictorSpec{ - XGBoost: &XGBoostSpec{ - PredictorExtensionSpec: PredictorExtensionSpec{ - StorageURI: proto.String("gs://kserve/model"), - }, - }, - }, - matcher: gomega.Equal([]v1.EnvVar{ - { - Name: constants.MLServerModelImplementationEnv, - Value: constants.MLServerXGBoostImplementation, - }, - { - Name: constants.MLServerModelNameEnv, - Value: metadata.Name, - }, - { - Name: constants.MLServerModelURIEnv, - Value: constants.DefaultModelLocalMountPath, - }, - }), - }, - } - - for name, scenario := range scenarios { - t.Run(name, func(t *testing.T) { - res := scenario.spec.XGBoost.getDefaultsV2(metadata) - if !g.Expect(res).To(scenario.matcher) { - t.Errorf("got %q, want %q", res, scenario.matcher) - } - }) - } -} diff --git a/pkg/apis/serving/v1beta1/transformer_custom.go b/pkg/apis/serving/v1beta1/transformer_custom.go index 0dbe242b3ec..70850193eb6 100644 --- a/pkg/apis/serving/v1beta1/transformer_custom.go +++ b/pkg/apis/serving/v1beta1/transformer_custom.go @@ -20,27 +20,26 @@ import ( "fmt" "strconv" - "github.com/kserve/kserve/pkg/constants" - "github.com/kserve/kserve/pkg/utils" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" logf "sigs.k8s.io/controller-runtime/pkg/log" + + "github.com/kserve/kserve/pkg/constants" + "github.com/kserve/kserve/pkg/utils" ) // CustomTransformer defines arguments for configuring a custom transformer. type CustomTransformer struct { - v1.PodSpec `json:",inline"` + corev1.PodSpec `json:",inline"` } -var ( - // logger for the custom transformer - customTransformerLogger = logf.Log.WithName("inferenceservice-v1beta1-custom-transformer") -) +// logger for the custom transformer +var customTransformerLogger = logf.Log.WithName("inferenceservice-v1beta1-custom-transformer") var _ ComponentImplementation = &CustomTransformer{} func NewCustomTransformer(podSpec *PodSpec) *CustomTransformer { - return &CustomTransformer{PodSpec: v1.PodSpec(*podSpec)} + return &CustomTransformer{PodSpec: corev1.PodSpec(*podSpec)} } // Validate returns an error if invalid @@ -51,10 +50,10 @@ func (c *CustomTransformer) Validate() error { // Default sets defaults on the resource func (c *CustomTransformer) Default(config *InferenceServicesConfig) { if len(c.Containers) == 0 { - c.Containers = append(c.Containers, v1.Container{}) + c.Containers = append(c.Containers, corev1.Container{}) } c.Containers[0].Name = constants.InferenceServiceContainerName - setResourceRequirementDefaults(&c.Containers[0].Resources) + setResourceRequirementDefaults(config, &c.Containers[0].Resources) } func (c *CustomTransformer) GetStorageUri() *string { @@ -73,7 +72,8 @@ func (c *CustomTransformer) GetStorageSpec() *StorageSpec { // GetContainer transforms the resource into a container spec func (c *CustomTransformer) GetContainer(metadata metav1.ObjectMeta, extensions *ComponentExtensionSpec, config *InferenceServicesConfig, - predictorHost ...string) *v1.Container { + predictorHost ...string, +) *corev1.Container { container := &c.Containers[0] argumentPredictorHost := fmt.Sprintf("%s.%s", predictorHost[0], metadata.Namespace) deploymentMode, ok := metadata.Annotations[constants.DeploymentMode] diff --git a/pkg/apis/serving/v1beta1/transformer_custom_test.go b/pkg/apis/serving/v1beta1/transformer_custom_test.go index b2a6ac42850..854a52d116a 100644 --- a/pkg/apis/serving/v1beta1/transformer_custom_test.go +++ b/pkg/apis/serving/v1beta1/transformer_custom_test.go @@ -20,20 +20,29 @@ import ( "fmt" "testing" - "github.com/kserve/kserve/pkg/constants" "github.com/onsi/gomega" "github.com/onsi/gomega/types" "google.golang.org/protobuf/proto" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/kserve/kserve/pkg/constants" ) func TestTransformerDefaulter(t *testing.T) { g := gomega.NewGomegaWithT(t) - defaultResource = v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("1"), - v1.ResourceMemory: resource.MustParse("2Gi"), + defaultResource := corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("2Gi"), + } + config := &InferenceServicesConfig{ + Resource: ResourceConfig{ + CPULimit: "1", + MemoryLimit: "2Gi", + CPURequest: "1", + MemoryRequest: "2Gi", + }, } scenarios := map[string]struct { spec TransformerSpec @@ -42,9 +51,9 @@ func TestTransformerDefaulter(t *testing.T) { "DefaultResources": { spec: TransformerSpec{ PodSpec: PodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "STORAGE_URI", Value: "hdfs://modelzoo", @@ -56,16 +65,16 @@ func TestTransformerDefaulter(t *testing.T) { }, expected: TransformerSpec{ PodSpec: PodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "STORAGE_URI", Value: "hdfs://modelzoo", }, }, - Resources: v1.ResourceRequirements{ + Resources: corev1.ResourceRequirements{ Requests: defaultResource, Limits: defaultResource, }, @@ -79,7 +88,7 @@ func TestTransformerDefaulter(t *testing.T) { for name, scenario := range scenarios { t.Run(name, func(t *testing.T) { CustomTransformer := NewCustomTransformer(&scenario.spec.PodSpec) - CustomTransformer.Default(nil) + CustomTransformer.Default(config) if !g.Expect(scenario.spec).To(gomega.Equal(scenario.expected)) { t.Errorf("got %v, want %v", scenario.spec, scenario.expected) } @@ -88,15 +97,14 @@ func TestTransformerDefaulter(t *testing.T) { } func TestCreateTransformerContainer(t *testing.T) { - - var requestedResource = v1.ResourceRequirements{ - Limits: v1.ResourceList{ + requestedResource := corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ "cpu": resource.Quantity{ Format: "100", }, "memory": resource.MustParse("1Gi"), }, - Requests: v1.ResourceList{ + Requests: corev1.ResourceList{ "cpu": resource.Quantity{ Format: "90", }, @@ -106,7 +114,7 @@ func TestCreateTransformerContainer(t *testing.T) { g := gomega.NewGomegaWithT(t) scenarios := map[string]struct { isvc InferenceService - expectedContainerSpec *v1.Container + expectedContainerSpec *corev1.Container }{ "ContainerSpecWithCustomImage": { isvc: InferenceService{ @@ -118,7 +126,7 @@ func TestCreateTransformerContainer(t *testing.T) { SKLearn: &SKLearnSpec{ PredictorExtensionSpec: PredictorExtensionSpec{ StorageURI: proto.String("gs://someUri"), - Container: v1.Container{ + Container: corev1.Container{ Image: "customImage:0.1.0", Resources: requestedResource, }, @@ -127,10 +135,10 @@ func TestCreateTransformerContainer(t *testing.T) { }, Transformer: &TransformerSpec{ PodSpec: PodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Image: "transformer:0.1.0", - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "STORAGE_URI", Value: "hdfs://modelzoo", @@ -143,7 +151,7 @@ func TestCreateTransformerContainer(t *testing.T) { }, }, }, - expectedContainerSpec: &v1.Container{ + expectedContainerSpec: &corev1.Container{ Image: "transformer:0.1.0", Name: constants.InferenceServiceContainerName, Resources: requestedResource, @@ -155,7 +163,7 @@ func TestCreateTransformerContainer(t *testing.T) { "--http_port", "8080", }, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "STORAGE_URI", Value: "hdfs://modelzoo", @@ -176,7 +184,7 @@ func TestCreateTransformerContainer(t *testing.T) { SKLearn: &SKLearnSpec{ PredictorExtensionSpec: PredictorExtensionSpec{ StorageURI: proto.String("gs://someUri"), - Container: v1.Container{ + Container: corev1.Container{ Resources: requestedResource, }, }, @@ -187,10 +195,10 @@ func TestCreateTransformerContainer(t *testing.T) { ContainerConcurrency: proto.Int64(2), }, PodSpec: PodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Image: "transformer:0.1.0", - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "STORAGE_URI", Value: "hdfs://modelzoo", @@ -203,7 +211,7 @@ func TestCreateTransformerContainer(t *testing.T) { }, }, }, - expectedContainerSpec: &v1.Container{ + expectedContainerSpec: &corev1.Container{ Image: "transformer:0.1.0", Name: constants.InferenceServiceContainerName, Resources: requestedResource, @@ -217,7 +225,7 @@ func TestCreateTransformerContainer(t *testing.T) { "--workers", "2", }, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "STORAGE_URI", Value: "hdfs://modelzoo", @@ -238,7 +246,7 @@ func TestCreateTransformerContainer(t *testing.T) { SKLearn: &SKLearnSpec{ PredictorExtensionSpec: PredictorExtensionSpec{ StorageURI: proto.String("gs://someUri"), - Container: v1.Container{ + Container: corev1.Container{ Resources: requestedResource, Args: []string{ "--workers", @@ -253,10 +261,10 @@ func TestCreateTransformerContainer(t *testing.T) { ContainerConcurrency: proto.Int64(2), }, PodSpec: PodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Image: "transformer:0.1.0", - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "STORAGE_URI", Value: "hdfs://modelzoo", @@ -279,7 +287,7 @@ func TestCreateTransformerContainer(t *testing.T) { }, }, }, - expectedContainerSpec: &v1.Container{ + expectedContainerSpec: &corev1.Container{ Image: "transformer:0.1.0", Name: constants.InferenceServiceContainerName, Resources: requestedResource, @@ -293,7 +301,7 @@ func TestCreateTransformerContainer(t *testing.T) { "--workers", "1", }, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "STORAGE_URI", Value: "hdfs://modelzoo", @@ -323,11 +331,11 @@ func TestTransformerGetProtocol(t *testing.T) { }{ "DefaultProtocol": { spec: &CustomTransformer{ - PodSpec: v1.PodSpec{ - Containers: []v1.Container{ + PodSpec: corev1.PodSpec{ + Containers: []corev1.Container{ { Image: "transformer:0.1.0", - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "STORAGE_URI", Value: "hdfs://modelzoo", @@ -352,11 +360,11 @@ func TestTransformerGetProtocol(t *testing.T) { }, "ProtocolSpecified": { spec: &CustomTransformer{ - PodSpec: v1.PodSpec{ - Containers: []v1.Container{ + PodSpec: corev1.PodSpec{ + Containers: []corev1.Container{ { Image: "transformer:0.1.0", - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "STORAGE_URI", Value: "hdfs://modelzoo", diff --git a/pkg/apis/serving/v1beta1/v1beta1.go b/pkg/apis/serving/v1beta1/v1beta1.go index bc9c45d9fbc..f3d5f214cbc 100644 --- a/pkg/apis/serving/v1beta1/v1beta1.go +++ b/pkg/apis/serving/v1beta1/v1beta1.go @@ -24,9 +24,10 @@ limitations under the License. package v1beta1 import ( - "github.com/kserve/kserve/pkg/constants" "k8s.io/apimachinery/pkg/runtime/schema" "sigs.k8s.io/controller-runtime/pkg/scheme" + + "github.com/kserve/kserve/pkg/constants" ) var ( diff --git a/pkg/apis/serving/v1beta1/zz_generated.deepcopy.go b/pkg/apis/serving/v1beta1/zz_generated.deepcopy.go index 1957a4009b9..60f1d864038 100644 --- a/pkg/apis/serving/v1beta1/zz_generated.deepcopy.go +++ b/pkg/apis/serving/v1beta1/zz_generated.deepcopy.go @@ -81,12 +81,12 @@ func (in *ComponentExtensionSpec) DeepCopyInto(out *ComponentExtensionSpec) { *out = *in if in.MinReplicas != nil { in, out := &in.MinReplicas, &out.MinReplicas - *out = new(int) + *out = new(int32) **out = **in } if in.ScaleTarget != nil { in, out := &in.ScaleTarget, &out.ScaleTarget - *out = new(int) + *out = new(int32) **out = **in } if in.ScaleMetric != nil { @@ -798,6 +798,11 @@ func (in *PodSpec) DeepCopyInto(out *PodSpec) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.Resources != nil { + in, out := &in.Resources, &out.Resources + *out = new(corev1.ResourceRequirements) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodSpec. diff --git a/pkg/apis/serving/v1beta1/zz_generated.defaults.go b/pkg/apis/serving/v1beta1/zz_generated.defaults.go index 54778cd8686..be1f3efa0b4 100644 --- a/pkg/apis/serving/v1beta1/zz_generated.defaults.go +++ b/pkg/apis/serving/v1beta1/zz_generated.defaults.go @@ -22,6 +22,7 @@ limitations under the License. package v1beta1 import ( + v1 "k8s.io/api/core/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) @@ -388,6 +389,51 @@ func SetObjectDefaults_InferenceService(in *InferenceService) { } } if in.Spec.Predictor.WorkerSpec != nil { + for i := range in.Spec.Predictor.WorkerSpec.PodSpec.Volumes { + a := &in.Spec.Predictor.WorkerSpec.PodSpec.Volumes[i] + if a.VolumeSource.ISCSI != nil { + if a.VolumeSource.ISCSI.ISCSIInterface == "" { + a.VolumeSource.ISCSI.ISCSIInterface = "default" + } + } + if a.VolumeSource.RBD != nil { + if a.VolumeSource.RBD.RBDPool == "" { + a.VolumeSource.RBD.RBDPool = "rbd" + } + if a.VolumeSource.RBD.RadosUser == "" { + a.VolumeSource.RBD.RadosUser = "admin" + } + if a.VolumeSource.RBD.Keyring == "" { + a.VolumeSource.RBD.Keyring = "/etc/ceph/keyring" + } + } + if a.VolumeSource.AzureDisk != nil { + if a.VolumeSource.AzureDisk.CachingMode == nil { + ptrVar1 := v1.AzureDataDiskCachingMode(v1.AzureDataDiskCachingReadWrite) + a.VolumeSource.AzureDisk.CachingMode = &ptrVar1 + } + if a.VolumeSource.AzureDisk.FSType == nil { + var ptrVar1 string = "ext4" + a.VolumeSource.AzureDisk.FSType = &ptrVar1 + } + if a.VolumeSource.AzureDisk.ReadOnly == nil { + var ptrVar1 bool = false + a.VolumeSource.AzureDisk.ReadOnly = &ptrVar1 + } + if a.VolumeSource.AzureDisk.Kind == nil { + ptrVar1 := v1.AzureDataDiskKind(v1.AzureSharedBlobDisk) + a.VolumeSource.AzureDisk.Kind = &ptrVar1 + } + } + if a.VolumeSource.ScaleIO != nil { + if a.VolumeSource.ScaleIO.StorageMode == "" { + a.VolumeSource.ScaleIO.StorageMode = "ThinProvisioned" + } + if a.VolumeSource.ScaleIO.FSType == "" { + a.VolumeSource.ScaleIO.FSType = "xfs" + } + } + } for i := range in.Spec.Predictor.WorkerSpec.PodSpec.InitContainers { a := &in.Spec.Predictor.WorkerSpec.PodSpec.InitContainers[i] for j := range a.Ports { @@ -488,6 +534,51 @@ func SetObjectDefaults_InferenceService(in *InferenceService) { } } } + for i := range in.Spec.Predictor.PodSpec.Volumes { + a := &in.Spec.Predictor.PodSpec.Volumes[i] + if a.VolumeSource.ISCSI != nil { + if a.VolumeSource.ISCSI.ISCSIInterface == "" { + a.VolumeSource.ISCSI.ISCSIInterface = "default" + } + } + if a.VolumeSource.RBD != nil { + if a.VolumeSource.RBD.RBDPool == "" { + a.VolumeSource.RBD.RBDPool = "rbd" + } + if a.VolumeSource.RBD.RadosUser == "" { + a.VolumeSource.RBD.RadosUser = "admin" + } + if a.VolumeSource.RBD.Keyring == "" { + a.VolumeSource.RBD.Keyring = "/etc/ceph/keyring" + } + } + if a.VolumeSource.AzureDisk != nil { + if a.VolumeSource.AzureDisk.CachingMode == nil { + ptrVar1 := v1.AzureDataDiskCachingMode(v1.AzureDataDiskCachingReadWrite) + a.VolumeSource.AzureDisk.CachingMode = &ptrVar1 + } + if a.VolumeSource.AzureDisk.FSType == nil { + var ptrVar1 string = "ext4" + a.VolumeSource.AzureDisk.FSType = &ptrVar1 + } + if a.VolumeSource.AzureDisk.ReadOnly == nil { + var ptrVar1 bool = false + a.VolumeSource.AzureDisk.ReadOnly = &ptrVar1 + } + if a.VolumeSource.AzureDisk.Kind == nil { + ptrVar1 := v1.AzureDataDiskKind(v1.AzureSharedBlobDisk) + a.VolumeSource.AzureDisk.Kind = &ptrVar1 + } + } + if a.VolumeSource.ScaleIO != nil { + if a.VolumeSource.ScaleIO.StorageMode == "" { + a.VolumeSource.ScaleIO.StorageMode = "ThinProvisioned" + } + if a.VolumeSource.ScaleIO.FSType == "" { + a.VolumeSource.ScaleIO.FSType = "xfs" + } + } + } for i := range in.Spec.Predictor.PodSpec.InitContainers { a := &in.Spec.Predictor.PodSpec.InitContainers[i] for j := range a.Ports { @@ -620,6 +711,51 @@ func SetObjectDefaults_InferenceService(in *InferenceService) { } } } + for i := range in.Spec.Explainer.PodSpec.Volumes { + a := &in.Spec.Explainer.PodSpec.Volumes[i] + if a.VolumeSource.ISCSI != nil { + if a.VolumeSource.ISCSI.ISCSIInterface == "" { + a.VolumeSource.ISCSI.ISCSIInterface = "default" + } + } + if a.VolumeSource.RBD != nil { + if a.VolumeSource.RBD.RBDPool == "" { + a.VolumeSource.RBD.RBDPool = "rbd" + } + if a.VolumeSource.RBD.RadosUser == "" { + a.VolumeSource.RBD.RadosUser = "admin" + } + if a.VolumeSource.RBD.Keyring == "" { + a.VolumeSource.RBD.Keyring = "/etc/ceph/keyring" + } + } + if a.VolumeSource.AzureDisk != nil { + if a.VolumeSource.AzureDisk.CachingMode == nil { + ptrVar1 := v1.AzureDataDiskCachingMode(v1.AzureDataDiskCachingReadWrite) + a.VolumeSource.AzureDisk.CachingMode = &ptrVar1 + } + if a.VolumeSource.AzureDisk.FSType == nil { + var ptrVar1 string = "ext4" + a.VolumeSource.AzureDisk.FSType = &ptrVar1 + } + if a.VolumeSource.AzureDisk.ReadOnly == nil { + var ptrVar1 bool = false + a.VolumeSource.AzureDisk.ReadOnly = &ptrVar1 + } + if a.VolumeSource.AzureDisk.Kind == nil { + ptrVar1 := v1.AzureDataDiskKind(v1.AzureSharedBlobDisk) + a.VolumeSource.AzureDisk.Kind = &ptrVar1 + } + } + if a.VolumeSource.ScaleIO != nil { + if a.VolumeSource.ScaleIO.StorageMode == "" { + a.VolumeSource.ScaleIO.StorageMode = "ThinProvisioned" + } + if a.VolumeSource.ScaleIO.FSType == "" { + a.VolumeSource.ScaleIO.FSType = "xfs" + } + } + } for i := range in.Spec.Explainer.PodSpec.InitContainers { a := &in.Spec.Explainer.PodSpec.InitContainers[i] for j := range a.Ports { @@ -721,6 +857,51 @@ func SetObjectDefaults_InferenceService(in *InferenceService) { } } if in.Spec.Transformer != nil { + for i := range in.Spec.Transformer.PodSpec.Volumes { + a := &in.Spec.Transformer.PodSpec.Volumes[i] + if a.VolumeSource.ISCSI != nil { + if a.VolumeSource.ISCSI.ISCSIInterface == "" { + a.VolumeSource.ISCSI.ISCSIInterface = "default" + } + } + if a.VolumeSource.RBD != nil { + if a.VolumeSource.RBD.RBDPool == "" { + a.VolumeSource.RBD.RBDPool = "rbd" + } + if a.VolumeSource.RBD.RadosUser == "" { + a.VolumeSource.RBD.RadosUser = "admin" + } + if a.VolumeSource.RBD.Keyring == "" { + a.VolumeSource.RBD.Keyring = "/etc/ceph/keyring" + } + } + if a.VolumeSource.AzureDisk != nil { + if a.VolumeSource.AzureDisk.CachingMode == nil { + ptrVar1 := v1.AzureDataDiskCachingMode(v1.AzureDataDiskCachingReadWrite) + a.VolumeSource.AzureDisk.CachingMode = &ptrVar1 + } + if a.VolumeSource.AzureDisk.FSType == nil { + var ptrVar1 string = "ext4" + a.VolumeSource.AzureDisk.FSType = &ptrVar1 + } + if a.VolumeSource.AzureDisk.ReadOnly == nil { + var ptrVar1 bool = false + a.VolumeSource.AzureDisk.ReadOnly = &ptrVar1 + } + if a.VolumeSource.AzureDisk.Kind == nil { + ptrVar1 := v1.AzureDataDiskKind(v1.AzureSharedBlobDisk) + a.VolumeSource.AzureDisk.Kind = &ptrVar1 + } + } + if a.VolumeSource.ScaleIO != nil { + if a.VolumeSource.ScaleIO.StorageMode == "" { + a.VolumeSource.ScaleIO.StorageMode = "ThinProvisioned" + } + if a.VolumeSource.ScaleIO.FSType == "" { + a.VolumeSource.ScaleIO.FSType = "xfs" + } + } + } for i := range in.Spec.Transformer.PodSpec.InitContainers { a := &in.Spec.Transformer.PodSpec.InitContainers[i] for j := range a.Ports { diff --git a/pkg/batcher/handler.go b/pkg/batcher/handler.go index 8a525ee459a..de32e13f32b 100644 --- a/pkg/batcher/handler.go +++ b/pkg/batcher/handler.go @@ -101,7 +101,7 @@ func (handler *BatchHandler) batchPredict() { handler.batcherInfo.Instances, }) reader := bytes.NewReader(jsonStr) - r := httptest.NewRequest("POST", handler.batcherInfo.Path, reader) + r := httptest.NewRequest(http.MethodPost, handler.batcherInfo.Path, reader) rr := httptest.NewRecorder() handler.next.ServeHTTP(rr, r) responseBody := rr.Body.Bytes() @@ -166,8 +166,8 @@ func (handler *BatchHandler) batch() { handler.batcherInfo.Path = req.Path handler.batcherInfo.CurrentInputLen = len(handler.batcherInfo.Instances) handler.batcherInfo.Instances = append(handler.batcherInfo.Instances, *req.Instances...) - var index = make([]int, 0) - for i := 0; i < len(*req.Instances); i++ { + index := make([]int, 0) + for i := range len(*req.Instances) { index = append(index, handler.batcherInfo.CurrentInputLen+i) } handler.batcherInfo.ContextMap[req.ContextInput] = InputInfo{ @@ -221,7 +221,7 @@ func New(maxBatchSize int, maxLatency int, handler http.Handler, logger *zap.Sug func (handler *BatchHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { // only batch predict requests - var predictVerb = regexp.MustCompile(`:predict$`) + predictVerb := regexp.MustCompile(`:predict$`) if !predictVerb.MatchString(r.URL.Path) { handler.next.ServeHTTP(w, r) return @@ -243,8 +243,8 @@ func (handler *BatchHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { return } handler.log.Infof("serving request %s", r.URL.Path) - var ctx = context.Background() - var chl = make(chan Response) + ctx := context.Background() + chl := make(chan Response) handler.channelIn <- Input{ &ctx, r.URL.Path, diff --git a/pkg/batcher/handler_test.go b/pkg/batcher/handler_test.go index 8a580bd525c..cc78c82cd39 100644 --- a/pkg/batcher/handler_test.go +++ b/pkg/batcher/handler_test.go @@ -20,15 +20,16 @@ import ( "bytes" "encoding/json" "fmt" - "github.com/onsi/gomega" "io" - pkglogging "knative.dev/pkg/logging" "net/http" "net/http/httptest" "net/http/httputil" "net/url" "sync" "testing" + + "github.com/onsi/gomega" + pkglogging "knative.dev/pkg/logging" ) func serveRequest(batchHandler *BatchHandler, wg *sync.WaitGroup, index int) { @@ -37,14 +38,15 @@ func serveRequest(batchHandler *BatchHandler, wg *sync.WaitGroup, index int) { predictorRequest := []byte(instances) reader := bytes.NewReader(predictorRequest) path := "/v1/models/test:predict" - r := httptest.NewRequest("POST", path, reader) + r := httptest.NewRequest(http.MethodPost, path, reader) w := httptest.NewRecorder() batchHandler.ServeHTTP(w, r) - b2, _ := io.ReadAll(w.Result().Body) + resp := w.Result() + defer resp.Body.Close() + b2, _ := io.ReadAll(resp.Body) var res Response _ = json.Unmarshal(b2, &res) - fmt.Printf("Got response %v\n", res) } func TestBatcher(t *testing.T) { @@ -56,33 +58,33 @@ func TestBatcher(t *testing.T) { // Start a local HTTP server predictor := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { b, err := io.ReadAll(req.Body) - g.Expect(err).To(gomega.BeNil()) + g.Expect(err).ToNot(gomega.HaveOccurred()) var request Request err = json.Unmarshal(b, &request) - g.Expect(err).To(gomega.BeNil()) + g.Expect(err).ToNot(gomega.HaveOccurred()) logger.Infof("Get request %v", string(b)) response := Response{ Predictions: request.Instances, } responseChan <- response responseBytes, err := json.Marshal(response) - g.Expect(err).To(gomega.BeNil()) + g.Expect(err).ToNot(gomega.HaveOccurred()) _, err = rw.Write(responseBytes) - g.Expect(err).To(gomega.BeNil()) + g.Expect(err).ToNot(gomega.HaveOccurred()) })) // Close the server when test finishes defer predictor.Close() predictorSvcUrl, err := url.Parse(predictor.URL) logger.Infof("predictor url %s", predictorSvcUrl) - g.Expect(err).To(gomega.BeNil()) + g.Expect(err).ToNot(gomega.HaveOccurred()) httpProxy := httputil.NewSingleHostReverseProxy(predictorSvcUrl) batchHandler := New(32, 50, httpProxy, logger) var wg sync.WaitGroup - for i := 0; i < 10; i++ { + for i := range 10 { wg.Add(1) go serveRequest(batchHandler, &wg, i) } - //var responseBytes []byte + // var responseBytes []byte <-responseChan wg.Wait() } @@ -97,32 +99,32 @@ func TestBatcherFail(t *testing.T) { // Start a local HTTP server predictor := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { b, err := io.ReadAll(req.Body) - g.Expect(err).To(gomega.BeNil()) + g.Expect(err).ToNot(gomega.HaveOccurred()) var request Request err = json.Unmarshal(b, &request) - g.Expect(err).To(gomega.BeNil()) + g.Expect(err).ToNot(gomega.HaveOccurred()) logger.Infof("Get request %v", string(b)) response := Response{} responseChan <- response responseBytes, err := json.Marshal(response) - g.Expect(err).To(gomega.BeNil()) - rw.WriteHeader(500) + g.Expect(err).ToNot(gomega.HaveOccurred()) + rw.WriteHeader(http.StatusInternalServerError) _, err = rw.Write(responseBytes) - g.Expect(err).To(gomega.BeNil()) + g.Expect(err).ToNot(gomega.HaveOccurred()) })) // Close the server when test finishes defer predictor.Close() predictorSvcUrl, err := url.Parse(predictor.URL) logger.Infof("predictor url %s", predictorSvcUrl) - g.Expect(err).To(gomega.BeNil()) + g.Expect(err).ToNot(gomega.HaveOccurred()) httpProxy := httputil.NewSingleHostReverseProxy(predictorSvcUrl) batchHandler := New(32, 50, httpProxy, logger) var wg sync.WaitGroup - for i := 0; i < 10; i++ { + for i := range 10 { wg.Add(1) go serveRequest(batchHandler, &wg, i) } - //var responseBytes []byte + // var responseBytes []byte <-responseChan wg.Wait() } @@ -137,34 +139,34 @@ func TestBatcherDefaults(t *testing.T) { // Start a local HTTP server predictor := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { b, err := io.ReadAll(req.Body) - g.Expect(err).To(gomega.BeNil()) + g.Expect(err).ToNot(gomega.HaveOccurred()) var request Request err = json.Unmarshal(b, &request) - g.Expect(err).To(gomega.BeNil()) + g.Expect(err).ToNot(gomega.HaveOccurred()) logger.Infof("Get request %v", string(b)) response := Response{ Predictions: request.Instances, } responseChan <- response responseBytes, err := json.Marshal(response) - g.Expect(err).To(gomega.BeNil()) - rw.WriteHeader(500) + g.Expect(err).ToNot(gomega.HaveOccurred()) + rw.WriteHeader(http.StatusInternalServerError) _, err = rw.Write(responseBytes) - g.Expect(err).To(gomega.BeNil()) + g.Expect(err).ToNot(gomega.HaveOccurred()) })) // Close the server when test finishes defer predictor.Close() predictorSvcUrl, err := url.Parse(predictor.URL) logger.Infof("predictor url %s", predictorSvcUrl) - g.Expect(err).To(gomega.BeNil()) + g.Expect(err).ToNot(gomega.HaveOccurred()) httpProxy := httputil.NewSingleHostReverseProxy(predictorSvcUrl) batchHandler := New(-1, -1, httpProxy, logger) var wg sync.WaitGroup - for i := 0; i < 10; i++ { + for i := range 10 { wg.Add(1) go serveRequest(batchHandler, &wg, i) } - //var responseBytes []byte + // var responseBytes []byte <-responseChan wg.Wait() g.Expect(batchHandler.MaxBatchSize).To(gomega.Equal(MaxBatchSize)) diff --git a/pkg/client/clientset/versioned/clientset.go b/pkg/client/clientset/versioned/clientset.go index 1a7e465deab..b786b48da8e 100644 --- a/pkg/client/clientset/versioned/clientset.go +++ b/pkg/client/clientset/versioned/clientset.go @@ -19,8 +19,8 @@ limitations under the License. package versioned import ( - "fmt" - "net/http" + fmt "fmt" + http "net/http" servingv1alpha1 "github.com/kserve/kserve/pkg/client/clientset/versioned/typed/serving/v1alpha1" servingv1beta1 "github.com/kserve/kserve/pkg/client/clientset/versioned/typed/serving/v1beta1" diff --git a/pkg/client/clientset/versioned/fake/clientset_generated.go b/pkg/client/clientset/versioned/fake/clientset_generated.go index a66dc0c1548..9b922f4ebc0 100644 --- a/pkg/client/clientset/versioned/fake/clientset_generated.go +++ b/pkg/client/clientset/versioned/fake/clientset_generated.go @@ -33,8 +33,12 @@ import ( // NewSimpleClientset returns a clientset that will respond with the provided objects. // It's backed by a very simple object tracker that processes creates, updates and deletions as-is, -// without applying any validations and/or defaults. It shouldn't be considered a replacement +// without applying any field management, validations and/or defaults. It shouldn't be considered a replacement // for a real clientset and is mostly useful in simple unit tests. +// +// DEPRECATED: NewClientset replaces this with support for field management, which significantly improves +// server side apply testing. NewClientset is only available when apply configurations are generated (e.g. +// via --with-applyconfig). func NewSimpleClientset(objects ...runtime.Object) *Clientset { o := testing.NewObjectTracker(scheme, codecs.UniversalDecoder()) for _, obj := range objects { diff --git a/pkg/client/clientset/versioned/typed/serving/v1alpha1/clusterservingruntime.go b/pkg/client/clientset/versioned/typed/serving/v1alpha1/clusterservingruntime.go index c6e8bc2e2aa..4053b415ad4 100644 --- a/pkg/client/clientset/versioned/typed/serving/v1alpha1/clusterservingruntime.go +++ b/pkg/client/clientset/versioned/typed/serving/v1alpha1/clusterservingruntime.go @@ -19,15 +19,14 @@ limitations under the License. package v1alpha1 import ( - "context" - "time" + context "context" - v1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" + servingv1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" scheme "github.com/kserve/kserve/pkg/client/clientset/versioned/scheme" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" types "k8s.io/apimachinery/pkg/types" watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" + gentype "k8s.io/client-go/gentype" ) // ClusterServingRuntimesGetter has a method to return a ClusterServingRuntimeInterface. @@ -38,158 +37,34 @@ type ClusterServingRuntimesGetter interface { // ClusterServingRuntimeInterface has methods to work with ClusterServingRuntime resources. type ClusterServingRuntimeInterface interface { - Create(ctx context.Context, clusterServingRuntime *v1alpha1.ClusterServingRuntime, opts v1.CreateOptions) (*v1alpha1.ClusterServingRuntime, error) - Update(ctx context.Context, clusterServingRuntime *v1alpha1.ClusterServingRuntime, opts v1.UpdateOptions) (*v1alpha1.ClusterServingRuntime, error) - UpdateStatus(ctx context.Context, clusterServingRuntime *v1alpha1.ClusterServingRuntime, opts v1.UpdateOptions) (*v1alpha1.ClusterServingRuntime, error) + Create(ctx context.Context, clusterServingRuntime *servingv1alpha1.ClusterServingRuntime, opts v1.CreateOptions) (*servingv1alpha1.ClusterServingRuntime, error) + Update(ctx context.Context, clusterServingRuntime *servingv1alpha1.ClusterServingRuntime, opts v1.UpdateOptions) (*servingv1alpha1.ClusterServingRuntime, error) + // Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). + UpdateStatus(ctx context.Context, clusterServingRuntime *servingv1alpha1.ClusterServingRuntime, opts v1.UpdateOptions) (*servingv1alpha1.ClusterServingRuntime, error) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error - Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.ClusterServingRuntime, error) - List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.ClusterServingRuntimeList, error) + Get(ctx context.Context, name string, opts v1.GetOptions) (*servingv1alpha1.ClusterServingRuntime, error) + List(ctx context.Context, opts v1.ListOptions) (*servingv1alpha1.ClusterServingRuntimeList, error) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) - Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ClusterServingRuntime, err error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *servingv1alpha1.ClusterServingRuntime, err error) ClusterServingRuntimeExpansion } // clusterServingRuntimes implements ClusterServingRuntimeInterface type clusterServingRuntimes struct { - client rest.Interface - ns string + *gentype.ClientWithList[*servingv1alpha1.ClusterServingRuntime, *servingv1alpha1.ClusterServingRuntimeList] } // newClusterServingRuntimes returns a ClusterServingRuntimes func newClusterServingRuntimes(c *ServingV1alpha1Client, namespace string) *clusterServingRuntimes { return &clusterServingRuntimes{ - client: c.RESTClient(), - ns: namespace, + gentype.NewClientWithList[*servingv1alpha1.ClusterServingRuntime, *servingv1alpha1.ClusterServingRuntimeList]( + "clusterservingruntimes", + c.RESTClient(), + scheme.ParameterCodec, + namespace, + func() *servingv1alpha1.ClusterServingRuntime { return &servingv1alpha1.ClusterServingRuntime{} }, + func() *servingv1alpha1.ClusterServingRuntimeList { return &servingv1alpha1.ClusterServingRuntimeList{} }, + ), } } - -// Get takes name of the clusterServingRuntime, and returns the corresponding clusterServingRuntime object, and an error if there is any. -func (c *clusterServingRuntimes) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.ClusterServingRuntime, err error) { - result = &v1alpha1.ClusterServingRuntime{} - err = c.client.Get(). - Namespace(c.ns). - Resource("clusterservingruntimes"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(ctx). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of ClusterServingRuntimes that match those selectors. -func (c *clusterServingRuntimes) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.ClusterServingRuntimeList, err error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - result = &v1alpha1.ClusterServingRuntimeList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("clusterservingruntimes"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Do(ctx). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested clusterServingRuntimes. -func (c *clusterServingRuntimes) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("clusterservingruntimes"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Watch(ctx) -} - -// Create takes the representation of a clusterServingRuntime and creates it. Returns the server's representation of the clusterServingRuntime, and an error, if there is any. -func (c *clusterServingRuntimes) Create(ctx context.Context, clusterServingRuntime *v1alpha1.ClusterServingRuntime, opts v1.CreateOptions) (result *v1alpha1.ClusterServingRuntime, err error) { - result = &v1alpha1.ClusterServingRuntime{} - err = c.client.Post(). - Namespace(c.ns). - Resource("clusterservingruntimes"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(clusterServingRuntime). - Do(ctx). - Into(result) - return -} - -// Update takes the representation of a clusterServingRuntime and updates it. Returns the server's representation of the clusterServingRuntime, and an error, if there is any. -func (c *clusterServingRuntimes) Update(ctx context.Context, clusterServingRuntime *v1alpha1.ClusterServingRuntime, opts v1.UpdateOptions) (result *v1alpha1.ClusterServingRuntime, err error) { - result = &v1alpha1.ClusterServingRuntime{} - err = c.client.Put(). - Namespace(c.ns). - Resource("clusterservingruntimes"). - Name(clusterServingRuntime.Name). - VersionedParams(&opts, scheme.ParameterCodec). - Body(clusterServingRuntime). - Do(ctx). - Into(result) - return -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *clusterServingRuntimes) UpdateStatus(ctx context.Context, clusterServingRuntime *v1alpha1.ClusterServingRuntime, opts v1.UpdateOptions) (result *v1alpha1.ClusterServingRuntime, err error) { - result = &v1alpha1.ClusterServingRuntime{} - err = c.client.Put(). - Namespace(c.ns). - Resource("clusterservingruntimes"). - Name(clusterServingRuntime.Name). - SubResource("status"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(clusterServingRuntime). - Do(ctx). - Into(result) - return -} - -// Delete takes name of the clusterServingRuntime and deletes it. Returns an error if one occurs. -func (c *clusterServingRuntimes) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("clusterservingruntimes"). - Name(name). - Body(&opts). - Do(ctx). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *clusterServingRuntimes) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - var timeout time.Duration - if listOpts.TimeoutSeconds != nil { - timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second - } - return c.client.Delete(). - Namespace(c.ns). - Resource("clusterservingruntimes"). - VersionedParams(&listOpts, scheme.ParameterCodec). - Timeout(timeout). - Body(&opts). - Do(ctx). - Error() -} - -// Patch applies the patch and returns the patched clusterServingRuntime. -func (c *clusterServingRuntimes) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ClusterServingRuntime, err error) { - result = &v1alpha1.ClusterServingRuntime{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("clusterservingruntimes"). - Name(name). - SubResource(subresources...). - VersionedParams(&opts, scheme.ParameterCodec). - Body(data). - Do(ctx). - Into(result) - return -} diff --git a/pkg/client/clientset/versioned/typed/serving/v1alpha1/clusterstoragecontainer.go b/pkg/client/clientset/versioned/typed/serving/v1alpha1/clusterstoragecontainer.go index 3b3cd697530..47665c92eb7 100644 --- a/pkg/client/clientset/versioned/typed/serving/v1alpha1/clusterstoragecontainer.go +++ b/pkg/client/clientset/versioned/typed/serving/v1alpha1/clusterstoragecontainer.go @@ -19,15 +19,14 @@ limitations under the License. package v1alpha1 import ( - "context" - "time" + context "context" - v1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" + servingv1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" scheme "github.com/kserve/kserve/pkg/client/clientset/versioned/scheme" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" types "k8s.io/apimachinery/pkg/types" watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" + gentype "k8s.io/client-go/gentype" ) // ClusterStorageContainersGetter has a method to return a ClusterStorageContainerInterface. @@ -38,141 +37,34 @@ type ClusterStorageContainersGetter interface { // ClusterStorageContainerInterface has methods to work with ClusterStorageContainer resources. type ClusterStorageContainerInterface interface { - Create(ctx context.Context, clusterStorageContainer *v1alpha1.ClusterStorageContainer, opts v1.CreateOptions) (*v1alpha1.ClusterStorageContainer, error) - Update(ctx context.Context, clusterStorageContainer *v1alpha1.ClusterStorageContainer, opts v1.UpdateOptions) (*v1alpha1.ClusterStorageContainer, error) + Create(ctx context.Context, clusterStorageContainer *servingv1alpha1.ClusterStorageContainer, opts v1.CreateOptions) (*servingv1alpha1.ClusterStorageContainer, error) + Update(ctx context.Context, clusterStorageContainer *servingv1alpha1.ClusterStorageContainer, opts v1.UpdateOptions) (*servingv1alpha1.ClusterStorageContainer, error) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error - Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.ClusterStorageContainer, error) - List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.ClusterStorageContainerList, error) + Get(ctx context.Context, name string, opts v1.GetOptions) (*servingv1alpha1.ClusterStorageContainer, error) + List(ctx context.Context, opts v1.ListOptions) (*servingv1alpha1.ClusterStorageContainerList, error) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) - Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ClusterStorageContainer, err error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *servingv1alpha1.ClusterStorageContainer, err error) ClusterStorageContainerExpansion } // clusterStorageContainers implements ClusterStorageContainerInterface type clusterStorageContainers struct { - client rest.Interface - ns string + *gentype.ClientWithList[*servingv1alpha1.ClusterStorageContainer, *servingv1alpha1.ClusterStorageContainerList] } // newClusterStorageContainers returns a ClusterStorageContainers func newClusterStorageContainers(c *ServingV1alpha1Client, namespace string) *clusterStorageContainers { return &clusterStorageContainers{ - client: c.RESTClient(), - ns: namespace, + gentype.NewClientWithList[*servingv1alpha1.ClusterStorageContainer, *servingv1alpha1.ClusterStorageContainerList]( + "clusterstoragecontainers", + c.RESTClient(), + scheme.ParameterCodec, + namespace, + func() *servingv1alpha1.ClusterStorageContainer { return &servingv1alpha1.ClusterStorageContainer{} }, + func() *servingv1alpha1.ClusterStorageContainerList { + return &servingv1alpha1.ClusterStorageContainerList{} + }, + ), } } - -// Get takes name of the clusterStorageContainer, and returns the corresponding clusterStorageContainer object, and an error if there is any. -func (c *clusterStorageContainers) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.ClusterStorageContainer, err error) { - result = &v1alpha1.ClusterStorageContainer{} - err = c.client.Get(). - Namespace(c.ns). - Resource("clusterstoragecontainers"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(ctx). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of ClusterStorageContainers that match those selectors. -func (c *clusterStorageContainers) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.ClusterStorageContainerList, err error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - result = &v1alpha1.ClusterStorageContainerList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("clusterstoragecontainers"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Do(ctx). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested clusterStorageContainers. -func (c *clusterStorageContainers) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("clusterstoragecontainers"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Watch(ctx) -} - -// Create takes the representation of a clusterStorageContainer and creates it. Returns the server's representation of the clusterStorageContainer, and an error, if there is any. -func (c *clusterStorageContainers) Create(ctx context.Context, clusterStorageContainer *v1alpha1.ClusterStorageContainer, opts v1.CreateOptions) (result *v1alpha1.ClusterStorageContainer, err error) { - result = &v1alpha1.ClusterStorageContainer{} - err = c.client.Post(). - Namespace(c.ns). - Resource("clusterstoragecontainers"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(clusterStorageContainer). - Do(ctx). - Into(result) - return -} - -// Update takes the representation of a clusterStorageContainer and updates it. Returns the server's representation of the clusterStorageContainer, and an error, if there is any. -func (c *clusterStorageContainers) Update(ctx context.Context, clusterStorageContainer *v1alpha1.ClusterStorageContainer, opts v1.UpdateOptions) (result *v1alpha1.ClusterStorageContainer, err error) { - result = &v1alpha1.ClusterStorageContainer{} - err = c.client.Put(). - Namespace(c.ns). - Resource("clusterstoragecontainers"). - Name(clusterStorageContainer.Name). - VersionedParams(&opts, scheme.ParameterCodec). - Body(clusterStorageContainer). - Do(ctx). - Into(result) - return -} - -// Delete takes name of the clusterStorageContainer and deletes it. Returns an error if one occurs. -func (c *clusterStorageContainers) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("clusterstoragecontainers"). - Name(name). - Body(&opts). - Do(ctx). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *clusterStorageContainers) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - var timeout time.Duration - if listOpts.TimeoutSeconds != nil { - timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second - } - return c.client.Delete(). - Namespace(c.ns). - Resource("clusterstoragecontainers"). - VersionedParams(&listOpts, scheme.ParameterCodec). - Timeout(timeout). - Body(&opts). - Do(ctx). - Error() -} - -// Patch applies the patch and returns the patched clusterStorageContainer. -func (c *clusterStorageContainers) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ClusterStorageContainer, err error) { - result = &v1alpha1.ClusterStorageContainer{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("clusterstoragecontainers"). - Name(name). - SubResource(subresources...). - VersionedParams(&opts, scheme.ParameterCodec). - Body(data). - Do(ctx). - Into(result) - return -} diff --git a/pkg/client/clientset/versioned/typed/serving/v1alpha1/fake/fake_clusterservingruntime.go b/pkg/client/clientset/versioned/typed/serving/v1alpha1/fake/fake_clusterservingruntime.go index b4b75e2438a..c0a8d298547 100644 --- a/pkg/client/clientset/versioned/typed/serving/v1alpha1/fake/fake_clusterservingruntime.go +++ b/pkg/client/clientset/versioned/typed/serving/v1alpha1/fake/fake_clusterservingruntime.go @@ -19,123 +19,34 @@ limitations under the License. package fake import ( - "context" - v1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" + servingv1alpha1 "github.com/kserve/kserve/pkg/client/clientset/versioned/typed/serving/v1alpha1" + gentype "k8s.io/client-go/gentype" ) -// FakeClusterServingRuntimes implements ClusterServingRuntimeInterface -type FakeClusterServingRuntimes struct { +// fakeClusterServingRuntimes implements ClusterServingRuntimeInterface +type fakeClusterServingRuntimes struct { + *gentype.FakeClientWithList[*v1alpha1.ClusterServingRuntime, *v1alpha1.ClusterServingRuntimeList] Fake *FakeServingV1alpha1 - ns string -} - -var clusterservingruntimesResource = v1alpha1.SchemeGroupVersion.WithResource("clusterservingruntimes") - -var clusterservingruntimesKind = v1alpha1.SchemeGroupVersion.WithKind("ClusterServingRuntime") - -// Get takes name of the clusterServingRuntime, and returns the corresponding clusterServingRuntime object, and an error if there is any. -func (c *FakeClusterServingRuntimes) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.ClusterServingRuntime, err error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(clusterservingruntimesResource, c.ns, name), &v1alpha1.ClusterServingRuntime{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.ClusterServingRuntime), err -} - -// List takes label and field selectors, and returns the list of ClusterServingRuntimes that match those selectors. -func (c *FakeClusterServingRuntimes) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.ClusterServingRuntimeList, err error) { - obj, err := c.Fake. - Invokes(testing.NewListAction(clusterservingruntimesResource, clusterservingruntimesKind, c.ns, opts), &v1alpha1.ClusterServingRuntimeList{}) - - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &v1alpha1.ClusterServingRuntimeList{ListMeta: obj.(*v1alpha1.ClusterServingRuntimeList).ListMeta} - for _, item := range obj.(*v1alpha1.ClusterServingRuntimeList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested clusterServingRuntimes. -func (c *FakeClusterServingRuntimes) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchAction(clusterservingruntimesResource, c.ns, opts)) - -} - -// Create takes the representation of a clusterServingRuntime and creates it. Returns the server's representation of the clusterServingRuntime, and an error, if there is any. -func (c *FakeClusterServingRuntimes) Create(ctx context.Context, clusterServingRuntime *v1alpha1.ClusterServingRuntime, opts v1.CreateOptions) (result *v1alpha1.ClusterServingRuntime, err error) { - obj, err := c.Fake. - Invokes(testing.NewCreateAction(clusterservingruntimesResource, c.ns, clusterServingRuntime), &v1alpha1.ClusterServingRuntime{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.ClusterServingRuntime), err -} - -// Update takes the representation of a clusterServingRuntime and updates it. Returns the server's representation of the clusterServingRuntime, and an error, if there is any. -func (c *FakeClusterServingRuntimes) Update(ctx context.Context, clusterServingRuntime *v1alpha1.ClusterServingRuntime, opts v1.UpdateOptions) (result *v1alpha1.ClusterServingRuntime, err error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateAction(clusterservingruntimesResource, c.ns, clusterServingRuntime), &v1alpha1.ClusterServingRuntime{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.ClusterServingRuntime), err -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *FakeClusterServingRuntimes) UpdateStatus(ctx context.Context, clusterServingRuntime *v1alpha1.ClusterServingRuntime, opts v1.UpdateOptions) (*v1alpha1.ClusterServingRuntime, error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateSubresourceAction(clusterservingruntimesResource, "status", c.ns, clusterServingRuntime), &v1alpha1.ClusterServingRuntime{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.ClusterServingRuntime), err -} - -// Delete takes name of the clusterServingRuntime and deletes it. Returns an error if one occurs. -func (c *FakeClusterServingRuntimes) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteActionWithOptions(clusterservingruntimesResource, c.ns, name, opts), &v1alpha1.ClusterServingRuntime{}) - - return err } -// DeleteCollection deletes a collection of objects. -func (c *FakeClusterServingRuntimes) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - action := testing.NewDeleteCollectionAction(clusterservingruntimesResource, c.ns, listOpts) - - _, err := c.Fake.Invokes(action, &v1alpha1.ClusterServingRuntimeList{}) - return err -} - -// Patch applies the patch and returns the patched clusterServingRuntime. -func (c *FakeClusterServingRuntimes) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ClusterServingRuntime, err error) { - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(clusterservingruntimesResource, c.ns, name, pt, data, subresources...), &v1alpha1.ClusterServingRuntime{}) - - if obj == nil { - return nil, err +func newFakeClusterServingRuntimes(fake *FakeServingV1alpha1, namespace string) servingv1alpha1.ClusterServingRuntimeInterface { + return &fakeClusterServingRuntimes{ + gentype.NewFakeClientWithList[*v1alpha1.ClusterServingRuntime, *v1alpha1.ClusterServingRuntimeList]( + fake.Fake, + namespace, + v1alpha1.SchemeGroupVersion.WithResource("clusterservingruntimes"), + v1alpha1.SchemeGroupVersion.WithKind("ClusterServingRuntime"), + func() *v1alpha1.ClusterServingRuntime { return &v1alpha1.ClusterServingRuntime{} }, + func() *v1alpha1.ClusterServingRuntimeList { return &v1alpha1.ClusterServingRuntimeList{} }, + func(dst, src *v1alpha1.ClusterServingRuntimeList) { dst.ListMeta = src.ListMeta }, + func(list *v1alpha1.ClusterServingRuntimeList) []*v1alpha1.ClusterServingRuntime { + return gentype.ToPointerSlice(list.Items) + }, + func(list *v1alpha1.ClusterServingRuntimeList, items []*v1alpha1.ClusterServingRuntime) { + list.Items = gentype.FromPointerSlice(items) + }, + ), + fake, } - return obj.(*v1alpha1.ClusterServingRuntime), err } diff --git a/pkg/client/clientset/versioned/typed/serving/v1alpha1/fake/fake_clusterstoragecontainer.go b/pkg/client/clientset/versioned/typed/serving/v1alpha1/fake/fake_clusterstoragecontainer.go index fbbe6ef7d88..e131a0111f1 100644 --- a/pkg/client/clientset/versioned/typed/serving/v1alpha1/fake/fake_clusterstoragecontainer.go +++ b/pkg/client/clientset/versioned/typed/serving/v1alpha1/fake/fake_clusterstoragecontainer.go @@ -19,111 +19,34 @@ limitations under the License. package fake import ( - "context" - v1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" + servingv1alpha1 "github.com/kserve/kserve/pkg/client/clientset/versioned/typed/serving/v1alpha1" + gentype "k8s.io/client-go/gentype" ) -// FakeClusterStorageContainers implements ClusterStorageContainerInterface -type FakeClusterStorageContainers struct { +// fakeClusterStorageContainers implements ClusterStorageContainerInterface +type fakeClusterStorageContainers struct { + *gentype.FakeClientWithList[*v1alpha1.ClusterStorageContainer, *v1alpha1.ClusterStorageContainerList] Fake *FakeServingV1alpha1 - ns string -} - -var clusterstoragecontainersResource = v1alpha1.SchemeGroupVersion.WithResource("clusterstoragecontainers") - -var clusterstoragecontainersKind = v1alpha1.SchemeGroupVersion.WithKind("ClusterStorageContainer") - -// Get takes name of the clusterStorageContainer, and returns the corresponding clusterStorageContainer object, and an error if there is any. -func (c *FakeClusterStorageContainers) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.ClusterStorageContainer, err error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(clusterstoragecontainersResource, c.ns, name), &v1alpha1.ClusterStorageContainer{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.ClusterStorageContainer), err -} - -// List takes label and field selectors, and returns the list of ClusterStorageContainers that match those selectors. -func (c *FakeClusterStorageContainers) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.ClusterStorageContainerList, err error) { - obj, err := c.Fake. - Invokes(testing.NewListAction(clusterstoragecontainersResource, clusterstoragecontainersKind, c.ns, opts), &v1alpha1.ClusterStorageContainerList{}) - - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &v1alpha1.ClusterStorageContainerList{ListMeta: obj.(*v1alpha1.ClusterStorageContainerList).ListMeta} - for _, item := range obj.(*v1alpha1.ClusterStorageContainerList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested clusterStorageContainers. -func (c *FakeClusterStorageContainers) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchAction(clusterstoragecontainersResource, c.ns, opts)) - -} - -// Create takes the representation of a clusterStorageContainer and creates it. Returns the server's representation of the clusterStorageContainer, and an error, if there is any. -func (c *FakeClusterStorageContainers) Create(ctx context.Context, clusterStorageContainer *v1alpha1.ClusterStorageContainer, opts v1.CreateOptions) (result *v1alpha1.ClusterStorageContainer, err error) { - obj, err := c.Fake. - Invokes(testing.NewCreateAction(clusterstoragecontainersResource, c.ns, clusterStorageContainer), &v1alpha1.ClusterStorageContainer{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.ClusterStorageContainer), err -} - -// Update takes the representation of a clusterStorageContainer and updates it. Returns the server's representation of the clusterStorageContainer, and an error, if there is any. -func (c *FakeClusterStorageContainers) Update(ctx context.Context, clusterStorageContainer *v1alpha1.ClusterStorageContainer, opts v1.UpdateOptions) (result *v1alpha1.ClusterStorageContainer, err error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateAction(clusterstoragecontainersResource, c.ns, clusterStorageContainer), &v1alpha1.ClusterStorageContainer{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.ClusterStorageContainer), err -} - -// Delete takes name of the clusterStorageContainer and deletes it. Returns an error if one occurs. -func (c *FakeClusterStorageContainers) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteActionWithOptions(clusterstoragecontainersResource, c.ns, name, opts), &v1alpha1.ClusterStorageContainer{}) - - return err } -// DeleteCollection deletes a collection of objects. -func (c *FakeClusterStorageContainers) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - action := testing.NewDeleteCollectionAction(clusterstoragecontainersResource, c.ns, listOpts) - - _, err := c.Fake.Invokes(action, &v1alpha1.ClusterStorageContainerList{}) - return err -} - -// Patch applies the patch and returns the patched clusterStorageContainer. -func (c *FakeClusterStorageContainers) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ClusterStorageContainer, err error) { - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(clusterstoragecontainersResource, c.ns, name, pt, data, subresources...), &v1alpha1.ClusterStorageContainer{}) - - if obj == nil { - return nil, err +func newFakeClusterStorageContainers(fake *FakeServingV1alpha1, namespace string) servingv1alpha1.ClusterStorageContainerInterface { + return &fakeClusterStorageContainers{ + gentype.NewFakeClientWithList[*v1alpha1.ClusterStorageContainer, *v1alpha1.ClusterStorageContainerList]( + fake.Fake, + namespace, + v1alpha1.SchemeGroupVersion.WithResource("clusterstoragecontainers"), + v1alpha1.SchemeGroupVersion.WithKind("ClusterStorageContainer"), + func() *v1alpha1.ClusterStorageContainer { return &v1alpha1.ClusterStorageContainer{} }, + func() *v1alpha1.ClusterStorageContainerList { return &v1alpha1.ClusterStorageContainerList{} }, + func(dst, src *v1alpha1.ClusterStorageContainerList) { dst.ListMeta = src.ListMeta }, + func(list *v1alpha1.ClusterStorageContainerList) []*v1alpha1.ClusterStorageContainer { + return gentype.ToPointerSlice(list.Items) + }, + func(list *v1alpha1.ClusterStorageContainerList, items []*v1alpha1.ClusterStorageContainer) { + list.Items = gentype.FromPointerSlice(items) + }, + ), + fake, } - return obj.(*v1alpha1.ClusterStorageContainer), err } diff --git a/pkg/client/clientset/versioned/typed/serving/v1alpha1/fake/fake_inferencegraph.go b/pkg/client/clientset/versioned/typed/serving/v1alpha1/fake/fake_inferencegraph.go index aeaf9b8c375..66ce30ed5d5 100644 --- a/pkg/client/clientset/versioned/typed/serving/v1alpha1/fake/fake_inferencegraph.go +++ b/pkg/client/clientset/versioned/typed/serving/v1alpha1/fake/fake_inferencegraph.go @@ -19,123 +19,34 @@ limitations under the License. package fake import ( - "context" - v1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" + servingv1alpha1 "github.com/kserve/kserve/pkg/client/clientset/versioned/typed/serving/v1alpha1" + gentype "k8s.io/client-go/gentype" ) -// FakeInferenceGraphs implements InferenceGraphInterface -type FakeInferenceGraphs struct { +// fakeInferenceGraphs implements InferenceGraphInterface +type fakeInferenceGraphs struct { + *gentype.FakeClientWithList[*v1alpha1.InferenceGraph, *v1alpha1.InferenceGraphList] Fake *FakeServingV1alpha1 - ns string -} - -var inferencegraphsResource = v1alpha1.SchemeGroupVersion.WithResource("inferencegraphs") - -var inferencegraphsKind = v1alpha1.SchemeGroupVersion.WithKind("InferenceGraph") - -// Get takes name of the inferenceGraph, and returns the corresponding inferenceGraph object, and an error if there is any. -func (c *FakeInferenceGraphs) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.InferenceGraph, err error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(inferencegraphsResource, c.ns, name), &v1alpha1.InferenceGraph{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.InferenceGraph), err -} - -// List takes label and field selectors, and returns the list of InferenceGraphs that match those selectors. -func (c *FakeInferenceGraphs) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.InferenceGraphList, err error) { - obj, err := c.Fake. - Invokes(testing.NewListAction(inferencegraphsResource, inferencegraphsKind, c.ns, opts), &v1alpha1.InferenceGraphList{}) - - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &v1alpha1.InferenceGraphList{ListMeta: obj.(*v1alpha1.InferenceGraphList).ListMeta} - for _, item := range obj.(*v1alpha1.InferenceGraphList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested inferenceGraphs. -func (c *FakeInferenceGraphs) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchAction(inferencegraphsResource, c.ns, opts)) - -} - -// Create takes the representation of a inferenceGraph and creates it. Returns the server's representation of the inferenceGraph, and an error, if there is any. -func (c *FakeInferenceGraphs) Create(ctx context.Context, inferenceGraph *v1alpha1.InferenceGraph, opts v1.CreateOptions) (result *v1alpha1.InferenceGraph, err error) { - obj, err := c.Fake. - Invokes(testing.NewCreateAction(inferencegraphsResource, c.ns, inferenceGraph), &v1alpha1.InferenceGraph{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.InferenceGraph), err -} - -// Update takes the representation of a inferenceGraph and updates it. Returns the server's representation of the inferenceGraph, and an error, if there is any. -func (c *FakeInferenceGraphs) Update(ctx context.Context, inferenceGraph *v1alpha1.InferenceGraph, opts v1.UpdateOptions) (result *v1alpha1.InferenceGraph, err error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateAction(inferencegraphsResource, c.ns, inferenceGraph), &v1alpha1.InferenceGraph{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.InferenceGraph), err -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *FakeInferenceGraphs) UpdateStatus(ctx context.Context, inferenceGraph *v1alpha1.InferenceGraph, opts v1.UpdateOptions) (*v1alpha1.InferenceGraph, error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateSubresourceAction(inferencegraphsResource, "status", c.ns, inferenceGraph), &v1alpha1.InferenceGraph{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.InferenceGraph), err -} - -// Delete takes name of the inferenceGraph and deletes it. Returns an error if one occurs. -func (c *FakeInferenceGraphs) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteActionWithOptions(inferencegraphsResource, c.ns, name, opts), &v1alpha1.InferenceGraph{}) - - return err } -// DeleteCollection deletes a collection of objects. -func (c *FakeInferenceGraphs) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - action := testing.NewDeleteCollectionAction(inferencegraphsResource, c.ns, listOpts) - - _, err := c.Fake.Invokes(action, &v1alpha1.InferenceGraphList{}) - return err -} - -// Patch applies the patch and returns the patched inferenceGraph. -func (c *FakeInferenceGraphs) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.InferenceGraph, err error) { - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(inferencegraphsResource, c.ns, name, pt, data, subresources...), &v1alpha1.InferenceGraph{}) - - if obj == nil { - return nil, err +func newFakeInferenceGraphs(fake *FakeServingV1alpha1, namespace string) servingv1alpha1.InferenceGraphInterface { + return &fakeInferenceGraphs{ + gentype.NewFakeClientWithList[*v1alpha1.InferenceGraph, *v1alpha1.InferenceGraphList]( + fake.Fake, + namespace, + v1alpha1.SchemeGroupVersion.WithResource("inferencegraphs"), + v1alpha1.SchemeGroupVersion.WithKind("InferenceGraph"), + func() *v1alpha1.InferenceGraph { return &v1alpha1.InferenceGraph{} }, + func() *v1alpha1.InferenceGraphList { return &v1alpha1.InferenceGraphList{} }, + func(dst, src *v1alpha1.InferenceGraphList) { dst.ListMeta = src.ListMeta }, + func(list *v1alpha1.InferenceGraphList) []*v1alpha1.InferenceGraph { + return gentype.ToPointerSlice(list.Items) + }, + func(list *v1alpha1.InferenceGraphList, items []*v1alpha1.InferenceGraph) { + list.Items = gentype.FromPointerSlice(items) + }, + ), + fake, } - return obj.(*v1alpha1.InferenceGraph), err } diff --git a/pkg/client/clientset/versioned/typed/serving/v1alpha1/fake/fake_localmodelcache.go b/pkg/client/clientset/versioned/typed/serving/v1alpha1/fake/fake_localmodelcache.go index b2bd7c6c2c1..e5bf885b44e 100644 --- a/pkg/client/clientset/versioned/typed/serving/v1alpha1/fake/fake_localmodelcache.go +++ b/pkg/client/clientset/versioned/typed/serving/v1alpha1/fake/fake_localmodelcache.go @@ -19,123 +19,34 @@ limitations under the License. package fake import ( - "context" - v1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" + servingv1alpha1 "github.com/kserve/kserve/pkg/client/clientset/versioned/typed/serving/v1alpha1" + gentype "k8s.io/client-go/gentype" ) -// FakeLocalModelCaches implements LocalModelCacheInterface -type FakeLocalModelCaches struct { +// fakeLocalModelCaches implements LocalModelCacheInterface +type fakeLocalModelCaches struct { + *gentype.FakeClientWithList[*v1alpha1.LocalModelCache, *v1alpha1.LocalModelCacheList] Fake *FakeServingV1alpha1 - ns string -} - -var localmodelcachesResource = v1alpha1.SchemeGroupVersion.WithResource("localmodelcaches") - -var localmodelcachesKind = v1alpha1.SchemeGroupVersion.WithKind("LocalModelCache") - -// Get takes name of the localModelCache, and returns the corresponding localModelCache object, and an error if there is any. -func (c *FakeLocalModelCaches) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.LocalModelCache, err error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(localmodelcachesResource, c.ns, name), &v1alpha1.LocalModelCache{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.LocalModelCache), err -} - -// List takes label and field selectors, and returns the list of LocalModelCaches that match those selectors. -func (c *FakeLocalModelCaches) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.LocalModelCacheList, err error) { - obj, err := c.Fake. - Invokes(testing.NewListAction(localmodelcachesResource, localmodelcachesKind, c.ns, opts), &v1alpha1.LocalModelCacheList{}) - - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &v1alpha1.LocalModelCacheList{ListMeta: obj.(*v1alpha1.LocalModelCacheList).ListMeta} - for _, item := range obj.(*v1alpha1.LocalModelCacheList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested localModelCaches. -func (c *FakeLocalModelCaches) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchAction(localmodelcachesResource, c.ns, opts)) - -} - -// Create takes the representation of a localModelCache and creates it. Returns the server's representation of the localModelCache, and an error, if there is any. -func (c *FakeLocalModelCaches) Create(ctx context.Context, localModelCache *v1alpha1.LocalModelCache, opts v1.CreateOptions) (result *v1alpha1.LocalModelCache, err error) { - obj, err := c.Fake. - Invokes(testing.NewCreateAction(localmodelcachesResource, c.ns, localModelCache), &v1alpha1.LocalModelCache{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.LocalModelCache), err -} - -// Update takes the representation of a localModelCache and updates it. Returns the server's representation of the localModelCache, and an error, if there is any. -func (c *FakeLocalModelCaches) Update(ctx context.Context, localModelCache *v1alpha1.LocalModelCache, opts v1.UpdateOptions) (result *v1alpha1.LocalModelCache, err error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateAction(localmodelcachesResource, c.ns, localModelCache), &v1alpha1.LocalModelCache{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.LocalModelCache), err -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *FakeLocalModelCaches) UpdateStatus(ctx context.Context, localModelCache *v1alpha1.LocalModelCache, opts v1.UpdateOptions) (*v1alpha1.LocalModelCache, error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateSubresourceAction(localmodelcachesResource, "status", c.ns, localModelCache), &v1alpha1.LocalModelCache{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.LocalModelCache), err -} - -// Delete takes name of the localModelCache and deletes it. Returns an error if one occurs. -func (c *FakeLocalModelCaches) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteActionWithOptions(localmodelcachesResource, c.ns, name, opts), &v1alpha1.LocalModelCache{}) - - return err } -// DeleteCollection deletes a collection of objects. -func (c *FakeLocalModelCaches) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - action := testing.NewDeleteCollectionAction(localmodelcachesResource, c.ns, listOpts) - - _, err := c.Fake.Invokes(action, &v1alpha1.LocalModelCacheList{}) - return err -} - -// Patch applies the patch and returns the patched localModelCache. -func (c *FakeLocalModelCaches) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.LocalModelCache, err error) { - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(localmodelcachesResource, c.ns, name, pt, data, subresources...), &v1alpha1.LocalModelCache{}) - - if obj == nil { - return nil, err +func newFakeLocalModelCaches(fake *FakeServingV1alpha1, namespace string) servingv1alpha1.LocalModelCacheInterface { + return &fakeLocalModelCaches{ + gentype.NewFakeClientWithList[*v1alpha1.LocalModelCache, *v1alpha1.LocalModelCacheList]( + fake.Fake, + namespace, + v1alpha1.SchemeGroupVersion.WithResource("localmodelcaches"), + v1alpha1.SchemeGroupVersion.WithKind("LocalModelCache"), + func() *v1alpha1.LocalModelCache { return &v1alpha1.LocalModelCache{} }, + func() *v1alpha1.LocalModelCacheList { return &v1alpha1.LocalModelCacheList{} }, + func(dst, src *v1alpha1.LocalModelCacheList) { dst.ListMeta = src.ListMeta }, + func(list *v1alpha1.LocalModelCacheList) []*v1alpha1.LocalModelCache { + return gentype.ToPointerSlice(list.Items) + }, + func(list *v1alpha1.LocalModelCacheList, items []*v1alpha1.LocalModelCache) { + list.Items = gentype.FromPointerSlice(items) + }, + ), + fake, } - return obj.(*v1alpha1.LocalModelCache), err } diff --git a/pkg/client/clientset/versioned/typed/serving/v1alpha1/fake/fake_localmodelnode.go b/pkg/client/clientset/versioned/typed/serving/v1alpha1/fake/fake_localmodelnode.go index c0a64c469dc..27b144bd558 100644 --- a/pkg/client/clientset/versioned/typed/serving/v1alpha1/fake/fake_localmodelnode.go +++ b/pkg/client/clientset/versioned/typed/serving/v1alpha1/fake/fake_localmodelnode.go @@ -19,123 +19,34 @@ limitations under the License. package fake import ( - "context" - v1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" + servingv1alpha1 "github.com/kserve/kserve/pkg/client/clientset/versioned/typed/serving/v1alpha1" + gentype "k8s.io/client-go/gentype" ) -// FakeLocalModelNodes implements LocalModelNodeInterface -type FakeLocalModelNodes struct { +// fakeLocalModelNodes implements LocalModelNodeInterface +type fakeLocalModelNodes struct { + *gentype.FakeClientWithList[*v1alpha1.LocalModelNode, *v1alpha1.LocalModelNodeList] Fake *FakeServingV1alpha1 - ns string -} - -var localmodelnodesResource = v1alpha1.SchemeGroupVersion.WithResource("localmodelnodes") - -var localmodelnodesKind = v1alpha1.SchemeGroupVersion.WithKind("LocalModelNode") - -// Get takes name of the localModelNode, and returns the corresponding localModelNode object, and an error if there is any. -func (c *FakeLocalModelNodes) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.LocalModelNode, err error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(localmodelnodesResource, c.ns, name), &v1alpha1.LocalModelNode{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.LocalModelNode), err -} - -// List takes label and field selectors, and returns the list of LocalModelNodes that match those selectors. -func (c *FakeLocalModelNodes) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.LocalModelNodeList, err error) { - obj, err := c.Fake. - Invokes(testing.NewListAction(localmodelnodesResource, localmodelnodesKind, c.ns, opts), &v1alpha1.LocalModelNodeList{}) - - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &v1alpha1.LocalModelNodeList{ListMeta: obj.(*v1alpha1.LocalModelNodeList).ListMeta} - for _, item := range obj.(*v1alpha1.LocalModelNodeList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested localModelNodes. -func (c *FakeLocalModelNodes) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchAction(localmodelnodesResource, c.ns, opts)) - -} - -// Create takes the representation of a localModelNode and creates it. Returns the server's representation of the localModelNode, and an error, if there is any. -func (c *FakeLocalModelNodes) Create(ctx context.Context, localModelNode *v1alpha1.LocalModelNode, opts v1.CreateOptions) (result *v1alpha1.LocalModelNode, err error) { - obj, err := c.Fake. - Invokes(testing.NewCreateAction(localmodelnodesResource, c.ns, localModelNode), &v1alpha1.LocalModelNode{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.LocalModelNode), err -} - -// Update takes the representation of a localModelNode and updates it. Returns the server's representation of the localModelNode, and an error, if there is any. -func (c *FakeLocalModelNodes) Update(ctx context.Context, localModelNode *v1alpha1.LocalModelNode, opts v1.UpdateOptions) (result *v1alpha1.LocalModelNode, err error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateAction(localmodelnodesResource, c.ns, localModelNode), &v1alpha1.LocalModelNode{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.LocalModelNode), err -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *FakeLocalModelNodes) UpdateStatus(ctx context.Context, localModelNode *v1alpha1.LocalModelNode, opts v1.UpdateOptions) (*v1alpha1.LocalModelNode, error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateSubresourceAction(localmodelnodesResource, "status", c.ns, localModelNode), &v1alpha1.LocalModelNode{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.LocalModelNode), err -} - -// Delete takes name of the localModelNode and deletes it. Returns an error if one occurs. -func (c *FakeLocalModelNodes) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteActionWithOptions(localmodelnodesResource, c.ns, name, opts), &v1alpha1.LocalModelNode{}) - - return err } -// DeleteCollection deletes a collection of objects. -func (c *FakeLocalModelNodes) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - action := testing.NewDeleteCollectionAction(localmodelnodesResource, c.ns, listOpts) - - _, err := c.Fake.Invokes(action, &v1alpha1.LocalModelNodeList{}) - return err -} - -// Patch applies the patch and returns the patched localModelNode. -func (c *FakeLocalModelNodes) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.LocalModelNode, err error) { - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(localmodelnodesResource, c.ns, name, pt, data, subresources...), &v1alpha1.LocalModelNode{}) - - if obj == nil { - return nil, err +func newFakeLocalModelNodes(fake *FakeServingV1alpha1, namespace string) servingv1alpha1.LocalModelNodeInterface { + return &fakeLocalModelNodes{ + gentype.NewFakeClientWithList[*v1alpha1.LocalModelNode, *v1alpha1.LocalModelNodeList]( + fake.Fake, + namespace, + v1alpha1.SchemeGroupVersion.WithResource("localmodelnodes"), + v1alpha1.SchemeGroupVersion.WithKind("LocalModelNode"), + func() *v1alpha1.LocalModelNode { return &v1alpha1.LocalModelNode{} }, + func() *v1alpha1.LocalModelNodeList { return &v1alpha1.LocalModelNodeList{} }, + func(dst, src *v1alpha1.LocalModelNodeList) { dst.ListMeta = src.ListMeta }, + func(list *v1alpha1.LocalModelNodeList) []*v1alpha1.LocalModelNode { + return gentype.ToPointerSlice(list.Items) + }, + func(list *v1alpha1.LocalModelNodeList, items []*v1alpha1.LocalModelNode) { + list.Items = gentype.FromPointerSlice(items) + }, + ), + fake, } - return obj.(*v1alpha1.LocalModelNode), err } diff --git a/pkg/client/clientset/versioned/typed/serving/v1alpha1/fake/fake_localmodelnodegroup.go b/pkg/client/clientset/versioned/typed/serving/v1alpha1/fake/fake_localmodelnodegroup.go index 41d7b43cdfc..329ac3e836d 100644 --- a/pkg/client/clientset/versioned/typed/serving/v1alpha1/fake/fake_localmodelnodegroup.go +++ b/pkg/client/clientset/versioned/typed/serving/v1alpha1/fake/fake_localmodelnodegroup.go @@ -19,123 +19,34 @@ limitations under the License. package fake import ( - "context" - v1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" + servingv1alpha1 "github.com/kserve/kserve/pkg/client/clientset/versioned/typed/serving/v1alpha1" + gentype "k8s.io/client-go/gentype" ) -// FakeLocalModelNodeGroups implements LocalModelNodeGroupInterface -type FakeLocalModelNodeGroups struct { +// fakeLocalModelNodeGroups implements LocalModelNodeGroupInterface +type fakeLocalModelNodeGroups struct { + *gentype.FakeClientWithList[*v1alpha1.LocalModelNodeGroup, *v1alpha1.LocalModelNodeGroupList] Fake *FakeServingV1alpha1 - ns string -} - -var localmodelnodegroupsResource = v1alpha1.SchemeGroupVersion.WithResource("localmodelnodegroups") - -var localmodelnodegroupsKind = v1alpha1.SchemeGroupVersion.WithKind("LocalModelNodeGroup") - -// Get takes name of the localModelNodeGroup, and returns the corresponding localModelNodeGroup object, and an error if there is any. -func (c *FakeLocalModelNodeGroups) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.LocalModelNodeGroup, err error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(localmodelnodegroupsResource, c.ns, name), &v1alpha1.LocalModelNodeGroup{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.LocalModelNodeGroup), err -} - -// List takes label and field selectors, and returns the list of LocalModelNodeGroups that match those selectors. -func (c *FakeLocalModelNodeGroups) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.LocalModelNodeGroupList, err error) { - obj, err := c.Fake. - Invokes(testing.NewListAction(localmodelnodegroupsResource, localmodelnodegroupsKind, c.ns, opts), &v1alpha1.LocalModelNodeGroupList{}) - - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &v1alpha1.LocalModelNodeGroupList{ListMeta: obj.(*v1alpha1.LocalModelNodeGroupList).ListMeta} - for _, item := range obj.(*v1alpha1.LocalModelNodeGroupList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested localModelNodeGroups. -func (c *FakeLocalModelNodeGroups) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchAction(localmodelnodegroupsResource, c.ns, opts)) - -} - -// Create takes the representation of a localModelNodeGroup and creates it. Returns the server's representation of the localModelNodeGroup, and an error, if there is any. -func (c *FakeLocalModelNodeGroups) Create(ctx context.Context, localModelNodeGroup *v1alpha1.LocalModelNodeGroup, opts v1.CreateOptions) (result *v1alpha1.LocalModelNodeGroup, err error) { - obj, err := c.Fake. - Invokes(testing.NewCreateAction(localmodelnodegroupsResource, c.ns, localModelNodeGroup), &v1alpha1.LocalModelNodeGroup{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.LocalModelNodeGroup), err -} - -// Update takes the representation of a localModelNodeGroup and updates it. Returns the server's representation of the localModelNodeGroup, and an error, if there is any. -func (c *FakeLocalModelNodeGroups) Update(ctx context.Context, localModelNodeGroup *v1alpha1.LocalModelNodeGroup, opts v1.UpdateOptions) (result *v1alpha1.LocalModelNodeGroup, err error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateAction(localmodelnodegroupsResource, c.ns, localModelNodeGroup), &v1alpha1.LocalModelNodeGroup{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.LocalModelNodeGroup), err -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *FakeLocalModelNodeGroups) UpdateStatus(ctx context.Context, localModelNodeGroup *v1alpha1.LocalModelNodeGroup, opts v1.UpdateOptions) (*v1alpha1.LocalModelNodeGroup, error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateSubresourceAction(localmodelnodegroupsResource, "status", c.ns, localModelNodeGroup), &v1alpha1.LocalModelNodeGroup{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.LocalModelNodeGroup), err -} - -// Delete takes name of the localModelNodeGroup and deletes it. Returns an error if one occurs. -func (c *FakeLocalModelNodeGroups) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteActionWithOptions(localmodelnodegroupsResource, c.ns, name, opts), &v1alpha1.LocalModelNodeGroup{}) - - return err } -// DeleteCollection deletes a collection of objects. -func (c *FakeLocalModelNodeGroups) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - action := testing.NewDeleteCollectionAction(localmodelnodegroupsResource, c.ns, listOpts) - - _, err := c.Fake.Invokes(action, &v1alpha1.LocalModelNodeGroupList{}) - return err -} - -// Patch applies the patch and returns the patched localModelNodeGroup. -func (c *FakeLocalModelNodeGroups) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.LocalModelNodeGroup, err error) { - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(localmodelnodegroupsResource, c.ns, name, pt, data, subresources...), &v1alpha1.LocalModelNodeGroup{}) - - if obj == nil { - return nil, err +func newFakeLocalModelNodeGroups(fake *FakeServingV1alpha1, namespace string) servingv1alpha1.LocalModelNodeGroupInterface { + return &fakeLocalModelNodeGroups{ + gentype.NewFakeClientWithList[*v1alpha1.LocalModelNodeGroup, *v1alpha1.LocalModelNodeGroupList]( + fake.Fake, + namespace, + v1alpha1.SchemeGroupVersion.WithResource("localmodelnodegroups"), + v1alpha1.SchemeGroupVersion.WithKind("LocalModelNodeGroup"), + func() *v1alpha1.LocalModelNodeGroup { return &v1alpha1.LocalModelNodeGroup{} }, + func() *v1alpha1.LocalModelNodeGroupList { return &v1alpha1.LocalModelNodeGroupList{} }, + func(dst, src *v1alpha1.LocalModelNodeGroupList) { dst.ListMeta = src.ListMeta }, + func(list *v1alpha1.LocalModelNodeGroupList) []*v1alpha1.LocalModelNodeGroup { + return gentype.ToPointerSlice(list.Items) + }, + func(list *v1alpha1.LocalModelNodeGroupList, items []*v1alpha1.LocalModelNodeGroup) { + list.Items = gentype.FromPointerSlice(items) + }, + ), + fake, } - return obj.(*v1alpha1.LocalModelNodeGroup), err } diff --git a/pkg/client/clientset/versioned/typed/serving/v1alpha1/fake/fake_serving_client.go b/pkg/client/clientset/versioned/typed/serving/v1alpha1/fake/fake_serving_client.go index 89c88a7a9ec..7c1cda295af 100644 --- a/pkg/client/clientset/versioned/typed/serving/v1alpha1/fake/fake_serving_client.go +++ b/pkg/client/clientset/versioned/typed/serving/v1alpha1/fake/fake_serving_client.go @@ -29,35 +29,35 @@ type FakeServingV1alpha1 struct { } func (c *FakeServingV1alpha1) ClusterServingRuntimes(namespace string) v1alpha1.ClusterServingRuntimeInterface { - return &FakeClusterServingRuntimes{c, namespace} + return newFakeClusterServingRuntimes(c, namespace) } func (c *FakeServingV1alpha1) ClusterStorageContainers(namespace string) v1alpha1.ClusterStorageContainerInterface { - return &FakeClusterStorageContainers{c, namespace} + return newFakeClusterStorageContainers(c, namespace) } func (c *FakeServingV1alpha1) InferenceGraphs(namespace string) v1alpha1.InferenceGraphInterface { - return &FakeInferenceGraphs{c, namespace} + return newFakeInferenceGraphs(c, namespace) } func (c *FakeServingV1alpha1) LocalModelCaches(namespace string) v1alpha1.LocalModelCacheInterface { - return &FakeLocalModelCaches{c, namespace} + return newFakeLocalModelCaches(c, namespace) } func (c *FakeServingV1alpha1) LocalModelNodes(namespace string) v1alpha1.LocalModelNodeInterface { - return &FakeLocalModelNodes{c, namespace} + return newFakeLocalModelNodes(c, namespace) } func (c *FakeServingV1alpha1) LocalModelNodeGroups(namespace string) v1alpha1.LocalModelNodeGroupInterface { - return &FakeLocalModelNodeGroups{c, namespace} + return newFakeLocalModelNodeGroups(c, namespace) } func (c *FakeServingV1alpha1) ServingRuntimes(namespace string) v1alpha1.ServingRuntimeInterface { - return &FakeServingRuntimes{c, namespace} + return newFakeServingRuntimes(c, namespace) } func (c *FakeServingV1alpha1) TrainedModels(namespace string) v1alpha1.TrainedModelInterface { - return &FakeTrainedModels{c, namespace} + return newFakeTrainedModels(c, namespace) } // RESTClient returns a RESTClient that is used to communicate diff --git a/pkg/client/clientset/versioned/typed/serving/v1alpha1/fake/fake_servingruntime.go b/pkg/client/clientset/versioned/typed/serving/v1alpha1/fake/fake_servingruntime.go index b311bf1ce7a..f56b42b4f41 100644 --- a/pkg/client/clientset/versioned/typed/serving/v1alpha1/fake/fake_servingruntime.go +++ b/pkg/client/clientset/versioned/typed/serving/v1alpha1/fake/fake_servingruntime.go @@ -19,123 +19,34 @@ limitations under the License. package fake import ( - "context" - v1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" + servingv1alpha1 "github.com/kserve/kserve/pkg/client/clientset/versioned/typed/serving/v1alpha1" + gentype "k8s.io/client-go/gentype" ) -// FakeServingRuntimes implements ServingRuntimeInterface -type FakeServingRuntimes struct { +// fakeServingRuntimes implements ServingRuntimeInterface +type fakeServingRuntimes struct { + *gentype.FakeClientWithList[*v1alpha1.ServingRuntime, *v1alpha1.ServingRuntimeList] Fake *FakeServingV1alpha1 - ns string -} - -var servingruntimesResource = v1alpha1.SchemeGroupVersion.WithResource("servingruntimes") - -var servingruntimesKind = v1alpha1.SchemeGroupVersion.WithKind("ServingRuntime") - -// Get takes name of the servingRuntime, and returns the corresponding servingRuntime object, and an error if there is any. -func (c *FakeServingRuntimes) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.ServingRuntime, err error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(servingruntimesResource, c.ns, name), &v1alpha1.ServingRuntime{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.ServingRuntime), err -} - -// List takes label and field selectors, and returns the list of ServingRuntimes that match those selectors. -func (c *FakeServingRuntimes) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.ServingRuntimeList, err error) { - obj, err := c.Fake. - Invokes(testing.NewListAction(servingruntimesResource, servingruntimesKind, c.ns, opts), &v1alpha1.ServingRuntimeList{}) - - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &v1alpha1.ServingRuntimeList{ListMeta: obj.(*v1alpha1.ServingRuntimeList).ListMeta} - for _, item := range obj.(*v1alpha1.ServingRuntimeList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested servingRuntimes. -func (c *FakeServingRuntimes) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchAction(servingruntimesResource, c.ns, opts)) - -} - -// Create takes the representation of a servingRuntime and creates it. Returns the server's representation of the servingRuntime, and an error, if there is any. -func (c *FakeServingRuntimes) Create(ctx context.Context, servingRuntime *v1alpha1.ServingRuntime, opts v1.CreateOptions) (result *v1alpha1.ServingRuntime, err error) { - obj, err := c.Fake. - Invokes(testing.NewCreateAction(servingruntimesResource, c.ns, servingRuntime), &v1alpha1.ServingRuntime{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.ServingRuntime), err -} - -// Update takes the representation of a servingRuntime and updates it. Returns the server's representation of the servingRuntime, and an error, if there is any. -func (c *FakeServingRuntimes) Update(ctx context.Context, servingRuntime *v1alpha1.ServingRuntime, opts v1.UpdateOptions) (result *v1alpha1.ServingRuntime, err error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateAction(servingruntimesResource, c.ns, servingRuntime), &v1alpha1.ServingRuntime{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.ServingRuntime), err -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *FakeServingRuntimes) UpdateStatus(ctx context.Context, servingRuntime *v1alpha1.ServingRuntime, opts v1.UpdateOptions) (*v1alpha1.ServingRuntime, error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateSubresourceAction(servingruntimesResource, "status", c.ns, servingRuntime), &v1alpha1.ServingRuntime{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.ServingRuntime), err -} - -// Delete takes name of the servingRuntime and deletes it. Returns an error if one occurs. -func (c *FakeServingRuntimes) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteActionWithOptions(servingruntimesResource, c.ns, name, opts), &v1alpha1.ServingRuntime{}) - - return err } -// DeleteCollection deletes a collection of objects. -func (c *FakeServingRuntimes) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - action := testing.NewDeleteCollectionAction(servingruntimesResource, c.ns, listOpts) - - _, err := c.Fake.Invokes(action, &v1alpha1.ServingRuntimeList{}) - return err -} - -// Patch applies the patch and returns the patched servingRuntime. -func (c *FakeServingRuntimes) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ServingRuntime, err error) { - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(servingruntimesResource, c.ns, name, pt, data, subresources...), &v1alpha1.ServingRuntime{}) - - if obj == nil { - return nil, err +func newFakeServingRuntimes(fake *FakeServingV1alpha1, namespace string) servingv1alpha1.ServingRuntimeInterface { + return &fakeServingRuntimes{ + gentype.NewFakeClientWithList[*v1alpha1.ServingRuntime, *v1alpha1.ServingRuntimeList]( + fake.Fake, + namespace, + v1alpha1.SchemeGroupVersion.WithResource("servingruntimes"), + v1alpha1.SchemeGroupVersion.WithKind("ServingRuntime"), + func() *v1alpha1.ServingRuntime { return &v1alpha1.ServingRuntime{} }, + func() *v1alpha1.ServingRuntimeList { return &v1alpha1.ServingRuntimeList{} }, + func(dst, src *v1alpha1.ServingRuntimeList) { dst.ListMeta = src.ListMeta }, + func(list *v1alpha1.ServingRuntimeList) []*v1alpha1.ServingRuntime { + return gentype.ToPointerSlice(list.Items) + }, + func(list *v1alpha1.ServingRuntimeList, items []*v1alpha1.ServingRuntime) { + list.Items = gentype.FromPointerSlice(items) + }, + ), + fake, } - return obj.(*v1alpha1.ServingRuntime), err } diff --git a/pkg/client/clientset/versioned/typed/serving/v1alpha1/fake/fake_trainedmodel.go b/pkg/client/clientset/versioned/typed/serving/v1alpha1/fake/fake_trainedmodel.go index ed2511cb248..1679ce19cba 100644 --- a/pkg/client/clientset/versioned/typed/serving/v1alpha1/fake/fake_trainedmodel.go +++ b/pkg/client/clientset/versioned/typed/serving/v1alpha1/fake/fake_trainedmodel.go @@ -19,123 +19,34 @@ limitations under the License. package fake import ( - "context" - v1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" + servingv1alpha1 "github.com/kserve/kserve/pkg/client/clientset/versioned/typed/serving/v1alpha1" + gentype "k8s.io/client-go/gentype" ) -// FakeTrainedModels implements TrainedModelInterface -type FakeTrainedModels struct { +// fakeTrainedModels implements TrainedModelInterface +type fakeTrainedModels struct { + *gentype.FakeClientWithList[*v1alpha1.TrainedModel, *v1alpha1.TrainedModelList] Fake *FakeServingV1alpha1 - ns string -} - -var trainedmodelsResource = v1alpha1.SchemeGroupVersion.WithResource("trainedmodels") - -var trainedmodelsKind = v1alpha1.SchemeGroupVersion.WithKind("TrainedModel") - -// Get takes name of the trainedModel, and returns the corresponding trainedModel object, and an error if there is any. -func (c *FakeTrainedModels) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.TrainedModel, err error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(trainedmodelsResource, c.ns, name), &v1alpha1.TrainedModel{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.TrainedModel), err -} - -// List takes label and field selectors, and returns the list of TrainedModels that match those selectors. -func (c *FakeTrainedModels) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.TrainedModelList, err error) { - obj, err := c.Fake. - Invokes(testing.NewListAction(trainedmodelsResource, trainedmodelsKind, c.ns, opts), &v1alpha1.TrainedModelList{}) - - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &v1alpha1.TrainedModelList{ListMeta: obj.(*v1alpha1.TrainedModelList).ListMeta} - for _, item := range obj.(*v1alpha1.TrainedModelList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested trainedModels. -func (c *FakeTrainedModels) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchAction(trainedmodelsResource, c.ns, opts)) - -} - -// Create takes the representation of a trainedModel and creates it. Returns the server's representation of the trainedModel, and an error, if there is any. -func (c *FakeTrainedModels) Create(ctx context.Context, trainedModel *v1alpha1.TrainedModel, opts v1.CreateOptions) (result *v1alpha1.TrainedModel, err error) { - obj, err := c.Fake. - Invokes(testing.NewCreateAction(trainedmodelsResource, c.ns, trainedModel), &v1alpha1.TrainedModel{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.TrainedModel), err -} - -// Update takes the representation of a trainedModel and updates it. Returns the server's representation of the trainedModel, and an error, if there is any. -func (c *FakeTrainedModels) Update(ctx context.Context, trainedModel *v1alpha1.TrainedModel, opts v1.UpdateOptions) (result *v1alpha1.TrainedModel, err error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateAction(trainedmodelsResource, c.ns, trainedModel), &v1alpha1.TrainedModel{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.TrainedModel), err -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *FakeTrainedModels) UpdateStatus(ctx context.Context, trainedModel *v1alpha1.TrainedModel, opts v1.UpdateOptions) (*v1alpha1.TrainedModel, error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateSubresourceAction(trainedmodelsResource, "status", c.ns, trainedModel), &v1alpha1.TrainedModel{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.TrainedModel), err -} - -// Delete takes name of the trainedModel and deletes it. Returns an error if one occurs. -func (c *FakeTrainedModels) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteActionWithOptions(trainedmodelsResource, c.ns, name, opts), &v1alpha1.TrainedModel{}) - - return err } -// DeleteCollection deletes a collection of objects. -func (c *FakeTrainedModels) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - action := testing.NewDeleteCollectionAction(trainedmodelsResource, c.ns, listOpts) - - _, err := c.Fake.Invokes(action, &v1alpha1.TrainedModelList{}) - return err -} - -// Patch applies the patch and returns the patched trainedModel. -func (c *FakeTrainedModels) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.TrainedModel, err error) { - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(trainedmodelsResource, c.ns, name, pt, data, subresources...), &v1alpha1.TrainedModel{}) - - if obj == nil { - return nil, err +func newFakeTrainedModels(fake *FakeServingV1alpha1, namespace string) servingv1alpha1.TrainedModelInterface { + return &fakeTrainedModels{ + gentype.NewFakeClientWithList[*v1alpha1.TrainedModel, *v1alpha1.TrainedModelList]( + fake.Fake, + namespace, + v1alpha1.SchemeGroupVersion.WithResource("trainedmodels"), + v1alpha1.SchemeGroupVersion.WithKind("TrainedModel"), + func() *v1alpha1.TrainedModel { return &v1alpha1.TrainedModel{} }, + func() *v1alpha1.TrainedModelList { return &v1alpha1.TrainedModelList{} }, + func(dst, src *v1alpha1.TrainedModelList) { dst.ListMeta = src.ListMeta }, + func(list *v1alpha1.TrainedModelList) []*v1alpha1.TrainedModel { + return gentype.ToPointerSlice(list.Items) + }, + func(list *v1alpha1.TrainedModelList, items []*v1alpha1.TrainedModel) { + list.Items = gentype.FromPointerSlice(items) + }, + ), + fake, } - return obj.(*v1alpha1.TrainedModel), err } diff --git a/pkg/client/clientset/versioned/typed/serving/v1alpha1/inferencegraph.go b/pkg/client/clientset/versioned/typed/serving/v1alpha1/inferencegraph.go index 09dd6f439d8..1b77daabaf7 100644 --- a/pkg/client/clientset/versioned/typed/serving/v1alpha1/inferencegraph.go +++ b/pkg/client/clientset/versioned/typed/serving/v1alpha1/inferencegraph.go @@ -19,15 +19,14 @@ limitations under the License. package v1alpha1 import ( - "context" - "time" + context "context" - v1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" + servingv1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" scheme "github.com/kserve/kserve/pkg/client/clientset/versioned/scheme" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" types "k8s.io/apimachinery/pkg/types" watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" + gentype "k8s.io/client-go/gentype" ) // InferenceGraphsGetter has a method to return a InferenceGraphInterface. @@ -38,158 +37,34 @@ type InferenceGraphsGetter interface { // InferenceGraphInterface has methods to work with InferenceGraph resources. type InferenceGraphInterface interface { - Create(ctx context.Context, inferenceGraph *v1alpha1.InferenceGraph, opts v1.CreateOptions) (*v1alpha1.InferenceGraph, error) - Update(ctx context.Context, inferenceGraph *v1alpha1.InferenceGraph, opts v1.UpdateOptions) (*v1alpha1.InferenceGraph, error) - UpdateStatus(ctx context.Context, inferenceGraph *v1alpha1.InferenceGraph, opts v1.UpdateOptions) (*v1alpha1.InferenceGraph, error) + Create(ctx context.Context, inferenceGraph *servingv1alpha1.InferenceGraph, opts v1.CreateOptions) (*servingv1alpha1.InferenceGraph, error) + Update(ctx context.Context, inferenceGraph *servingv1alpha1.InferenceGraph, opts v1.UpdateOptions) (*servingv1alpha1.InferenceGraph, error) + // Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). + UpdateStatus(ctx context.Context, inferenceGraph *servingv1alpha1.InferenceGraph, opts v1.UpdateOptions) (*servingv1alpha1.InferenceGraph, error) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error - Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.InferenceGraph, error) - List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.InferenceGraphList, error) + Get(ctx context.Context, name string, opts v1.GetOptions) (*servingv1alpha1.InferenceGraph, error) + List(ctx context.Context, opts v1.ListOptions) (*servingv1alpha1.InferenceGraphList, error) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) - Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.InferenceGraph, err error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *servingv1alpha1.InferenceGraph, err error) InferenceGraphExpansion } // inferenceGraphs implements InferenceGraphInterface type inferenceGraphs struct { - client rest.Interface - ns string + *gentype.ClientWithList[*servingv1alpha1.InferenceGraph, *servingv1alpha1.InferenceGraphList] } // newInferenceGraphs returns a InferenceGraphs func newInferenceGraphs(c *ServingV1alpha1Client, namespace string) *inferenceGraphs { return &inferenceGraphs{ - client: c.RESTClient(), - ns: namespace, + gentype.NewClientWithList[*servingv1alpha1.InferenceGraph, *servingv1alpha1.InferenceGraphList]( + "inferencegraphs", + c.RESTClient(), + scheme.ParameterCodec, + namespace, + func() *servingv1alpha1.InferenceGraph { return &servingv1alpha1.InferenceGraph{} }, + func() *servingv1alpha1.InferenceGraphList { return &servingv1alpha1.InferenceGraphList{} }, + ), } } - -// Get takes name of the inferenceGraph, and returns the corresponding inferenceGraph object, and an error if there is any. -func (c *inferenceGraphs) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.InferenceGraph, err error) { - result = &v1alpha1.InferenceGraph{} - err = c.client.Get(). - Namespace(c.ns). - Resource("inferencegraphs"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(ctx). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of InferenceGraphs that match those selectors. -func (c *inferenceGraphs) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.InferenceGraphList, err error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - result = &v1alpha1.InferenceGraphList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("inferencegraphs"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Do(ctx). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested inferenceGraphs. -func (c *inferenceGraphs) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("inferencegraphs"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Watch(ctx) -} - -// Create takes the representation of a inferenceGraph and creates it. Returns the server's representation of the inferenceGraph, and an error, if there is any. -func (c *inferenceGraphs) Create(ctx context.Context, inferenceGraph *v1alpha1.InferenceGraph, opts v1.CreateOptions) (result *v1alpha1.InferenceGraph, err error) { - result = &v1alpha1.InferenceGraph{} - err = c.client.Post(). - Namespace(c.ns). - Resource("inferencegraphs"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(inferenceGraph). - Do(ctx). - Into(result) - return -} - -// Update takes the representation of a inferenceGraph and updates it. Returns the server's representation of the inferenceGraph, and an error, if there is any. -func (c *inferenceGraphs) Update(ctx context.Context, inferenceGraph *v1alpha1.InferenceGraph, opts v1.UpdateOptions) (result *v1alpha1.InferenceGraph, err error) { - result = &v1alpha1.InferenceGraph{} - err = c.client.Put(). - Namespace(c.ns). - Resource("inferencegraphs"). - Name(inferenceGraph.Name). - VersionedParams(&opts, scheme.ParameterCodec). - Body(inferenceGraph). - Do(ctx). - Into(result) - return -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *inferenceGraphs) UpdateStatus(ctx context.Context, inferenceGraph *v1alpha1.InferenceGraph, opts v1.UpdateOptions) (result *v1alpha1.InferenceGraph, err error) { - result = &v1alpha1.InferenceGraph{} - err = c.client.Put(). - Namespace(c.ns). - Resource("inferencegraphs"). - Name(inferenceGraph.Name). - SubResource("status"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(inferenceGraph). - Do(ctx). - Into(result) - return -} - -// Delete takes name of the inferenceGraph and deletes it. Returns an error if one occurs. -func (c *inferenceGraphs) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("inferencegraphs"). - Name(name). - Body(&opts). - Do(ctx). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *inferenceGraphs) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - var timeout time.Duration - if listOpts.TimeoutSeconds != nil { - timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second - } - return c.client.Delete(). - Namespace(c.ns). - Resource("inferencegraphs"). - VersionedParams(&listOpts, scheme.ParameterCodec). - Timeout(timeout). - Body(&opts). - Do(ctx). - Error() -} - -// Patch applies the patch and returns the patched inferenceGraph. -func (c *inferenceGraphs) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.InferenceGraph, err error) { - result = &v1alpha1.InferenceGraph{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("inferencegraphs"). - Name(name). - SubResource(subresources...). - VersionedParams(&opts, scheme.ParameterCodec). - Body(data). - Do(ctx). - Into(result) - return -} diff --git a/pkg/client/clientset/versioned/typed/serving/v1alpha1/localmodelcache.go b/pkg/client/clientset/versioned/typed/serving/v1alpha1/localmodelcache.go index bf81697bb20..7e41f8157df 100644 --- a/pkg/client/clientset/versioned/typed/serving/v1alpha1/localmodelcache.go +++ b/pkg/client/clientset/versioned/typed/serving/v1alpha1/localmodelcache.go @@ -19,15 +19,14 @@ limitations under the License. package v1alpha1 import ( - "context" - "time" + context "context" - v1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" + servingv1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" scheme "github.com/kserve/kserve/pkg/client/clientset/versioned/scheme" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" types "k8s.io/apimachinery/pkg/types" watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" + gentype "k8s.io/client-go/gentype" ) // LocalModelCachesGetter has a method to return a LocalModelCacheInterface. @@ -38,158 +37,34 @@ type LocalModelCachesGetter interface { // LocalModelCacheInterface has methods to work with LocalModelCache resources. type LocalModelCacheInterface interface { - Create(ctx context.Context, localModelCache *v1alpha1.LocalModelCache, opts v1.CreateOptions) (*v1alpha1.LocalModelCache, error) - Update(ctx context.Context, localModelCache *v1alpha1.LocalModelCache, opts v1.UpdateOptions) (*v1alpha1.LocalModelCache, error) - UpdateStatus(ctx context.Context, localModelCache *v1alpha1.LocalModelCache, opts v1.UpdateOptions) (*v1alpha1.LocalModelCache, error) + Create(ctx context.Context, localModelCache *servingv1alpha1.LocalModelCache, opts v1.CreateOptions) (*servingv1alpha1.LocalModelCache, error) + Update(ctx context.Context, localModelCache *servingv1alpha1.LocalModelCache, opts v1.UpdateOptions) (*servingv1alpha1.LocalModelCache, error) + // Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). + UpdateStatus(ctx context.Context, localModelCache *servingv1alpha1.LocalModelCache, opts v1.UpdateOptions) (*servingv1alpha1.LocalModelCache, error) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error - Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.LocalModelCache, error) - List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.LocalModelCacheList, error) + Get(ctx context.Context, name string, opts v1.GetOptions) (*servingv1alpha1.LocalModelCache, error) + List(ctx context.Context, opts v1.ListOptions) (*servingv1alpha1.LocalModelCacheList, error) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) - Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.LocalModelCache, err error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *servingv1alpha1.LocalModelCache, err error) LocalModelCacheExpansion } // localModelCaches implements LocalModelCacheInterface type localModelCaches struct { - client rest.Interface - ns string + *gentype.ClientWithList[*servingv1alpha1.LocalModelCache, *servingv1alpha1.LocalModelCacheList] } // newLocalModelCaches returns a LocalModelCaches func newLocalModelCaches(c *ServingV1alpha1Client, namespace string) *localModelCaches { return &localModelCaches{ - client: c.RESTClient(), - ns: namespace, + gentype.NewClientWithList[*servingv1alpha1.LocalModelCache, *servingv1alpha1.LocalModelCacheList]( + "localmodelcaches", + c.RESTClient(), + scheme.ParameterCodec, + namespace, + func() *servingv1alpha1.LocalModelCache { return &servingv1alpha1.LocalModelCache{} }, + func() *servingv1alpha1.LocalModelCacheList { return &servingv1alpha1.LocalModelCacheList{} }, + ), } } - -// Get takes name of the localModelCache, and returns the corresponding localModelCache object, and an error if there is any. -func (c *localModelCaches) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.LocalModelCache, err error) { - result = &v1alpha1.LocalModelCache{} - err = c.client.Get(). - Namespace(c.ns). - Resource("localmodelcaches"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(ctx). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of LocalModelCaches that match those selectors. -func (c *localModelCaches) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.LocalModelCacheList, err error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - result = &v1alpha1.LocalModelCacheList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("localmodelcaches"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Do(ctx). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested localModelCaches. -func (c *localModelCaches) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("localmodelcaches"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Watch(ctx) -} - -// Create takes the representation of a localModelCache and creates it. Returns the server's representation of the localModelCache, and an error, if there is any. -func (c *localModelCaches) Create(ctx context.Context, localModelCache *v1alpha1.LocalModelCache, opts v1.CreateOptions) (result *v1alpha1.LocalModelCache, err error) { - result = &v1alpha1.LocalModelCache{} - err = c.client.Post(). - Namespace(c.ns). - Resource("localmodelcaches"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(localModelCache). - Do(ctx). - Into(result) - return -} - -// Update takes the representation of a localModelCache and updates it. Returns the server's representation of the localModelCache, and an error, if there is any. -func (c *localModelCaches) Update(ctx context.Context, localModelCache *v1alpha1.LocalModelCache, opts v1.UpdateOptions) (result *v1alpha1.LocalModelCache, err error) { - result = &v1alpha1.LocalModelCache{} - err = c.client.Put(). - Namespace(c.ns). - Resource("localmodelcaches"). - Name(localModelCache.Name). - VersionedParams(&opts, scheme.ParameterCodec). - Body(localModelCache). - Do(ctx). - Into(result) - return -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *localModelCaches) UpdateStatus(ctx context.Context, localModelCache *v1alpha1.LocalModelCache, opts v1.UpdateOptions) (result *v1alpha1.LocalModelCache, err error) { - result = &v1alpha1.LocalModelCache{} - err = c.client.Put(). - Namespace(c.ns). - Resource("localmodelcaches"). - Name(localModelCache.Name). - SubResource("status"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(localModelCache). - Do(ctx). - Into(result) - return -} - -// Delete takes name of the localModelCache and deletes it. Returns an error if one occurs. -func (c *localModelCaches) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("localmodelcaches"). - Name(name). - Body(&opts). - Do(ctx). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *localModelCaches) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - var timeout time.Duration - if listOpts.TimeoutSeconds != nil { - timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second - } - return c.client.Delete(). - Namespace(c.ns). - Resource("localmodelcaches"). - VersionedParams(&listOpts, scheme.ParameterCodec). - Timeout(timeout). - Body(&opts). - Do(ctx). - Error() -} - -// Patch applies the patch and returns the patched localModelCache. -func (c *localModelCaches) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.LocalModelCache, err error) { - result = &v1alpha1.LocalModelCache{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("localmodelcaches"). - Name(name). - SubResource(subresources...). - VersionedParams(&opts, scheme.ParameterCodec). - Body(data). - Do(ctx). - Into(result) - return -} diff --git a/pkg/client/clientset/versioned/typed/serving/v1alpha1/localmodelnode.go b/pkg/client/clientset/versioned/typed/serving/v1alpha1/localmodelnode.go index 03fef113abb..06bb09dc7ca 100644 --- a/pkg/client/clientset/versioned/typed/serving/v1alpha1/localmodelnode.go +++ b/pkg/client/clientset/versioned/typed/serving/v1alpha1/localmodelnode.go @@ -19,15 +19,14 @@ limitations under the License. package v1alpha1 import ( - "context" - "time" + context "context" - v1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" + servingv1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" scheme "github.com/kserve/kserve/pkg/client/clientset/versioned/scheme" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" types "k8s.io/apimachinery/pkg/types" watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" + gentype "k8s.io/client-go/gentype" ) // LocalModelNodesGetter has a method to return a LocalModelNodeInterface. @@ -38,158 +37,34 @@ type LocalModelNodesGetter interface { // LocalModelNodeInterface has methods to work with LocalModelNode resources. type LocalModelNodeInterface interface { - Create(ctx context.Context, localModelNode *v1alpha1.LocalModelNode, opts v1.CreateOptions) (*v1alpha1.LocalModelNode, error) - Update(ctx context.Context, localModelNode *v1alpha1.LocalModelNode, opts v1.UpdateOptions) (*v1alpha1.LocalModelNode, error) - UpdateStatus(ctx context.Context, localModelNode *v1alpha1.LocalModelNode, opts v1.UpdateOptions) (*v1alpha1.LocalModelNode, error) + Create(ctx context.Context, localModelNode *servingv1alpha1.LocalModelNode, opts v1.CreateOptions) (*servingv1alpha1.LocalModelNode, error) + Update(ctx context.Context, localModelNode *servingv1alpha1.LocalModelNode, opts v1.UpdateOptions) (*servingv1alpha1.LocalModelNode, error) + // Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). + UpdateStatus(ctx context.Context, localModelNode *servingv1alpha1.LocalModelNode, opts v1.UpdateOptions) (*servingv1alpha1.LocalModelNode, error) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error - Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.LocalModelNode, error) - List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.LocalModelNodeList, error) + Get(ctx context.Context, name string, opts v1.GetOptions) (*servingv1alpha1.LocalModelNode, error) + List(ctx context.Context, opts v1.ListOptions) (*servingv1alpha1.LocalModelNodeList, error) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) - Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.LocalModelNode, err error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *servingv1alpha1.LocalModelNode, err error) LocalModelNodeExpansion } // localModelNodes implements LocalModelNodeInterface type localModelNodes struct { - client rest.Interface - ns string + *gentype.ClientWithList[*servingv1alpha1.LocalModelNode, *servingv1alpha1.LocalModelNodeList] } // newLocalModelNodes returns a LocalModelNodes func newLocalModelNodes(c *ServingV1alpha1Client, namespace string) *localModelNodes { return &localModelNodes{ - client: c.RESTClient(), - ns: namespace, + gentype.NewClientWithList[*servingv1alpha1.LocalModelNode, *servingv1alpha1.LocalModelNodeList]( + "localmodelnodes", + c.RESTClient(), + scheme.ParameterCodec, + namespace, + func() *servingv1alpha1.LocalModelNode { return &servingv1alpha1.LocalModelNode{} }, + func() *servingv1alpha1.LocalModelNodeList { return &servingv1alpha1.LocalModelNodeList{} }, + ), } } - -// Get takes name of the localModelNode, and returns the corresponding localModelNode object, and an error if there is any. -func (c *localModelNodes) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.LocalModelNode, err error) { - result = &v1alpha1.LocalModelNode{} - err = c.client.Get(). - Namespace(c.ns). - Resource("localmodelnodes"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(ctx). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of LocalModelNodes that match those selectors. -func (c *localModelNodes) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.LocalModelNodeList, err error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - result = &v1alpha1.LocalModelNodeList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("localmodelnodes"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Do(ctx). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested localModelNodes. -func (c *localModelNodes) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("localmodelnodes"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Watch(ctx) -} - -// Create takes the representation of a localModelNode and creates it. Returns the server's representation of the localModelNode, and an error, if there is any. -func (c *localModelNodes) Create(ctx context.Context, localModelNode *v1alpha1.LocalModelNode, opts v1.CreateOptions) (result *v1alpha1.LocalModelNode, err error) { - result = &v1alpha1.LocalModelNode{} - err = c.client.Post(). - Namespace(c.ns). - Resource("localmodelnodes"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(localModelNode). - Do(ctx). - Into(result) - return -} - -// Update takes the representation of a localModelNode and updates it. Returns the server's representation of the localModelNode, and an error, if there is any. -func (c *localModelNodes) Update(ctx context.Context, localModelNode *v1alpha1.LocalModelNode, opts v1.UpdateOptions) (result *v1alpha1.LocalModelNode, err error) { - result = &v1alpha1.LocalModelNode{} - err = c.client.Put(). - Namespace(c.ns). - Resource("localmodelnodes"). - Name(localModelNode.Name). - VersionedParams(&opts, scheme.ParameterCodec). - Body(localModelNode). - Do(ctx). - Into(result) - return -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *localModelNodes) UpdateStatus(ctx context.Context, localModelNode *v1alpha1.LocalModelNode, opts v1.UpdateOptions) (result *v1alpha1.LocalModelNode, err error) { - result = &v1alpha1.LocalModelNode{} - err = c.client.Put(). - Namespace(c.ns). - Resource("localmodelnodes"). - Name(localModelNode.Name). - SubResource("status"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(localModelNode). - Do(ctx). - Into(result) - return -} - -// Delete takes name of the localModelNode and deletes it. Returns an error if one occurs. -func (c *localModelNodes) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("localmodelnodes"). - Name(name). - Body(&opts). - Do(ctx). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *localModelNodes) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - var timeout time.Duration - if listOpts.TimeoutSeconds != nil { - timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second - } - return c.client.Delete(). - Namespace(c.ns). - Resource("localmodelnodes"). - VersionedParams(&listOpts, scheme.ParameterCodec). - Timeout(timeout). - Body(&opts). - Do(ctx). - Error() -} - -// Patch applies the patch and returns the patched localModelNode. -func (c *localModelNodes) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.LocalModelNode, err error) { - result = &v1alpha1.LocalModelNode{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("localmodelnodes"). - Name(name). - SubResource(subresources...). - VersionedParams(&opts, scheme.ParameterCodec). - Body(data). - Do(ctx). - Into(result) - return -} diff --git a/pkg/client/clientset/versioned/typed/serving/v1alpha1/localmodelnodegroup.go b/pkg/client/clientset/versioned/typed/serving/v1alpha1/localmodelnodegroup.go index 1d4e471421e..fecb2599f12 100644 --- a/pkg/client/clientset/versioned/typed/serving/v1alpha1/localmodelnodegroup.go +++ b/pkg/client/clientset/versioned/typed/serving/v1alpha1/localmodelnodegroup.go @@ -19,15 +19,14 @@ limitations under the License. package v1alpha1 import ( - "context" - "time" + context "context" - v1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" + servingv1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" scheme "github.com/kserve/kserve/pkg/client/clientset/versioned/scheme" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" types "k8s.io/apimachinery/pkg/types" watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" + gentype "k8s.io/client-go/gentype" ) // LocalModelNodeGroupsGetter has a method to return a LocalModelNodeGroupInterface. @@ -38,158 +37,34 @@ type LocalModelNodeGroupsGetter interface { // LocalModelNodeGroupInterface has methods to work with LocalModelNodeGroup resources. type LocalModelNodeGroupInterface interface { - Create(ctx context.Context, localModelNodeGroup *v1alpha1.LocalModelNodeGroup, opts v1.CreateOptions) (*v1alpha1.LocalModelNodeGroup, error) - Update(ctx context.Context, localModelNodeGroup *v1alpha1.LocalModelNodeGroup, opts v1.UpdateOptions) (*v1alpha1.LocalModelNodeGroup, error) - UpdateStatus(ctx context.Context, localModelNodeGroup *v1alpha1.LocalModelNodeGroup, opts v1.UpdateOptions) (*v1alpha1.LocalModelNodeGroup, error) + Create(ctx context.Context, localModelNodeGroup *servingv1alpha1.LocalModelNodeGroup, opts v1.CreateOptions) (*servingv1alpha1.LocalModelNodeGroup, error) + Update(ctx context.Context, localModelNodeGroup *servingv1alpha1.LocalModelNodeGroup, opts v1.UpdateOptions) (*servingv1alpha1.LocalModelNodeGroup, error) + // Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). + UpdateStatus(ctx context.Context, localModelNodeGroup *servingv1alpha1.LocalModelNodeGroup, opts v1.UpdateOptions) (*servingv1alpha1.LocalModelNodeGroup, error) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error - Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.LocalModelNodeGroup, error) - List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.LocalModelNodeGroupList, error) + Get(ctx context.Context, name string, opts v1.GetOptions) (*servingv1alpha1.LocalModelNodeGroup, error) + List(ctx context.Context, opts v1.ListOptions) (*servingv1alpha1.LocalModelNodeGroupList, error) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) - Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.LocalModelNodeGroup, err error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *servingv1alpha1.LocalModelNodeGroup, err error) LocalModelNodeGroupExpansion } // localModelNodeGroups implements LocalModelNodeGroupInterface type localModelNodeGroups struct { - client rest.Interface - ns string + *gentype.ClientWithList[*servingv1alpha1.LocalModelNodeGroup, *servingv1alpha1.LocalModelNodeGroupList] } // newLocalModelNodeGroups returns a LocalModelNodeGroups func newLocalModelNodeGroups(c *ServingV1alpha1Client, namespace string) *localModelNodeGroups { return &localModelNodeGroups{ - client: c.RESTClient(), - ns: namespace, + gentype.NewClientWithList[*servingv1alpha1.LocalModelNodeGroup, *servingv1alpha1.LocalModelNodeGroupList]( + "localmodelnodegroups", + c.RESTClient(), + scheme.ParameterCodec, + namespace, + func() *servingv1alpha1.LocalModelNodeGroup { return &servingv1alpha1.LocalModelNodeGroup{} }, + func() *servingv1alpha1.LocalModelNodeGroupList { return &servingv1alpha1.LocalModelNodeGroupList{} }, + ), } } - -// Get takes name of the localModelNodeGroup, and returns the corresponding localModelNodeGroup object, and an error if there is any. -func (c *localModelNodeGroups) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.LocalModelNodeGroup, err error) { - result = &v1alpha1.LocalModelNodeGroup{} - err = c.client.Get(). - Namespace(c.ns). - Resource("localmodelnodegroups"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(ctx). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of LocalModelNodeGroups that match those selectors. -func (c *localModelNodeGroups) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.LocalModelNodeGroupList, err error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - result = &v1alpha1.LocalModelNodeGroupList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("localmodelnodegroups"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Do(ctx). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested localModelNodeGroups. -func (c *localModelNodeGroups) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("localmodelnodegroups"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Watch(ctx) -} - -// Create takes the representation of a localModelNodeGroup and creates it. Returns the server's representation of the localModelNodeGroup, and an error, if there is any. -func (c *localModelNodeGroups) Create(ctx context.Context, localModelNodeGroup *v1alpha1.LocalModelNodeGroup, opts v1.CreateOptions) (result *v1alpha1.LocalModelNodeGroup, err error) { - result = &v1alpha1.LocalModelNodeGroup{} - err = c.client.Post(). - Namespace(c.ns). - Resource("localmodelnodegroups"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(localModelNodeGroup). - Do(ctx). - Into(result) - return -} - -// Update takes the representation of a localModelNodeGroup and updates it. Returns the server's representation of the localModelNodeGroup, and an error, if there is any. -func (c *localModelNodeGroups) Update(ctx context.Context, localModelNodeGroup *v1alpha1.LocalModelNodeGroup, opts v1.UpdateOptions) (result *v1alpha1.LocalModelNodeGroup, err error) { - result = &v1alpha1.LocalModelNodeGroup{} - err = c.client.Put(). - Namespace(c.ns). - Resource("localmodelnodegroups"). - Name(localModelNodeGroup.Name). - VersionedParams(&opts, scheme.ParameterCodec). - Body(localModelNodeGroup). - Do(ctx). - Into(result) - return -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *localModelNodeGroups) UpdateStatus(ctx context.Context, localModelNodeGroup *v1alpha1.LocalModelNodeGroup, opts v1.UpdateOptions) (result *v1alpha1.LocalModelNodeGroup, err error) { - result = &v1alpha1.LocalModelNodeGroup{} - err = c.client.Put(). - Namespace(c.ns). - Resource("localmodelnodegroups"). - Name(localModelNodeGroup.Name). - SubResource("status"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(localModelNodeGroup). - Do(ctx). - Into(result) - return -} - -// Delete takes name of the localModelNodeGroup and deletes it. Returns an error if one occurs. -func (c *localModelNodeGroups) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("localmodelnodegroups"). - Name(name). - Body(&opts). - Do(ctx). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *localModelNodeGroups) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - var timeout time.Duration - if listOpts.TimeoutSeconds != nil { - timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second - } - return c.client.Delete(). - Namespace(c.ns). - Resource("localmodelnodegroups"). - VersionedParams(&listOpts, scheme.ParameterCodec). - Timeout(timeout). - Body(&opts). - Do(ctx). - Error() -} - -// Patch applies the patch and returns the patched localModelNodeGroup. -func (c *localModelNodeGroups) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.LocalModelNodeGroup, err error) { - result = &v1alpha1.LocalModelNodeGroup{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("localmodelnodegroups"). - Name(name). - SubResource(subresources...). - VersionedParams(&opts, scheme.ParameterCodec). - Body(data). - Do(ctx). - Into(result) - return -} diff --git a/pkg/client/clientset/versioned/typed/serving/v1alpha1/serving_client.go b/pkg/client/clientset/versioned/typed/serving/v1alpha1/serving_client.go index e9e63a138ea..565538de46b 100644 --- a/pkg/client/clientset/versioned/typed/serving/v1alpha1/serving_client.go +++ b/pkg/client/clientset/versioned/typed/serving/v1alpha1/serving_client.go @@ -19,10 +19,10 @@ limitations under the License. package v1alpha1 import ( - "net/http" + http "net/http" - v1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" - "github.com/kserve/kserve/pkg/client/clientset/versioned/scheme" + servingv1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" + scheme "github.com/kserve/kserve/pkg/client/clientset/versioned/scheme" rest "k8s.io/client-go/rest" ) @@ -120,10 +120,10 @@ func New(c rest.Interface) *ServingV1alpha1Client { } func setConfigDefaults(config *rest.Config) error { - gv := v1alpha1.SchemeGroupVersion + gv := servingv1alpha1.SchemeGroupVersion config.GroupVersion = &gv config.APIPath = "/apis" - config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() + config.NegotiatedSerializer = rest.CodecFactoryForGeneratedClient(scheme.Scheme, scheme.Codecs).WithoutConversion() if config.UserAgent == "" { config.UserAgent = rest.DefaultKubernetesUserAgent() diff --git a/pkg/client/clientset/versioned/typed/serving/v1alpha1/servingruntime.go b/pkg/client/clientset/versioned/typed/serving/v1alpha1/servingruntime.go index 252db9821c3..1a1a0e283a8 100644 --- a/pkg/client/clientset/versioned/typed/serving/v1alpha1/servingruntime.go +++ b/pkg/client/clientset/versioned/typed/serving/v1alpha1/servingruntime.go @@ -19,15 +19,14 @@ limitations under the License. package v1alpha1 import ( - "context" - "time" + context "context" - v1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" + servingv1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" scheme "github.com/kserve/kserve/pkg/client/clientset/versioned/scheme" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" types "k8s.io/apimachinery/pkg/types" watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" + gentype "k8s.io/client-go/gentype" ) // ServingRuntimesGetter has a method to return a ServingRuntimeInterface. @@ -38,158 +37,34 @@ type ServingRuntimesGetter interface { // ServingRuntimeInterface has methods to work with ServingRuntime resources. type ServingRuntimeInterface interface { - Create(ctx context.Context, servingRuntime *v1alpha1.ServingRuntime, opts v1.CreateOptions) (*v1alpha1.ServingRuntime, error) - Update(ctx context.Context, servingRuntime *v1alpha1.ServingRuntime, opts v1.UpdateOptions) (*v1alpha1.ServingRuntime, error) - UpdateStatus(ctx context.Context, servingRuntime *v1alpha1.ServingRuntime, opts v1.UpdateOptions) (*v1alpha1.ServingRuntime, error) + Create(ctx context.Context, servingRuntime *servingv1alpha1.ServingRuntime, opts v1.CreateOptions) (*servingv1alpha1.ServingRuntime, error) + Update(ctx context.Context, servingRuntime *servingv1alpha1.ServingRuntime, opts v1.UpdateOptions) (*servingv1alpha1.ServingRuntime, error) + // Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). + UpdateStatus(ctx context.Context, servingRuntime *servingv1alpha1.ServingRuntime, opts v1.UpdateOptions) (*servingv1alpha1.ServingRuntime, error) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error - Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.ServingRuntime, error) - List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.ServingRuntimeList, error) + Get(ctx context.Context, name string, opts v1.GetOptions) (*servingv1alpha1.ServingRuntime, error) + List(ctx context.Context, opts v1.ListOptions) (*servingv1alpha1.ServingRuntimeList, error) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) - Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ServingRuntime, err error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *servingv1alpha1.ServingRuntime, err error) ServingRuntimeExpansion } // servingRuntimes implements ServingRuntimeInterface type servingRuntimes struct { - client rest.Interface - ns string + *gentype.ClientWithList[*servingv1alpha1.ServingRuntime, *servingv1alpha1.ServingRuntimeList] } // newServingRuntimes returns a ServingRuntimes func newServingRuntimes(c *ServingV1alpha1Client, namespace string) *servingRuntimes { return &servingRuntimes{ - client: c.RESTClient(), - ns: namespace, + gentype.NewClientWithList[*servingv1alpha1.ServingRuntime, *servingv1alpha1.ServingRuntimeList]( + "servingruntimes", + c.RESTClient(), + scheme.ParameterCodec, + namespace, + func() *servingv1alpha1.ServingRuntime { return &servingv1alpha1.ServingRuntime{} }, + func() *servingv1alpha1.ServingRuntimeList { return &servingv1alpha1.ServingRuntimeList{} }, + ), } } - -// Get takes name of the servingRuntime, and returns the corresponding servingRuntime object, and an error if there is any. -func (c *servingRuntimes) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.ServingRuntime, err error) { - result = &v1alpha1.ServingRuntime{} - err = c.client.Get(). - Namespace(c.ns). - Resource("servingruntimes"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(ctx). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of ServingRuntimes that match those selectors. -func (c *servingRuntimes) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.ServingRuntimeList, err error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - result = &v1alpha1.ServingRuntimeList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("servingruntimes"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Do(ctx). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested servingRuntimes. -func (c *servingRuntimes) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("servingruntimes"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Watch(ctx) -} - -// Create takes the representation of a servingRuntime and creates it. Returns the server's representation of the servingRuntime, and an error, if there is any. -func (c *servingRuntimes) Create(ctx context.Context, servingRuntime *v1alpha1.ServingRuntime, opts v1.CreateOptions) (result *v1alpha1.ServingRuntime, err error) { - result = &v1alpha1.ServingRuntime{} - err = c.client.Post(). - Namespace(c.ns). - Resource("servingruntimes"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(servingRuntime). - Do(ctx). - Into(result) - return -} - -// Update takes the representation of a servingRuntime and updates it. Returns the server's representation of the servingRuntime, and an error, if there is any. -func (c *servingRuntimes) Update(ctx context.Context, servingRuntime *v1alpha1.ServingRuntime, opts v1.UpdateOptions) (result *v1alpha1.ServingRuntime, err error) { - result = &v1alpha1.ServingRuntime{} - err = c.client.Put(). - Namespace(c.ns). - Resource("servingruntimes"). - Name(servingRuntime.Name). - VersionedParams(&opts, scheme.ParameterCodec). - Body(servingRuntime). - Do(ctx). - Into(result) - return -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *servingRuntimes) UpdateStatus(ctx context.Context, servingRuntime *v1alpha1.ServingRuntime, opts v1.UpdateOptions) (result *v1alpha1.ServingRuntime, err error) { - result = &v1alpha1.ServingRuntime{} - err = c.client.Put(). - Namespace(c.ns). - Resource("servingruntimes"). - Name(servingRuntime.Name). - SubResource("status"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(servingRuntime). - Do(ctx). - Into(result) - return -} - -// Delete takes name of the servingRuntime and deletes it. Returns an error if one occurs. -func (c *servingRuntimes) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("servingruntimes"). - Name(name). - Body(&opts). - Do(ctx). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *servingRuntimes) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - var timeout time.Duration - if listOpts.TimeoutSeconds != nil { - timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second - } - return c.client.Delete(). - Namespace(c.ns). - Resource("servingruntimes"). - VersionedParams(&listOpts, scheme.ParameterCodec). - Timeout(timeout). - Body(&opts). - Do(ctx). - Error() -} - -// Patch applies the patch and returns the patched servingRuntime. -func (c *servingRuntimes) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ServingRuntime, err error) { - result = &v1alpha1.ServingRuntime{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("servingruntimes"). - Name(name). - SubResource(subresources...). - VersionedParams(&opts, scheme.ParameterCodec). - Body(data). - Do(ctx). - Into(result) - return -} diff --git a/pkg/client/clientset/versioned/typed/serving/v1alpha1/trainedmodel.go b/pkg/client/clientset/versioned/typed/serving/v1alpha1/trainedmodel.go index 8912c92a333..1b6d939dcce 100644 --- a/pkg/client/clientset/versioned/typed/serving/v1alpha1/trainedmodel.go +++ b/pkg/client/clientset/versioned/typed/serving/v1alpha1/trainedmodel.go @@ -19,15 +19,14 @@ limitations under the License. package v1alpha1 import ( - "context" - "time" + context "context" - v1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" + servingv1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" scheme "github.com/kserve/kserve/pkg/client/clientset/versioned/scheme" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" types "k8s.io/apimachinery/pkg/types" watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" + gentype "k8s.io/client-go/gentype" ) // TrainedModelsGetter has a method to return a TrainedModelInterface. @@ -38,158 +37,34 @@ type TrainedModelsGetter interface { // TrainedModelInterface has methods to work with TrainedModel resources. type TrainedModelInterface interface { - Create(ctx context.Context, trainedModel *v1alpha1.TrainedModel, opts v1.CreateOptions) (*v1alpha1.TrainedModel, error) - Update(ctx context.Context, trainedModel *v1alpha1.TrainedModel, opts v1.UpdateOptions) (*v1alpha1.TrainedModel, error) - UpdateStatus(ctx context.Context, trainedModel *v1alpha1.TrainedModel, opts v1.UpdateOptions) (*v1alpha1.TrainedModel, error) + Create(ctx context.Context, trainedModel *servingv1alpha1.TrainedModel, opts v1.CreateOptions) (*servingv1alpha1.TrainedModel, error) + Update(ctx context.Context, trainedModel *servingv1alpha1.TrainedModel, opts v1.UpdateOptions) (*servingv1alpha1.TrainedModel, error) + // Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). + UpdateStatus(ctx context.Context, trainedModel *servingv1alpha1.TrainedModel, opts v1.UpdateOptions) (*servingv1alpha1.TrainedModel, error) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error - Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.TrainedModel, error) - List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.TrainedModelList, error) + Get(ctx context.Context, name string, opts v1.GetOptions) (*servingv1alpha1.TrainedModel, error) + List(ctx context.Context, opts v1.ListOptions) (*servingv1alpha1.TrainedModelList, error) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) - Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.TrainedModel, err error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *servingv1alpha1.TrainedModel, err error) TrainedModelExpansion } // trainedModels implements TrainedModelInterface type trainedModels struct { - client rest.Interface - ns string + *gentype.ClientWithList[*servingv1alpha1.TrainedModel, *servingv1alpha1.TrainedModelList] } // newTrainedModels returns a TrainedModels func newTrainedModels(c *ServingV1alpha1Client, namespace string) *trainedModels { return &trainedModels{ - client: c.RESTClient(), - ns: namespace, + gentype.NewClientWithList[*servingv1alpha1.TrainedModel, *servingv1alpha1.TrainedModelList]( + "trainedmodels", + c.RESTClient(), + scheme.ParameterCodec, + namespace, + func() *servingv1alpha1.TrainedModel { return &servingv1alpha1.TrainedModel{} }, + func() *servingv1alpha1.TrainedModelList { return &servingv1alpha1.TrainedModelList{} }, + ), } } - -// Get takes name of the trainedModel, and returns the corresponding trainedModel object, and an error if there is any. -func (c *trainedModels) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.TrainedModel, err error) { - result = &v1alpha1.TrainedModel{} - err = c.client.Get(). - Namespace(c.ns). - Resource("trainedmodels"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(ctx). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of TrainedModels that match those selectors. -func (c *trainedModels) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.TrainedModelList, err error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - result = &v1alpha1.TrainedModelList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("trainedmodels"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Do(ctx). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested trainedModels. -func (c *trainedModels) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("trainedmodels"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Watch(ctx) -} - -// Create takes the representation of a trainedModel and creates it. Returns the server's representation of the trainedModel, and an error, if there is any. -func (c *trainedModels) Create(ctx context.Context, trainedModel *v1alpha1.TrainedModel, opts v1.CreateOptions) (result *v1alpha1.TrainedModel, err error) { - result = &v1alpha1.TrainedModel{} - err = c.client.Post(). - Namespace(c.ns). - Resource("trainedmodels"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(trainedModel). - Do(ctx). - Into(result) - return -} - -// Update takes the representation of a trainedModel and updates it. Returns the server's representation of the trainedModel, and an error, if there is any. -func (c *trainedModels) Update(ctx context.Context, trainedModel *v1alpha1.TrainedModel, opts v1.UpdateOptions) (result *v1alpha1.TrainedModel, err error) { - result = &v1alpha1.TrainedModel{} - err = c.client.Put(). - Namespace(c.ns). - Resource("trainedmodels"). - Name(trainedModel.Name). - VersionedParams(&opts, scheme.ParameterCodec). - Body(trainedModel). - Do(ctx). - Into(result) - return -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *trainedModels) UpdateStatus(ctx context.Context, trainedModel *v1alpha1.TrainedModel, opts v1.UpdateOptions) (result *v1alpha1.TrainedModel, err error) { - result = &v1alpha1.TrainedModel{} - err = c.client.Put(). - Namespace(c.ns). - Resource("trainedmodels"). - Name(trainedModel.Name). - SubResource("status"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(trainedModel). - Do(ctx). - Into(result) - return -} - -// Delete takes name of the trainedModel and deletes it. Returns an error if one occurs. -func (c *trainedModels) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("trainedmodels"). - Name(name). - Body(&opts). - Do(ctx). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *trainedModels) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - var timeout time.Duration - if listOpts.TimeoutSeconds != nil { - timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second - } - return c.client.Delete(). - Namespace(c.ns). - Resource("trainedmodels"). - VersionedParams(&listOpts, scheme.ParameterCodec). - Timeout(timeout). - Body(&opts). - Do(ctx). - Error() -} - -// Patch applies the patch and returns the patched trainedModel. -func (c *trainedModels) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.TrainedModel, err error) { - result = &v1alpha1.TrainedModel{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("trainedmodels"). - Name(name). - SubResource(subresources...). - VersionedParams(&opts, scheme.ParameterCodec). - Body(data). - Do(ctx). - Into(result) - return -} diff --git a/pkg/client/clientset/versioned/typed/serving/v1beta1/fake/fake_inferenceservice.go b/pkg/client/clientset/versioned/typed/serving/v1beta1/fake/fake_inferenceservice.go index 3b3afa06954..26c6a0429f2 100644 --- a/pkg/client/clientset/versioned/typed/serving/v1beta1/fake/fake_inferenceservice.go +++ b/pkg/client/clientset/versioned/typed/serving/v1beta1/fake/fake_inferenceservice.go @@ -19,123 +19,34 @@ limitations under the License. package fake import ( - "context" - v1beta1 "github.com/kserve/kserve/pkg/apis/serving/v1beta1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" + servingv1beta1 "github.com/kserve/kserve/pkg/client/clientset/versioned/typed/serving/v1beta1" + gentype "k8s.io/client-go/gentype" ) -// FakeInferenceServices implements InferenceServiceInterface -type FakeInferenceServices struct { +// fakeInferenceServices implements InferenceServiceInterface +type fakeInferenceServices struct { + *gentype.FakeClientWithList[*v1beta1.InferenceService, *v1beta1.InferenceServiceList] Fake *FakeServingV1beta1 - ns string -} - -var inferenceservicesResource = v1beta1.SchemeGroupVersion.WithResource("inferenceservices") - -var inferenceservicesKind = v1beta1.SchemeGroupVersion.WithKind("InferenceService") - -// Get takes name of the inferenceService, and returns the corresponding inferenceService object, and an error if there is any. -func (c *FakeInferenceServices) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta1.InferenceService, err error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(inferenceservicesResource, c.ns, name), &v1beta1.InferenceService{}) - - if obj == nil { - return nil, err - } - return obj.(*v1beta1.InferenceService), err -} - -// List takes label and field selectors, and returns the list of InferenceServices that match those selectors. -func (c *FakeInferenceServices) List(ctx context.Context, opts v1.ListOptions) (result *v1beta1.InferenceServiceList, err error) { - obj, err := c.Fake. - Invokes(testing.NewListAction(inferenceservicesResource, inferenceservicesKind, c.ns, opts), &v1beta1.InferenceServiceList{}) - - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &v1beta1.InferenceServiceList{ListMeta: obj.(*v1beta1.InferenceServiceList).ListMeta} - for _, item := range obj.(*v1beta1.InferenceServiceList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested inferenceServices. -func (c *FakeInferenceServices) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchAction(inferenceservicesResource, c.ns, opts)) - -} - -// Create takes the representation of a inferenceService and creates it. Returns the server's representation of the inferenceService, and an error, if there is any. -func (c *FakeInferenceServices) Create(ctx context.Context, inferenceService *v1beta1.InferenceService, opts v1.CreateOptions) (result *v1beta1.InferenceService, err error) { - obj, err := c.Fake. - Invokes(testing.NewCreateAction(inferenceservicesResource, c.ns, inferenceService), &v1beta1.InferenceService{}) - - if obj == nil { - return nil, err - } - return obj.(*v1beta1.InferenceService), err -} - -// Update takes the representation of a inferenceService and updates it. Returns the server's representation of the inferenceService, and an error, if there is any. -func (c *FakeInferenceServices) Update(ctx context.Context, inferenceService *v1beta1.InferenceService, opts v1.UpdateOptions) (result *v1beta1.InferenceService, err error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateAction(inferenceservicesResource, c.ns, inferenceService), &v1beta1.InferenceService{}) - - if obj == nil { - return nil, err - } - return obj.(*v1beta1.InferenceService), err -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *FakeInferenceServices) UpdateStatus(ctx context.Context, inferenceService *v1beta1.InferenceService, opts v1.UpdateOptions) (*v1beta1.InferenceService, error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateSubresourceAction(inferenceservicesResource, "status", c.ns, inferenceService), &v1beta1.InferenceService{}) - - if obj == nil { - return nil, err - } - return obj.(*v1beta1.InferenceService), err -} - -// Delete takes name of the inferenceService and deletes it. Returns an error if one occurs. -func (c *FakeInferenceServices) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteActionWithOptions(inferenceservicesResource, c.ns, name, opts), &v1beta1.InferenceService{}) - - return err } -// DeleteCollection deletes a collection of objects. -func (c *FakeInferenceServices) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - action := testing.NewDeleteCollectionAction(inferenceservicesResource, c.ns, listOpts) - - _, err := c.Fake.Invokes(action, &v1beta1.InferenceServiceList{}) - return err -} - -// Patch applies the patch and returns the patched inferenceService. -func (c *FakeInferenceServices) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta1.InferenceService, err error) { - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(inferenceservicesResource, c.ns, name, pt, data, subresources...), &v1beta1.InferenceService{}) - - if obj == nil { - return nil, err +func newFakeInferenceServices(fake *FakeServingV1beta1, namespace string) servingv1beta1.InferenceServiceInterface { + return &fakeInferenceServices{ + gentype.NewFakeClientWithList[*v1beta1.InferenceService, *v1beta1.InferenceServiceList]( + fake.Fake, + namespace, + v1beta1.SchemeGroupVersion.WithResource("inferenceservices"), + v1beta1.SchemeGroupVersion.WithKind("InferenceService"), + func() *v1beta1.InferenceService { return &v1beta1.InferenceService{} }, + func() *v1beta1.InferenceServiceList { return &v1beta1.InferenceServiceList{} }, + func(dst, src *v1beta1.InferenceServiceList) { dst.ListMeta = src.ListMeta }, + func(list *v1beta1.InferenceServiceList) []*v1beta1.InferenceService { + return gentype.ToPointerSlice(list.Items) + }, + func(list *v1beta1.InferenceServiceList, items []*v1beta1.InferenceService) { + list.Items = gentype.FromPointerSlice(items) + }, + ), + fake, } - return obj.(*v1beta1.InferenceService), err } diff --git a/pkg/client/clientset/versioned/typed/serving/v1beta1/fake/fake_serving_client.go b/pkg/client/clientset/versioned/typed/serving/v1beta1/fake/fake_serving_client.go index b6bf7aac80d..44cac790ebc 100644 --- a/pkg/client/clientset/versioned/typed/serving/v1beta1/fake/fake_serving_client.go +++ b/pkg/client/clientset/versioned/typed/serving/v1beta1/fake/fake_serving_client.go @@ -29,7 +29,7 @@ type FakeServingV1beta1 struct { } func (c *FakeServingV1beta1) InferenceServices(namespace string) v1beta1.InferenceServiceInterface { - return &FakeInferenceServices{c, namespace} + return newFakeInferenceServices(c, namespace) } // RESTClient returns a RESTClient that is used to communicate diff --git a/pkg/client/clientset/versioned/typed/serving/v1beta1/inferenceservice.go b/pkg/client/clientset/versioned/typed/serving/v1beta1/inferenceservice.go index 53e1118e530..64457ee0974 100644 --- a/pkg/client/clientset/versioned/typed/serving/v1beta1/inferenceservice.go +++ b/pkg/client/clientset/versioned/typed/serving/v1beta1/inferenceservice.go @@ -19,15 +19,14 @@ limitations under the License. package v1beta1 import ( - "context" - "time" + context "context" - v1beta1 "github.com/kserve/kserve/pkg/apis/serving/v1beta1" + servingv1beta1 "github.com/kserve/kserve/pkg/apis/serving/v1beta1" scheme "github.com/kserve/kserve/pkg/client/clientset/versioned/scheme" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" types "k8s.io/apimachinery/pkg/types" watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" + gentype "k8s.io/client-go/gentype" ) // InferenceServicesGetter has a method to return a InferenceServiceInterface. @@ -38,158 +37,34 @@ type InferenceServicesGetter interface { // InferenceServiceInterface has methods to work with InferenceService resources. type InferenceServiceInterface interface { - Create(ctx context.Context, inferenceService *v1beta1.InferenceService, opts v1.CreateOptions) (*v1beta1.InferenceService, error) - Update(ctx context.Context, inferenceService *v1beta1.InferenceService, opts v1.UpdateOptions) (*v1beta1.InferenceService, error) - UpdateStatus(ctx context.Context, inferenceService *v1beta1.InferenceService, opts v1.UpdateOptions) (*v1beta1.InferenceService, error) + Create(ctx context.Context, inferenceService *servingv1beta1.InferenceService, opts v1.CreateOptions) (*servingv1beta1.InferenceService, error) + Update(ctx context.Context, inferenceService *servingv1beta1.InferenceService, opts v1.UpdateOptions) (*servingv1beta1.InferenceService, error) + // Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). + UpdateStatus(ctx context.Context, inferenceService *servingv1beta1.InferenceService, opts v1.UpdateOptions) (*servingv1beta1.InferenceService, error) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error - Get(ctx context.Context, name string, opts v1.GetOptions) (*v1beta1.InferenceService, error) - List(ctx context.Context, opts v1.ListOptions) (*v1beta1.InferenceServiceList, error) + Get(ctx context.Context, name string, opts v1.GetOptions) (*servingv1beta1.InferenceService, error) + List(ctx context.Context, opts v1.ListOptions) (*servingv1beta1.InferenceServiceList, error) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) - Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta1.InferenceService, err error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *servingv1beta1.InferenceService, err error) InferenceServiceExpansion } // inferenceServices implements InferenceServiceInterface type inferenceServices struct { - client rest.Interface - ns string + *gentype.ClientWithList[*servingv1beta1.InferenceService, *servingv1beta1.InferenceServiceList] } // newInferenceServices returns a InferenceServices func newInferenceServices(c *ServingV1beta1Client, namespace string) *inferenceServices { return &inferenceServices{ - client: c.RESTClient(), - ns: namespace, + gentype.NewClientWithList[*servingv1beta1.InferenceService, *servingv1beta1.InferenceServiceList]( + "inferenceservices", + c.RESTClient(), + scheme.ParameterCodec, + namespace, + func() *servingv1beta1.InferenceService { return &servingv1beta1.InferenceService{} }, + func() *servingv1beta1.InferenceServiceList { return &servingv1beta1.InferenceServiceList{} }, + ), } } - -// Get takes name of the inferenceService, and returns the corresponding inferenceService object, and an error if there is any. -func (c *inferenceServices) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta1.InferenceService, err error) { - result = &v1beta1.InferenceService{} - err = c.client.Get(). - Namespace(c.ns). - Resource("inferenceservices"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(ctx). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of InferenceServices that match those selectors. -func (c *inferenceServices) List(ctx context.Context, opts v1.ListOptions) (result *v1beta1.InferenceServiceList, err error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - result = &v1beta1.InferenceServiceList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("inferenceservices"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Do(ctx). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested inferenceServices. -func (c *inferenceServices) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("inferenceservices"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Watch(ctx) -} - -// Create takes the representation of a inferenceService and creates it. Returns the server's representation of the inferenceService, and an error, if there is any. -func (c *inferenceServices) Create(ctx context.Context, inferenceService *v1beta1.InferenceService, opts v1.CreateOptions) (result *v1beta1.InferenceService, err error) { - result = &v1beta1.InferenceService{} - err = c.client.Post(). - Namespace(c.ns). - Resource("inferenceservices"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(inferenceService). - Do(ctx). - Into(result) - return -} - -// Update takes the representation of a inferenceService and updates it. Returns the server's representation of the inferenceService, and an error, if there is any. -func (c *inferenceServices) Update(ctx context.Context, inferenceService *v1beta1.InferenceService, opts v1.UpdateOptions) (result *v1beta1.InferenceService, err error) { - result = &v1beta1.InferenceService{} - err = c.client.Put(). - Namespace(c.ns). - Resource("inferenceservices"). - Name(inferenceService.Name). - VersionedParams(&opts, scheme.ParameterCodec). - Body(inferenceService). - Do(ctx). - Into(result) - return -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *inferenceServices) UpdateStatus(ctx context.Context, inferenceService *v1beta1.InferenceService, opts v1.UpdateOptions) (result *v1beta1.InferenceService, err error) { - result = &v1beta1.InferenceService{} - err = c.client.Put(). - Namespace(c.ns). - Resource("inferenceservices"). - Name(inferenceService.Name). - SubResource("status"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(inferenceService). - Do(ctx). - Into(result) - return -} - -// Delete takes name of the inferenceService and deletes it. Returns an error if one occurs. -func (c *inferenceServices) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("inferenceservices"). - Name(name). - Body(&opts). - Do(ctx). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *inferenceServices) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - var timeout time.Duration - if listOpts.TimeoutSeconds != nil { - timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second - } - return c.client.Delete(). - Namespace(c.ns). - Resource("inferenceservices"). - VersionedParams(&listOpts, scheme.ParameterCodec). - Timeout(timeout). - Body(&opts). - Do(ctx). - Error() -} - -// Patch applies the patch and returns the patched inferenceService. -func (c *inferenceServices) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta1.InferenceService, err error) { - result = &v1beta1.InferenceService{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("inferenceservices"). - Name(name). - SubResource(subresources...). - VersionedParams(&opts, scheme.ParameterCodec). - Body(data). - Do(ctx). - Into(result) - return -} diff --git a/pkg/client/clientset/versioned/typed/serving/v1beta1/serving_client.go b/pkg/client/clientset/versioned/typed/serving/v1beta1/serving_client.go index cb5e6d8172e..7cdc75758d3 100644 --- a/pkg/client/clientset/versioned/typed/serving/v1beta1/serving_client.go +++ b/pkg/client/clientset/versioned/typed/serving/v1beta1/serving_client.go @@ -19,10 +19,10 @@ limitations under the License. package v1beta1 import ( - "net/http" + http "net/http" - v1beta1 "github.com/kserve/kserve/pkg/apis/serving/v1beta1" - "github.com/kserve/kserve/pkg/client/clientset/versioned/scheme" + servingv1beta1 "github.com/kserve/kserve/pkg/apis/serving/v1beta1" + scheme "github.com/kserve/kserve/pkg/client/clientset/versioned/scheme" rest "k8s.io/client-go/rest" ) @@ -85,10 +85,10 @@ func New(c rest.Interface) *ServingV1beta1Client { } func setConfigDefaults(config *rest.Config) error { - gv := v1beta1.SchemeGroupVersion + gv := servingv1beta1.SchemeGroupVersion config.GroupVersion = &gv config.APIPath = "/apis" - config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() + config.NegotiatedSerializer = rest.CodecFactoryForGeneratedClient(scheme.Scheme, scheme.Codecs).WithoutConversion() if config.UserAgent == "" { config.UserAgent = rest.DefaultKubernetesUserAgent() diff --git a/pkg/client/informers/externalversions/factory.go b/pkg/client/informers/externalversions/factory.go index 428515b4626..6f5e0a25d47 100644 --- a/pkg/client/informers/externalversions/factory.go +++ b/pkg/client/informers/externalversions/factory.go @@ -228,6 +228,7 @@ type SharedInformerFactory interface { // Start initializes all requested informers. They are handled in goroutines // which run until the stop channel gets closed. + // Warning: Start does not block. When run in a go-routine, it will race with a later WaitForCacheSync. Start(stopCh <-chan struct{}) // Shutdown marks a factory as shutting down. At that point no new diff --git a/pkg/client/informers/externalversions/generic.go b/pkg/client/informers/externalversions/generic.go index 5152a88ead9..997540ba9da 100644 --- a/pkg/client/informers/externalversions/generic.go +++ b/pkg/client/informers/externalversions/generic.go @@ -19,7 +19,7 @@ limitations under the License. package externalversions import ( - "fmt" + fmt "fmt" v1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" v1beta1 "github.com/kserve/kserve/pkg/apis/serving/v1beta1" diff --git a/pkg/client/informers/externalversions/serving/v1alpha1/clusterservingruntime.go b/pkg/client/informers/externalversions/serving/v1alpha1/clusterservingruntime.go index dd61f1ee753..6627e519a7c 100644 --- a/pkg/client/informers/externalversions/serving/v1alpha1/clusterservingruntime.go +++ b/pkg/client/informers/externalversions/serving/v1alpha1/clusterservingruntime.go @@ -19,13 +19,13 @@ limitations under the License. package v1alpha1 import ( - "context" + context "context" time "time" - servingv1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" + apisservingv1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" versioned "github.com/kserve/kserve/pkg/client/clientset/versioned" internalinterfaces "github.com/kserve/kserve/pkg/client/informers/externalversions/internalinterfaces" - v1alpha1 "github.com/kserve/kserve/pkg/client/listers/serving/v1alpha1" + servingv1alpha1 "github.com/kserve/kserve/pkg/client/listers/serving/v1alpha1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" watch "k8s.io/apimachinery/pkg/watch" @@ -36,7 +36,7 @@ import ( // ClusterServingRuntimes. type ClusterServingRuntimeInformer interface { Informer() cache.SharedIndexInformer - Lister() v1alpha1.ClusterServingRuntimeLister + Lister() servingv1alpha1.ClusterServingRuntimeLister } type clusterServingRuntimeInformer struct { @@ -71,7 +71,7 @@ func NewFilteredClusterServingRuntimeInformer(client versioned.Interface, namesp return client.ServingV1alpha1().ClusterServingRuntimes(namespace).Watch(context.TODO(), options) }, }, - &servingv1alpha1.ClusterServingRuntime{}, + &apisservingv1alpha1.ClusterServingRuntime{}, resyncPeriod, indexers, ) @@ -82,9 +82,9 @@ func (f *clusterServingRuntimeInformer) defaultInformer(client versioned.Interfa } func (f *clusterServingRuntimeInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&servingv1alpha1.ClusterServingRuntime{}, f.defaultInformer) + return f.factory.InformerFor(&apisservingv1alpha1.ClusterServingRuntime{}, f.defaultInformer) } -func (f *clusterServingRuntimeInformer) Lister() v1alpha1.ClusterServingRuntimeLister { - return v1alpha1.NewClusterServingRuntimeLister(f.Informer().GetIndexer()) +func (f *clusterServingRuntimeInformer) Lister() servingv1alpha1.ClusterServingRuntimeLister { + return servingv1alpha1.NewClusterServingRuntimeLister(f.Informer().GetIndexer()) } diff --git a/pkg/client/informers/externalversions/serving/v1alpha1/clusterstoragecontainer.go b/pkg/client/informers/externalversions/serving/v1alpha1/clusterstoragecontainer.go index 48256f237e7..b087ce79efd 100644 --- a/pkg/client/informers/externalversions/serving/v1alpha1/clusterstoragecontainer.go +++ b/pkg/client/informers/externalversions/serving/v1alpha1/clusterstoragecontainer.go @@ -19,13 +19,13 @@ limitations under the License. package v1alpha1 import ( - "context" + context "context" time "time" - servingv1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" + apisservingv1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" versioned "github.com/kserve/kserve/pkg/client/clientset/versioned" internalinterfaces "github.com/kserve/kserve/pkg/client/informers/externalversions/internalinterfaces" - v1alpha1 "github.com/kserve/kserve/pkg/client/listers/serving/v1alpha1" + servingv1alpha1 "github.com/kserve/kserve/pkg/client/listers/serving/v1alpha1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" watch "k8s.io/apimachinery/pkg/watch" @@ -36,7 +36,7 @@ import ( // ClusterStorageContainers. type ClusterStorageContainerInformer interface { Informer() cache.SharedIndexInformer - Lister() v1alpha1.ClusterStorageContainerLister + Lister() servingv1alpha1.ClusterStorageContainerLister } type clusterStorageContainerInformer struct { @@ -71,7 +71,7 @@ func NewFilteredClusterStorageContainerInformer(client versioned.Interface, name return client.ServingV1alpha1().ClusterStorageContainers(namespace).Watch(context.TODO(), options) }, }, - &servingv1alpha1.ClusterStorageContainer{}, + &apisservingv1alpha1.ClusterStorageContainer{}, resyncPeriod, indexers, ) @@ -82,9 +82,9 @@ func (f *clusterStorageContainerInformer) defaultInformer(client versioned.Inter } func (f *clusterStorageContainerInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&servingv1alpha1.ClusterStorageContainer{}, f.defaultInformer) + return f.factory.InformerFor(&apisservingv1alpha1.ClusterStorageContainer{}, f.defaultInformer) } -func (f *clusterStorageContainerInformer) Lister() v1alpha1.ClusterStorageContainerLister { - return v1alpha1.NewClusterStorageContainerLister(f.Informer().GetIndexer()) +func (f *clusterStorageContainerInformer) Lister() servingv1alpha1.ClusterStorageContainerLister { + return servingv1alpha1.NewClusterStorageContainerLister(f.Informer().GetIndexer()) } diff --git a/pkg/client/informers/externalversions/serving/v1alpha1/inferencegraph.go b/pkg/client/informers/externalversions/serving/v1alpha1/inferencegraph.go index 5b1a7785862..00e8feff8bb 100644 --- a/pkg/client/informers/externalversions/serving/v1alpha1/inferencegraph.go +++ b/pkg/client/informers/externalversions/serving/v1alpha1/inferencegraph.go @@ -19,13 +19,13 @@ limitations under the License. package v1alpha1 import ( - "context" + context "context" time "time" - servingv1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" + apisservingv1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" versioned "github.com/kserve/kserve/pkg/client/clientset/versioned" internalinterfaces "github.com/kserve/kserve/pkg/client/informers/externalversions/internalinterfaces" - v1alpha1 "github.com/kserve/kserve/pkg/client/listers/serving/v1alpha1" + servingv1alpha1 "github.com/kserve/kserve/pkg/client/listers/serving/v1alpha1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" watch "k8s.io/apimachinery/pkg/watch" @@ -36,7 +36,7 @@ import ( // InferenceGraphs. type InferenceGraphInformer interface { Informer() cache.SharedIndexInformer - Lister() v1alpha1.InferenceGraphLister + Lister() servingv1alpha1.InferenceGraphLister } type inferenceGraphInformer struct { @@ -71,7 +71,7 @@ func NewFilteredInferenceGraphInformer(client versioned.Interface, namespace str return client.ServingV1alpha1().InferenceGraphs(namespace).Watch(context.TODO(), options) }, }, - &servingv1alpha1.InferenceGraph{}, + &apisservingv1alpha1.InferenceGraph{}, resyncPeriod, indexers, ) @@ -82,9 +82,9 @@ func (f *inferenceGraphInformer) defaultInformer(client versioned.Interface, res } func (f *inferenceGraphInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&servingv1alpha1.InferenceGraph{}, f.defaultInformer) + return f.factory.InformerFor(&apisservingv1alpha1.InferenceGraph{}, f.defaultInformer) } -func (f *inferenceGraphInformer) Lister() v1alpha1.InferenceGraphLister { - return v1alpha1.NewInferenceGraphLister(f.Informer().GetIndexer()) +func (f *inferenceGraphInformer) Lister() servingv1alpha1.InferenceGraphLister { + return servingv1alpha1.NewInferenceGraphLister(f.Informer().GetIndexer()) } diff --git a/pkg/client/informers/externalversions/serving/v1alpha1/localmodelcache.go b/pkg/client/informers/externalversions/serving/v1alpha1/localmodelcache.go index 7de7a39a99c..2f9caae6df0 100644 --- a/pkg/client/informers/externalversions/serving/v1alpha1/localmodelcache.go +++ b/pkg/client/informers/externalversions/serving/v1alpha1/localmodelcache.go @@ -19,13 +19,13 @@ limitations under the License. package v1alpha1 import ( - "context" + context "context" time "time" - servingv1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" + apisservingv1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" versioned "github.com/kserve/kserve/pkg/client/clientset/versioned" internalinterfaces "github.com/kserve/kserve/pkg/client/informers/externalversions/internalinterfaces" - v1alpha1 "github.com/kserve/kserve/pkg/client/listers/serving/v1alpha1" + servingv1alpha1 "github.com/kserve/kserve/pkg/client/listers/serving/v1alpha1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" watch "k8s.io/apimachinery/pkg/watch" @@ -36,7 +36,7 @@ import ( // LocalModelCaches. type LocalModelCacheInformer interface { Informer() cache.SharedIndexInformer - Lister() v1alpha1.LocalModelCacheLister + Lister() servingv1alpha1.LocalModelCacheLister } type localModelCacheInformer struct { @@ -71,7 +71,7 @@ func NewFilteredLocalModelCacheInformer(client versioned.Interface, namespace st return client.ServingV1alpha1().LocalModelCaches(namespace).Watch(context.TODO(), options) }, }, - &servingv1alpha1.LocalModelCache{}, + &apisservingv1alpha1.LocalModelCache{}, resyncPeriod, indexers, ) @@ -82,9 +82,9 @@ func (f *localModelCacheInformer) defaultInformer(client versioned.Interface, re } func (f *localModelCacheInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&servingv1alpha1.LocalModelCache{}, f.defaultInformer) + return f.factory.InformerFor(&apisservingv1alpha1.LocalModelCache{}, f.defaultInformer) } -func (f *localModelCacheInformer) Lister() v1alpha1.LocalModelCacheLister { - return v1alpha1.NewLocalModelCacheLister(f.Informer().GetIndexer()) +func (f *localModelCacheInformer) Lister() servingv1alpha1.LocalModelCacheLister { + return servingv1alpha1.NewLocalModelCacheLister(f.Informer().GetIndexer()) } diff --git a/pkg/client/informers/externalversions/serving/v1alpha1/localmodelnode.go b/pkg/client/informers/externalversions/serving/v1alpha1/localmodelnode.go index 11b347f7c5f..254191f7627 100644 --- a/pkg/client/informers/externalversions/serving/v1alpha1/localmodelnode.go +++ b/pkg/client/informers/externalversions/serving/v1alpha1/localmodelnode.go @@ -19,13 +19,13 @@ limitations under the License. package v1alpha1 import ( - "context" + context "context" time "time" - servingv1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" + apisservingv1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" versioned "github.com/kserve/kserve/pkg/client/clientset/versioned" internalinterfaces "github.com/kserve/kserve/pkg/client/informers/externalversions/internalinterfaces" - v1alpha1 "github.com/kserve/kserve/pkg/client/listers/serving/v1alpha1" + servingv1alpha1 "github.com/kserve/kserve/pkg/client/listers/serving/v1alpha1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" watch "k8s.io/apimachinery/pkg/watch" @@ -36,7 +36,7 @@ import ( // LocalModelNodes. type LocalModelNodeInformer interface { Informer() cache.SharedIndexInformer - Lister() v1alpha1.LocalModelNodeLister + Lister() servingv1alpha1.LocalModelNodeLister } type localModelNodeInformer struct { @@ -71,7 +71,7 @@ func NewFilteredLocalModelNodeInformer(client versioned.Interface, namespace str return client.ServingV1alpha1().LocalModelNodes(namespace).Watch(context.TODO(), options) }, }, - &servingv1alpha1.LocalModelNode{}, + &apisservingv1alpha1.LocalModelNode{}, resyncPeriod, indexers, ) @@ -82,9 +82,9 @@ func (f *localModelNodeInformer) defaultInformer(client versioned.Interface, res } func (f *localModelNodeInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&servingv1alpha1.LocalModelNode{}, f.defaultInformer) + return f.factory.InformerFor(&apisservingv1alpha1.LocalModelNode{}, f.defaultInformer) } -func (f *localModelNodeInformer) Lister() v1alpha1.LocalModelNodeLister { - return v1alpha1.NewLocalModelNodeLister(f.Informer().GetIndexer()) +func (f *localModelNodeInformer) Lister() servingv1alpha1.LocalModelNodeLister { + return servingv1alpha1.NewLocalModelNodeLister(f.Informer().GetIndexer()) } diff --git a/pkg/client/informers/externalversions/serving/v1alpha1/localmodelnodegroup.go b/pkg/client/informers/externalversions/serving/v1alpha1/localmodelnodegroup.go index 9d13f6c88b1..6cca637dc07 100644 --- a/pkg/client/informers/externalversions/serving/v1alpha1/localmodelnodegroup.go +++ b/pkg/client/informers/externalversions/serving/v1alpha1/localmodelnodegroup.go @@ -19,13 +19,13 @@ limitations under the License. package v1alpha1 import ( - "context" + context "context" time "time" - servingv1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" + apisservingv1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" versioned "github.com/kserve/kserve/pkg/client/clientset/versioned" internalinterfaces "github.com/kserve/kserve/pkg/client/informers/externalversions/internalinterfaces" - v1alpha1 "github.com/kserve/kserve/pkg/client/listers/serving/v1alpha1" + servingv1alpha1 "github.com/kserve/kserve/pkg/client/listers/serving/v1alpha1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" watch "k8s.io/apimachinery/pkg/watch" @@ -36,7 +36,7 @@ import ( // LocalModelNodeGroups. type LocalModelNodeGroupInformer interface { Informer() cache.SharedIndexInformer - Lister() v1alpha1.LocalModelNodeGroupLister + Lister() servingv1alpha1.LocalModelNodeGroupLister } type localModelNodeGroupInformer struct { @@ -71,7 +71,7 @@ func NewFilteredLocalModelNodeGroupInformer(client versioned.Interface, namespac return client.ServingV1alpha1().LocalModelNodeGroups(namespace).Watch(context.TODO(), options) }, }, - &servingv1alpha1.LocalModelNodeGroup{}, + &apisservingv1alpha1.LocalModelNodeGroup{}, resyncPeriod, indexers, ) @@ -82,9 +82,9 @@ func (f *localModelNodeGroupInformer) defaultInformer(client versioned.Interface } func (f *localModelNodeGroupInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&servingv1alpha1.LocalModelNodeGroup{}, f.defaultInformer) + return f.factory.InformerFor(&apisservingv1alpha1.LocalModelNodeGroup{}, f.defaultInformer) } -func (f *localModelNodeGroupInformer) Lister() v1alpha1.LocalModelNodeGroupLister { - return v1alpha1.NewLocalModelNodeGroupLister(f.Informer().GetIndexer()) +func (f *localModelNodeGroupInformer) Lister() servingv1alpha1.LocalModelNodeGroupLister { + return servingv1alpha1.NewLocalModelNodeGroupLister(f.Informer().GetIndexer()) } diff --git a/pkg/client/informers/externalversions/serving/v1alpha1/servingruntime.go b/pkg/client/informers/externalversions/serving/v1alpha1/servingruntime.go index e86d23d258f..b55160d9dd6 100644 --- a/pkg/client/informers/externalversions/serving/v1alpha1/servingruntime.go +++ b/pkg/client/informers/externalversions/serving/v1alpha1/servingruntime.go @@ -19,13 +19,13 @@ limitations under the License. package v1alpha1 import ( - "context" + context "context" time "time" - servingv1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" + apisservingv1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" versioned "github.com/kserve/kserve/pkg/client/clientset/versioned" internalinterfaces "github.com/kserve/kserve/pkg/client/informers/externalversions/internalinterfaces" - v1alpha1 "github.com/kserve/kserve/pkg/client/listers/serving/v1alpha1" + servingv1alpha1 "github.com/kserve/kserve/pkg/client/listers/serving/v1alpha1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" watch "k8s.io/apimachinery/pkg/watch" @@ -36,7 +36,7 @@ import ( // ServingRuntimes. type ServingRuntimeInformer interface { Informer() cache.SharedIndexInformer - Lister() v1alpha1.ServingRuntimeLister + Lister() servingv1alpha1.ServingRuntimeLister } type servingRuntimeInformer struct { @@ -71,7 +71,7 @@ func NewFilteredServingRuntimeInformer(client versioned.Interface, namespace str return client.ServingV1alpha1().ServingRuntimes(namespace).Watch(context.TODO(), options) }, }, - &servingv1alpha1.ServingRuntime{}, + &apisservingv1alpha1.ServingRuntime{}, resyncPeriod, indexers, ) @@ -82,9 +82,9 @@ func (f *servingRuntimeInformer) defaultInformer(client versioned.Interface, res } func (f *servingRuntimeInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&servingv1alpha1.ServingRuntime{}, f.defaultInformer) + return f.factory.InformerFor(&apisservingv1alpha1.ServingRuntime{}, f.defaultInformer) } -func (f *servingRuntimeInformer) Lister() v1alpha1.ServingRuntimeLister { - return v1alpha1.NewServingRuntimeLister(f.Informer().GetIndexer()) +func (f *servingRuntimeInformer) Lister() servingv1alpha1.ServingRuntimeLister { + return servingv1alpha1.NewServingRuntimeLister(f.Informer().GetIndexer()) } diff --git a/pkg/client/informers/externalversions/serving/v1alpha1/trainedmodel.go b/pkg/client/informers/externalversions/serving/v1alpha1/trainedmodel.go index de220ba24df..0dd07ca616e 100644 --- a/pkg/client/informers/externalversions/serving/v1alpha1/trainedmodel.go +++ b/pkg/client/informers/externalversions/serving/v1alpha1/trainedmodel.go @@ -19,13 +19,13 @@ limitations under the License. package v1alpha1 import ( - "context" + context "context" time "time" - servingv1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" + apisservingv1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" versioned "github.com/kserve/kserve/pkg/client/clientset/versioned" internalinterfaces "github.com/kserve/kserve/pkg/client/informers/externalversions/internalinterfaces" - v1alpha1 "github.com/kserve/kserve/pkg/client/listers/serving/v1alpha1" + servingv1alpha1 "github.com/kserve/kserve/pkg/client/listers/serving/v1alpha1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" watch "k8s.io/apimachinery/pkg/watch" @@ -36,7 +36,7 @@ import ( // TrainedModels. type TrainedModelInformer interface { Informer() cache.SharedIndexInformer - Lister() v1alpha1.TrainedModelLister + Lister() servingv1alpha1.TrainedModelLister } type trainedModelInformer struct { @@ -71,7 +71,7 @@ func NewFilteredTrainedModelInformer(client versioned.Interface, namespace strin return client.ServingV1alpha1().TrainedModels(namespace).Watch(context.TODO(), options) }, }, - &servingv1alpha1.TrainedModel{}, + &apisservingv1alpha1.TrainedModel{}, resyncPeriod, indexers, ) @@ -82,9 +82,9 @@ func (f *trainedModelInformer) defaultInformer(client versioned.Interface, resyn } func (f *trainedModelInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&servingv1alpha1.TrainedModel{}, f.defaultInformer) + return f.factory.InformerFor(&apisservingv1alpha1.TrainedModel{}, f.defaultInformer) } -func (f *trainedModelInformer) Lister() v1alpha1.TrainedModelLister { - return v1alpha1.NewTrainedModelLister(f.Informer().GetIndexer()) +func (f *trainedModelInformer) Lister() servingv1alpha1.TrainedModelLister { + return servingv1alpha1.NewTrainedModelLister(f.Informer().GetIndexer()) } diff --git a/pkg/client/informers/externalversions/serving/v1beta1/inferenceservice.go b/pkg/client/informers/externalversions/serving/v1beta1/inferenceservice.go index add99783c7b..b7dfad31cb6 100644 --- a/pkg/client/informers/externalversions/serving/v1beta1/inferenceservice.go +++ b/pkg/client/informers/externalversions/serving/v1beta1/inferenceservice.go @@ -19,13 +19,13 @@ limitations under the License. package v1beta1 import ( - "context" + context "context" time "time" - servingv1beta1 "github.com/kserve/kserve/pkg/apis/serving/v1beta1" + apisservingv1beta1 "github.com/kserve/kserve/pkg/apis/serving/v1beta1" versioned "github.com/kserve/kserve/pkg/client/clientset/versioned" internalinterfaces "github.com/kserve/kserve/pkg/client/informers/externalversions/internalinterfaces" - v1beta1 "github.com/kserve/kserve/pkg/client/listers/serving/v1beta1" + servingv1beta1 "github.com/kserve/kserve/pkg/client/listers/serving/v1beta1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" watch "k8s.io/apimachinery/pkg/watch" @@ -36,7 +36,7 @@ import ( // InferenceServices. type InferenceServiceInformer interface { Informer() cache.SharedIndexInformer - Lister() v1beta1.InferenceServiceLister + Lister() servingv1beta1.InferenceServiceLister } type inferenceServiceInformer struct { @@ -71,7 +71,7 @@ func NewFilteredInferenceServiceInformer(client versioned.Interface, namespace s return client.ServingV1beta1().InferenceServices(namespace).Watch(context.TODO(), options) }, }, - &servingv1beta1.InferenceService{}, + &apisservingv1beta1.InferenceService{}, resyncPeriod, indexers, ) @@ -82,9 +82,9 @@ func (f *inferenceServiceInformer) defaultInformer(client versioned.Interface, r } func (f *inferenceServiceInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&servingv1beta1.InferenceService{}, f.defaultInformer) + return f.factory.InformerFor(&apisservingv1beta1.InferenceService{}, f.defaultInformer) } -func (f *inferenceServiceInformer) Lister() v1beta1.InferenceServiceLister { - return v1beta1.NewInferenceServiceLister(f.Informer().GetIndexer()) +func (f *inferenceServiceInformer) Lister() servingv1beta1.InferenceServiceLister { + return servingv1beta1.NewInferenceServiceLister(f.Informer().GetIndexer()) } diff --git a/pkg/client/listers/serving/v1alpha1/clusterservingruntime.go b/pkg/client/listers/serving/v1alpha1/clusterservingruntime.go index 9f890e099d0..2db3f3951ef 100644 --- a/pkg/client/listers/serving/v1alpha1/clusterservingruntime.go +++ b/pkg/client/listers/serving/v1alpha1/clusterservingruntime.go @@ -19,10 +19,10 @@ limitations under the License. package v1alpha1 import ( - v1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" + servingv1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" + labels "k8s.io/apimachinery/pkg/labels" + listers "k8s.io/client-go/listers" + cache "k8s.io/client-go/tools/cache" ) // ClusterServingRuntimeLister helps list ClusterServingRuntimes. @@ -30,7 +30,7 @@ import ( type ClusterServingRuntimeLister interface { // List lists all ClusterServingRuntimes in the indexer. // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1alpha1.ClusterServingRuntime, err error) + List(selector labels.Selector) (ret []*servingv1alpha1.ClusterServingRuntime, err error) // ClusterServingRuntimes returns an object that can list and get ClusterServingRuntimes. ClusterServingRuntimes(namespace string) ClusterServingRuntimeNamespaceLister ClusterServingRuntimeListerExpansion @@ -38,25 +38,17 @@ type ClusterServingRuntimeLister interface { // clusterServingRuntimeLister implements the ClusterServingRuntimeLister interface. type clusterServingRuntimeLister struct { - indexer cache.Indexer + listers.ResourceIndexer[*servingv1alpha1.ClusterServingRuntime] } // NewClusterServingRuntimeLister returns a new ClusterServingRuntimeLister. func NewClusterServingRuntimeLister(indexer cache.Indexer) ClusterServingRuntimeLister { - return &clusterServingRuntimeLister{indexer: indexer} -} - -// List lists all ClusterServingRuntimes in the indexer. -func (s *clusterServingRuntimeLister) List(selector labels.Selector) (ret []*v1alpha1.ClusterServingRuntime, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.ClusterServingRuntime)) - }) - return ret, err + return &clusterServingRuntimeLister{listers.New[*servingv1alpha1.ClusterServingRuntime](indexer, servingv1alpha1.Resource("clusterservingruntime"))} } // ClusterServingRuntimes returns an object that can list and get ClusterServingRuntimes. func (s *clusterServingRuntimeLister) ClusterServingRuntimes(namespace string) ClusterServingRuntimeNamespaceLister { - return clusterServingRuntimeNamespaceLister{indexer: s.indexer, namespace: namespace} + return clusterServingRuntimeNamespaceLister{listers.NewNamespaced[*servingv1alpha1.ClusterServingRuntime](s.ResourceIndexer, namespace)} } // ClusterServingRuntimeNamespaceLister helps list and get ClusterServingRuntimes. @@ -64,36 +56,15 @@ func (s *clusterServingRuntimeLister) ClusterServingRuntimes(namespace string) C type ClusterServingRuntimeNamespaceLister interface { // List lists all ClusterServingRuntimes in the indexer for a given namespace. // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1alpha1.ClusterServingRuntime, err error) + List(selector labels.Selector) (ret []*servingv1alpha1.ClusterServingRuntime, err error) // Get retrieves the ClusterServingRuntime from the indexer for a given namespace and name. // Objects returned here must be treated as read-only. - Get(name string) (*v1alpha1.ClusterServingRuntime, error) + Get(name string) (*servingv1alpha1.ClusterServingRuntime, error) ClusterServingRuntimeNamespaceListerExpansion } // clusterServingRuntimeNamespaceLister implements the ClusterServingRuntimeNamespaceLister // interface. type clusterServingRuntimeNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all ClusterServingRuntimes in the indexer for a given namespace. -func (s clusterServingRuntimeNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.ClusterServingRuntime, err error) { - err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.ClusterServingRuntime)) - }) - return ret, err -} - -// Get retrieves the ClusterServingRuntime from the indexer for a given namespace and name. -func (s clusterServingRuntimeNamespaceLister) Get(name string) (*v1alpha1.ClusterServingRuntime, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1alpha1.Resource("clusterservingruntime"), name) - } - return obj.(*v1alpha1.ClusterServingRuntime), nil + listers.ResourceIndexer[*servingv1alpha1.ClusterServingRuntime] } diff --git a/pkg/client/listers/serving/v1alpha1/clusterstoragecontainer.go b/pkg/client/listers/serving/v1alpha1/clusterstoragecontainer.go index eec27b1bd2c..f239b003d07 100644 --- a/pkg/client/listers/serving/v1alpha1/clusterstoragecontainer.go +++ b/pkg/client/listers/serving/v1alpha1/clusterstoragecontainer.go @@ -19,10 +19,10 @@ limitations under the License. package v1alpha1 import ( - v1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" + servingv1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" + labels "k8s.io/apimachinery/pkg/labels" + listers "k8s.io/client-go/listers" + cache "k8s.io/client-go/tools/cache" ) // ClusterStorageContainerLister helps list ClusterStorageContainers. @@ -30,7 +30,7 @@ import ( type ClusterStorageContainerLister interface { // List lists all ClusterStorageContainers in the indexer. // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1alpha1.ClusterStorageContainer, err error) + List(selector labels.Selector) (ret []*servingv1alpha1.ClusterStorageContainer, err error) // ClusterStorageContainers returns an object that can list and get ClusterStorageContainers. ClusterStorageContainers(namespace string) ClusterStorageContainerNamespaceLister ClusterStorageContainerListerExpansion @@ -38,25 +38,17 @@ type ClusterStorageContainerLister interface { // clusterStorageContainerLister implements the ClusterStorageContainerLister interface. type clusterStorageContainerLister struct { - indexer cache.Indexer + listers.ResourceIndexer[*servingv1alpha1.ClusterStorageContainer] } // NewClusterStorageContainerLister returns a new ClusterStorageContainerLister. func NewClusterStorageContainerLister(indexer cache.Indexer) ClusterStorageContainerLister { - return &clusterStorageContainerLister{indexer: indexer} -} - -// List lists all ClusterStorageContainers in the indexer. -func (s *clusterStorageContainerLister) List(selector labels.Selector) (ret []*v1alpha1.ClusterStorageContainer, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.ClusterStorageContainer)) - }) - return ret, err + return &clusterStorageContainerLister{listers.New[*servingv1alpha1.ClusterStorageContainer](indexer, servingv1alpha1.Resource("clusterstoragecontainer"))} } // ClusterStorageContainers returns an object that can list and get ClusterStorageContainers. func (s *clusterStorageContainerLister) ClusterStorageContainers(namespace string) ClusterStorageContainerNamespaceLister { - return clusterStorageContainerNamespaceLister{indexer: s.indexer, namespace: namespace} + return clusterStorageContainerNamespaceLister{listers.NewNamespaced[*servingv1alpha1.ClusterStorageContainer](s.ResourceIndexer, namespace)} } // ClusterStorageContainerNamespaceLister helps list and get ClusterStorageContainers. @@ -64,36 +56,15 @@ func (s *clusterStorageContainerLister) ClusterStorageContainers(namespace strin type ClusterStorageContainerNamespaceLister interface { // List lists all ClusterStorageContainers in the indexer for a given namespace. // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1alpha1.ClusterStorageContainer, err error) + List(selector labels.Selector) (ret []*servingv1alpha1.ClusterStorageContainer, err error) // Get retrieves the ClusterStorageContainer from the indexer for a given namespace and name. // Objects returned here must be treated as read-only. - Get(name string) (*v1alpha1.ClusterStorageContainer, error) + Get(name string) (*servingv1alpha1.ClusterStorageContainer, error) ClusterStorageContainerNamespaceListerExpansion } // clusterStorageContainerNamespaceLister implements the ClusterStorageContainerNamespaceLister // interface. type clusterStorageContainerNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all ClusterStorageContainers in the indexer for a given namespace. -func (s clusterStorageContainerNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.ClusterStorageContainer, err error) { - err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.ClusterStorageContainer)) - }) - return ret, err -} - -// Get retrieves the ClusterStorageContainer from the indexer for a given namespace and name. -func (s clusterStorageContainerNamespaceLister) Get(name string) (*v1alpha1.ClusterStorageContainer, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1alpha1.Resource("clusterstoragecontainer"), name) - } - return obj.(*v1alpha1.ClusterStorageContainer), nil + listers.ResourceIndexer[*servingv1alpha1.ClusterStorageContainer] } diff --git a/pkg/client/listers/serving/v1alpha1/inferencegraph.go b/pkg/client/listers/serving/v1alpha1/inferencegraph.go index 1f0e5cac814..59221481b0a 100644 --- a/pkg/client/listers/serving/v1alpha1/inferencegraph.go +++ b/pkg/client/listers/serving/v1alpha1/inferencegraph.go @@ -19,10 +19,10 @@ limitations under the License. package v1alpha1 import ( - v1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" + servingv1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" + labels "k8s.io/apimachinery/pkg/labels" + listers "k8s.io/client-go/listers" + cache "k8s.io/client-go/tools/cache" ) // InferenceGraphLister helps list InferenceGraphs. @@ -30,7 +30,7 @@ import ( type InferenceGraphLister interface { // List lists all InferenceGraphs in the indexer. // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1alpha1.InferenceGraph, err error) + List(selector labels.Selector) (ret []*servingv1alpha1.InferenceGraph, err error) // InferenceGraphs returns an object that can list and get InferenceGraphs. InferenceGraphs(namespace string) InferenceGraphNamespaceLister InferenceGraphListerExpansion @@ -38,25 +38,17 @@ type InferenceGraphLister interface { // inferenceGraphLister implements the InferenceGraphLister interface. type inferenceGraphLister struct { - indexer cache.Indexer + listers.ResourceIndexer[*servingv1alpha1.InferenceGraph] } // NewInferenceGraphLister returns a new InferenceGraphLister. func NewInferenceGraphLister(indexer cache.Indexer) InferenceGraphLister { - return &inferenceGraphLister{indexer: indexer} -} - -// List lists all InferenceGraphs in the indexer. -func (s *inferenceGraphLister) List(selector labels.Selector) (ret []*v1alpha1.InferenceGraph, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.InferenceGraph)) - }) - return ret, err + return &inferenceGraphLister{listers.New[*servingv1alpha1.InferenceGraph](indexer, servingv1alpha1.Resource("inferencegraph"))} } // InferenceGraphs returns an object that can list and get InferenceGraphs. func (s *inferenceGraphLister) InferenceGraphs(namespace string) InferenceGraphNamespaceLister { - return inferenceGraphNamespaceLister{indexer: s.indexer, namespace: namespace} + return inferenceGraphNamespaceLister{listers.NewNamespaced[*servingv1alpha1.InferenceGraph](s.ResourceIndexer, namespace)} } // InferenceGraphNamespaceLister helps list and get InferenceGraphs. @@ -64,36 +56,15 @@ func (s *inferenceGraphLister) InferenceGraphs(namespace string) InferenceGraphN type InferenceGraphNamespaceLister interface { // List lists all InferenceGraphs in the indexer for a given namespace. // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1alpha1.InferenceGraph, err error) + List(selector labels.Selector) (ret []*servingv1alpha1.InferenceGraph, err error) // Get retrieves the InferenceGraph from the indexer for a given namespace and name. // Objects returned here must be treated as read-only. - Get(name string) (*v1alpha1.InferenceGraph, error) + Get(name string) (*servingv1alpha1.InferenceGraph, error) InferenceGraphNamespaceListerExpansion } // inferenceGraphNamespaceLister implements the InferenceGraphNamespaceLister // interface. type inferenceGraphNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all InferenceGraphs in the indexer for a given namespace. -func (s inferenceGraphNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.InferenceGraph, err error) { - err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.InferenceGraph)) - }) - return ret, err -} - -// Get retrieves the InferenceGraph from the indexer for a given namespace and name. -func (s inferenceGraphNamespaceLister) Get(name string) (*v1alpha1.InferenceGraph, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1alpha1.Resource("inferencegraph"), name) - } - return obj.(*v1alpha1.InferenceGraph), nil + listers.ResourceIndexer[*servingv1alpha1.InferenceGraph] } diff --git a/pkg/client/listers/serving/v1alpha1/localmodelcache.go b/pkg/client/listers/serving/v1alpha1/localmodelcache.go index e3cd5f5f889..f412175c4e7 100644 --- a/pkg/client/listers/serving/v1alpha1/localmodelcache.go +++ b/pkg/client/listers/serving/v1alpha1/localmodelcache.go @@ -19,10 +19,10 @@ limitations under the License. package v1alpha1 import ( - v1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" + servingv1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" + labels "k8s.io/apimachinery/pkg/labels" + listers "k8s.io/client-go/listers" + cache "k8s.io/client-go/tools/cache" ) // LocalModelCacheLister helps list LocalModelCaches. @@ -30,7 +30,7 @@ import ( type LocalModelCacheLister interface { // List lists all LocalModelCaches in the indexer. // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1alpha1.LocalModelCache, err error) + List(selector labels.Selector) (ret []*servingv1alpha1.LocalModelCache, err error) // LocalModelCaches returns an object that can list and get LocalModelCaches. LocalModelCaches(namespace string) LocalModelCacheNamespaceLister LocalModelCacheListerExpansion @@ -38,25 +38,17 @@ type LocalModelCacheLister interface { // localModelCacheLister implements the LocalModelCacheLister interface. type localModelCacheLister struct { - indexer cache.Indexer + listers.ResourceIndexer[*servingv1alpha1.LocalModelCache] } // NewLocalModelCacheLister returns a new LocalModelCacheLister. func NewLocalModelCacheLister(indexer cache.Indexer) LocalModelCacheLister { - return &localModelCacheLister{indexer: indexer} -} - -// List lists all LocalModelCaches in the indexer. -func (s *localModelCacheLister) List(selector labels.Selector) (ret []*v1alpha1.LocalModelCache, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.LocalModelCache)) - }) - return ret, err + return &localModelCacheLister{listers.New[*servingv1alpha1.LocalModelCache](indexer, servingv1alpha1.Resource("localmodelcache"))} } // LocalModelCaches returns an object that can list and get LocalModelCaches. func (s *localModelCacheLister) LocalModelCaches(namespace string) LocalModelCacheNamespaceLister { - return localModelCacheNamespaceLister{indexer: s.indexer, namespace: namespace} + return localModelCacheNamespaceLister{listers.NewNamespaced[*servingv1alpha1.LocalModelCache](s.ResourceIndexer, namespace)} } // LocalModelCacheNamespaceLister helps list and get LocalModelCaches. @@ -64,36 +56,15 @@ func (s *localModelCacheLister) LocalModelCaches(namespace string) LocalModelCac type LocalModelCacheNamespaceLister interface { // List lists all LocalModelCaches in the indexer for a given namespace. // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1alpha1.LocalModelCache, err error) + List(selector labels.Selector) (ret []*servingv1alpha1.LocalModelCache, err error) // Get retrieves the LocalModelCache from the indexer for a given namespace and name. // Objects returned here must be treated as read-only. - Get(name string) (*v1alpha1.LocalModelCache, error) + Get(name string) (*servingv1alpha1.LocalModelCache, error) LocalModelCacheNamespaceListerExpansion } // localModelCacheNamespaceLister implements the LocalModelCacheNamespaceLister // interface. type localModelCacheNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all LocalModelCaches in the indexer for a given namespace. -func (s localModelCacheNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.LocalModelCache, err error) { - err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.LocalModelCache)) - }) - return ret, err -} - -// Get retrieves the LocalModelCache from the indexer for a given namespace and name. -func (s localModelCacheNamespaceLister) Get(name string) (*v1alpha1.LocalModelCache, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1alpha1.Resource("localmodelcache"), name) - } - return obj.(*v1alpha1.LocalModelCache), nil + listers.ResourceIndexer[*servingv1alpha1.LocalModelCache] } diff --git a/pkg/client/listers/serving/v1alpha1/localmodelnode.go b/pkg/client/listers/serving/v1alpha1/localmodelnode.go index ace902f1489..38db0d1490c 100644 --- a/pkg/client/listers/serving/v1alpha1/localmodelnode.go +++ b/pkg/client/listers/serving/v1alpha1/localmodelnode.go @@ -19,10 +19,10 @@ limitations under the License. package v1alpha1 import ( - v1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" + servingv1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" + labels "k8s.io/apimachinery/pkg/labels" + listers "k8s.io/client-go/listers" + cache "k8s.io/client-go/tools/cache" ) // LocalModelNodeLister helps list LocalModelNodes. @@ -30,7 +30,7 @@ import ( type LocalModelNodeLister interface { // List lists all LocalModelNodes in the indexer. // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1alpha1.LocalModelNode, err error) + List(selector labels.Selector) (ret []*servingv1alpha1.LocalModelNode, err error) // LocalModelNodes returns an object that can list and get LocalModelNodes. LocalModelNodes(namespace string) LocalModelNodeNamespaceLister LocalModelNodeListerExpansion @@ -38,25 +38,17 @@ type LocalModelNodeLister interface { // localModelNodeLister implements the LocalModelNodeLister interface. type localModelNodeLister struct { - indexer cache.Indexer + listers.ResourceIndexer[*servingv1alpha1.LocalModelNode] } // NewLocalModelNodeLister returns a new LocalModelNodeLister. func NewLocalModelNodeLister(indexer cache.Indexer) LocalModelNodeLister { - return &localModelNodeLister{indexer: indexer} -} - -// List lists all LocalModelNodes in the indexer. -func (s *localModelNodeLister) List(selector labels.Selector) (ret []*v1alpha1.LocalModelNode, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.LocalModelNode)) - }) - return ret, err + return &localModelNodeLister{listers.New[*servingv1alpha1.LocalModelNode](indexer, servingv1alpha1.Resource("localmodelnode"))} } // LocalModelNodes returns an object that can list and get LocalModelNodes. func (s *localModelNodeLister) LocalModelNodes(namespace string) LocalModelNodeNamespaceLister { - return localModelNodeNamespaceLister{indexer: s.indexer, namespace: namespace} + return localModelNodeNamespaceLister{listers.NewNamespaced[*servingv1alpha1.LocalModelNode](s.ResourceIndexer, namespace)} } // LocalModelNodeNamespaceLister helps list and get LocalModelNodes. @@ -64,36 +56,15 @@ func (s *localModelNodeLister) LocalModelNodes(namespace string) LocalModelNodeN type LocalModelNodeNamespaceLister interface { // List lists all LocalModelNodes in the indexer for a given namespace. // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1alpha1.LocalModelNode, err error) + List(selector labels.Selector) (ret []*servingv1alpha1.LocalModelNode, err error) // Get retrieves the LocalModelNode from the indexer for a given namespace and name. // Objects returned here must be treated as read-only. - Get(name string) (*v1alpha1.LocalModelNode, error) + Get(name string) (*servingv1alpha1.LocalModelNode, error) LocalModelNodeNamespaceListerExpansion } // localModelNodeNamespaceLister implements the LocalModelNodeNamespaceLister // interface. type localModelNodeNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all LocalModelNodes in the indexer for a given namespace. -func (s localModelNodeNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.LocalModelNode, err error) { - err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.LocalModelNode)) - }) - return ret, err -} - -// Get retrieves the LocalModelNode from the indexer for a given namespace and name. -func (s localModelNodeNamespaceLister) Get(name string) (*v1alpha1.LocalModelNode, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1alpha1.Resource("localmodelnode"), name) - } - return obj.(*v1alpha1.LocalModelNode), nil + listers.ResourceIndexer[*servingv1alpha1.LocalModelNode] } diff --git a/pkg/client/listers/serving/v1alpha1/localmodelnodegroup.go b/pkg/client/listers/serving/v1alpha1/localmodelnodegroup.go index 60dc28640b4..cd900534001 100644 --- a/pkg/client/listers/serving/v1alpha1/localmodelnodegroup.go +++ b/pkg/client/listers/serving/v1alpha1/localmodelnodegroup.go @@ -19,10 +19,10 @@ limitations under the License. package v1alpha1 import ( - v1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" + servingv1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" + labels "k8s.io/apimachinery/pkg/labels" + listers "k8s.io/client-go/listers" + cache "k8s.io/client-go/tools/cache" ) // LocalModelNodeGroupLister helps list LocalModelNodeGroups. @@ -30,7 +30,7 @@ import ( type LocalModelNodeGroupLister interface { // List lists all LocalModelNodeGroups in the indexer. // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1alpha1.LocalModelNodeGroup, err error) + List(selector labels.Selector) (ret []*servingv1alpha1.LocalModelNodeGroup, err error) // LocalModelNodeGroups returns an object that can list and get LocalModelNodeGroups. LocalModelNodeGroups(namespace string) LocalModelNodeGroupNamespaceLister LocalModelNodeGroupListerExpansion @@ -38,25 +38,17 @@ type LocalModelNodeGroupLister interface { // localModelNodeGroupLister implements the LocalModelNodeGroupLister interface. type localModelNodeGroupLister struct { - indexer cache.Indexer + listers.ResourceIndexer[*servingv1alpha1.LocalModelNodeGroup] } // NewLocalModelNodeGroupLister returns a new LocalModelNodeGroupLister. func NewLocalModelNodeGroupLister(indexer cache.Indexer) LocalModelNodeGroupLister { - return &localModelNodeGroupLister{indexer: indexer} -} - -// List lists all LocalModelNodeGroups in the indexer. -func (s *localModelNodeGroupLister) List(selector labels.Selector) (ret []*v1alpha1.LocalModelNodeGroup, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.LocalModelNodeGroup)) - }) - return ret, err + return &localModelNodeGroupLister{listers.New[*servingv1alpha1.LocalModelNodeGroup](indexer, servingv1alpha1.Resource("localmodelnodegroup"))} } // LocalModelNodeGroups returns an object that can list and get LocalModelNodeGroups. func (s *localModelNodeGroupLister) LocalModelNodeGroups(namespace string) LocalModelNodeGroupNamespaceLister { - return localModelNodeGroupNamespaceLister{indexer: s.indexer, namespace: namespace} + return localModelNodeGroupNamespaceLister{listers.NewNamespaced[*servingv1alpha1.LocalModelNodeGroup](s.ResourceIndexer, namespace)} } // LocalModelNodeGroupNamespaceLister helps list and get LocalModelNodeGroups. @@ -64,36 +56,15 @@ func (s *localModelNodeGroupLister) LocalModelNodeGroups(namespace string) Local type LocalModelNodeGroupNamespaceLister interface { // List lists all LocalModelNodeGroups in the indexer for a given namespace. // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1alpha1.LocalModelNodeGroup, err error) + List(selector labels.Selector) (ret []*servingv1alpha1.LocalModelNodeGroup, err error) // Get retrieves the LocalModelNodeGroup from the indexer for a given namespace and name. // Objects returned here must be treated as read-only. - Get(name string) (*v1alpha1.LocalModelNodeGroup, error) + Get(name string) (*servingv1alpha1.LocalModelNodeGroup, error) LocalModelNodeGroupNamespaceListerExpansion } // localModelNodeGroupNamespaceLister implements the LocalModelNodeGroupNamespaceLister // interface. type localModelNodeGroupNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all LocalModelNodeGroups in the indexer for a given namespace. -func (s localModelNodeGroupNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.LocalModelNodeGroup, err error) { - err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.LocalModelNodeGroup)) - }) - return ret, err -} - -// Get retrieves the LocalModelNodeGroup from the indexer for a given namespace and name. -func (s localModelNodeGroupNamespaceLister) Get(name string) (*v1alpha1.LocalModelNodeGroup, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1alpha1.Resource("localmodelnodegroup"), name) - } - return obj.(*v1alpha1.LocalModelNodeGroup), nil + listers.ResourceIndexer[*servingv1alpha1.LocalModelNodeGroup] } diff --git a/pkg/client/listers/serving/v1alpha1/servingruntime.go b/pkg/client/listers/serving/v1alpha1/servingruntime.go index f94d3fc32ca..28a539ab3c5 100644 --- a/pkg/client/listers/serving/v1alpha1/servingruntime.go +++ b/pkg/client/listers/serving/v1alpha1/servingruntime.go @@ -19,10 +19,10 @@ limitations under the License. package v1alpha1 import ( - v1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" + servingv1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" + labels "k8s.io/apimachinery/pkg/labels" + listers "k8s.io/client-go/listers" + cache "k8s.io/client-go/tools/cache" ) // ServingRuntimeLister helps list ServingRuntimes. @@ -30,7 +30,7 @@ import ( type ServingRuntimeLister interface { // List lists all ServingRuntimes in the indexer. // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1alpha1.ServingRuntime, err error) + List(selector labels.Selector) (ret []*servingv1alpha1.ServingRuntime, err error) // ServingRuntimes returns an object that can list and get ServingRuntimes. ServingRuntimes(namespace string) ServingRuntimeNamespaceLister ServingRuntimeListerExpansion @@ -38,25 +38,17 @@ type ServingRuntimeLister interface { // servingRuntimeLister implements the ServingRuntimeLister interface. type servingRuntimeLister struct { - indexer cache.Indexer + listers.ResourceIndexer[*servingv1alpha1.ServingRuntime] } // NewServingRuntimeLister returns a new ServingRuntimeLister. func NewServingRuntimeLister(indexer cache.Indexer) ServingRuntimeLister { - return &servingRuntimeLister{indexer: indexer} -} - -// List lists all ServingRuntimes in the indexer. -func (s *servingRuntimeLister) List(selector labels.Selector) (ret []*v1alpha1.ServingRuntime, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.ServingRuntime)) - }) - return ret, err + return &servingRuntimeLister{listers.New[*servingv1alpha1.ServingRuntime](indexer, servingv1alpha1.Resource("servingruntime"))} } // ServingRuntimes returns an object that can list and get ServingRuntimes. func (s *servingRuntimeLister) ServingRuntimes(namespace string) ServingRuntimeNamespaceLister { - return servingRuntimeNamespaceLister{indexer: s.indexer, namespace: namespace} + return servingRuntimeNamespaceLister{listers.NewNamespaced[*servingv1alpha1.ServingRuntime](s.ResourceIndexer, namespace)} } // ServingRuntimeNamespaceLister helps list and get ServingRuntimes. @@ -64,36 +56,15 @@ func (s *servingRuntimeLister) ServingRuntimes(namespace string) ServingRuntimeN type ServingRuntimeNamespaceLister interface { // List lists all ServingRuntimes in the indexer for a given namespace. // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1alpha1.ServingRuntime, err error) + List(selector labels.Selector) (ret []*servingv1alpha1.ServingRuntime, err error) // Get retrieves the ServingRuntime from the indexer for a given namespace and name. // Objects returned here must be treated as read-only. - Get(name string) (*v1alpha1.ServingRuntime, error) + Get(name string) (*servingv1alpha1.ServingRuntime, error) ServingRuntimeNamespaceListerExpansion } // servingRuntimeNamespaceLister implements the ServingRuntimeNamespaceLister // interface. type servingRuntimeNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all ServingRuntimes in the indexer for a given namespace. -func (s servingRuntimeNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.ServingRuntime, err error) { - err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.ServingRuntime)) - }) - return ret, err -} - -// Get retrieves the ServingRuntime from the indexer for a given namespace and name. -func (s servingRuntimeNamespaceLister) Get(name string) (*v1alpha1.ServingRuntime, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1alpha1.Resource("servingruntime"), name) - } - return obj.(*v1alpha1.ServingRuntime), nil + listers.ResourceIndexer[*servingv1alpha1.ServingRuntime] } diff --git a/pkg/client/listers/serving/v1alpha1/trainedmodel.go b/pkg/client/listers/serving/v1alpha1/trainedmodel.go index 2d17690e939..6c4f09d4659 100644 --- a/pkg/client/listers/serving/v1alpha1/trainedmodel.go +++ b/pkg/client/listers/serving/v1alpha1/trainedmodel.go @@ -19,10 +19,10 @@ limitations under the License. package v1alpha1 import ( - v1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" + servingv1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" + labels "k8s.io/apimachinery/pkg/labels" + listers "k8s.io/client-go/listers" + cache "k8s.io/client-go/tools/cache" ) // TrainedModelLister helps list TrainedModels. @@ -30,7 +30,7 @@ import ( type TrainedModelLister interface { // List lists all TrainedModels in the indexer. // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1alpha1.TrainedModel, err error) + List(selector labels.Selector) (ret []*servingv1alpha1.TrainedModel, err error) // TrainedModels returns an object that can list and get TrainedModels. TrainedModels(namespace string) TrainedModelNamespaceLister TrainedModelListerExpansion @@ -38,25 +38,17 @@ type TrainedModelLister interface { // trainedModelLister implements the TrainedModelLister interface. type trainedModelLister struct { - indexer cache.Indexer + listers.ResourceIndexer[*servingv1alpha1.TrainedModel] } // NewTrainedModelLister returns a new TrainedModelLister. func NewTrainedModelLister(indexer cache.Indexer) TrainedModelLister { - return &trainedModelLister{indexer: indexer} -} - -// List lists all TrainedModels in the indexer. -func (s *trainedModelLister) List(selector labels.Selector) (ret []*v1alpha1.TrainedModel, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.TrainedModel)) - }) - return ret, err + return &trainedModelLister{listers.New[*servingv1alpha1.TrainedModel](indexer, servingv1alpha1.Resource("trainedmodel"))} } // TrainedModels returns an object that can list and get TrainedModels. func (s *trainedModelLister) TrainedModels(namespace string) TrainedModelNamespaceLister { - return trainedModelNamespaceLister{indexer: s.indexer, namespace: namespace} + return trainedModelNamespaceLister{listers.NewNamespaced[*servingv1alpha1.TrainedModel](s.ResourceIndexer, namespace)} } // TrainedModelNamespaceLister helps list and get TrainedModels. @@ -64,36 +56,15 @@ func (s *trainedModelLister) TrainedModels(namespace string) TrainedModelNamespa type TrainedModelNamespaceLister interface { // List lists all TrainedModels in the indexer for a given namespace. // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1alpha1.TrainedModel, err error) + List(selector labels.Selector) (ret []*servingv1alpha1.TrainedModel, err error) // Get retrieves the TrainedModel from the indexer for a given namespace and name. // Objects returned here must be treated as read-only. - Get(name string) (*v1alpha1.TrainedModel, error) + Get(name string) (*servingv1alpha1.TrainedModel, error) TrainedModelNamespaceListerExpansion } // trainedModelNamespaceLister implements the TrainedModelNamespaceLister // interface. type trainedModelNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all TrainedModels in the indexer for a given namespace. -func (s trainedModelNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.TrainedModel, err error) { - err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.TrainedModel)) - }) - return ret, err -} - -// Get retrieves the TrainedModel from the indexer for a given namespace and name. -func (s trainedModelNamespaceLister) Get(name string) (*v1alpha1.TrainedModel, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1alpha1.Resource("trainedmodel"), name) - } - return obj.(*v1alpha1.TrainedModel), nil + listers.ResourceIndexer[*servingv1alpha1.TrainedModel] } diff --git a/pkg/client/listers/serving/v1beta1/inferenceservice.go b/pkg/client/listers/serving/v1beta1/inferenceservice.go index e11cfee019b..ab2c86176d0 100644 --- a/pkg/client/listers/serving/v1beta1/inferenceservice.go +++ b/pkg/client/listers/serving/v1beta1/inferenceservice.go @@ -19,10 +19,10 @@ limitations under the License. package v1beta1 import ( - v1beta1 "github.com/kserve/kserve/pkg/apis/serving/v1beta1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" + servingv1beta1 "github.com/kserve/kserve/pkg/apis/serving/v1beta1" + labels "k8s.io/apimachinery/pkg/labels" + listers "k8s.io/client-go/listers" + cache "k8s.io/client-go/tools/cache" ) // InferenceServiceLister helps list InferenceServices. @@ -30,7 +30,7 @@ import ( type InferenceServiceLister interface { // List lists all InferenceServices in the indexer. // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1beta1.InferenceService, err error) + List(selector labels.Selector) (ret []*servingv1beta1.InferenceService, err error) // InferenceServices returns an object that can list and get InferenceServices. InferenceServices(namespace string) InferenceServiceNamespaceLister InferenceServiceListerExpansion @@ -38,25 +38,17 @@ type InferenceServiceLister interface { // inferenceServiceLister implements the InferenceServiceLister interface. type inferenceServiceLister struct { - indexer cache.Indexer + listers.ResourceIndexer[*servingv1beta1.InferenceService] } // NewInferenceServiceLister returns a new InferenceServiceLister. func NewInferenceServiceLister(indexer cache.Indexer) InferenceServiceLister { - return &inferenceServiceLister{indexer: indexer} -} - -// List lists all InferenceServices in the indexer. -func (s *inferenceServiceLister) List(selector labels.Selector) (ret []*v1beta1.InferenceService, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1beta1.InferenceService)) - }) - return ret, err + return &inferenceServiceLister{listers.New[*servingv1beta1.InferenceService](indexer, servingv1beta1.Resource("inferenceservice"))} } // InferenceServices returns an object that can list and get InferenceServices. func (s *inferenceServiceLister) InferenceServices(namespace string) InferenceServiceNamespaceLister { - return inferenceServiceNamespaceLister{indexer: s.indexer, namespace: namespace} + return inferenceServiceNamespaceLister{listers.NewNamespaced[*servingv1beta1.InferenceService](s.ResourceIndexer, namespace)} } // InferenceServiceNamespaceLister helps list and get InferenceServices. @@ -64,36 +56,15 @@ func (s *inferenceServiceLister) InferenceServices(namespace string) InferenceSe type InferenceServiceNamespaceLister interface { // List lists all InferenceServices in the indexer for a given namespace. // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1beta1.InferenceService, err error) + List(selector labels.Selector) (ret []*servingv1beta1.InferenceService, err error) // Get retrieves the InferenceService from the indexer for a given namespace and name. // Objects returned here must be treated as read-only. - Get(name string) (*v1beta1.InferenceService, error) + Get(name string) (*servingv1beta1.InferenceService, error) InferenceServiceNamespaceListerExpansion } // inferenceServiceNamespaceLister implements the InferenceServiceNamespaceLister // interface. type inferenceServiceNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all InferenceServices in the indexer for a given namespace. -func (s inferenceServiceNamespaceLister) List(selector labels.Selector) (ret []*v1beta1.InferenceService, err error) { - err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*v1beta1.InferenceService)) - }) - return ret, err -} - -// Get retrieves the InferenceService from the indexer for a given namespace and name. -func (s inferenceServiceNamespaceLister) Get(name string) (*v1beta1.InferenceService, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1beta1.Resource("inferenceservice"), name) - } - return obj.(*v1beta1.InferenceService), nil + listers.ResourceIndexer[*servingv1beta1.InferenceService] } diff --git a/pkg/constants/constants.go b/pkg/constants/constants.go index 0df2711a3df..f3097f788ae 100644 --- a/pkg/constants/constants.go +++ b/pkg/constants/constants.go @@ -22,11 +22,11 @@ import ( "regexp" "strings" - "knative.dev/serving/pkg/apis/autoscaling" - - "knative.dev/pkg/network" - + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" + "knative.dev/pkg/network" + "knative.dev/serving/pkg/apis/autoscaling" ) // KServe Constants @@ -53,6 +53,8 @@ var ( const ( RouterHeadersPropagateEnvVar = "PROPAGATE_HEADERS" InferenceGraphLabel = "serving.kserve.io/inferencegraph" + RouterReadinessEndpoint = "/readyz" + RouterPort = 8080 ) // TrainedModel Constants @@ -101,8 +103,9 @@ var ( PrometheusPathAnnotationKey = "prometheus.io/path" StorageReadonlyAnnotationKey = "storage.kserve.io/readonly" DefaultPrometheusPath = "/metrics" - QueueProxyAggregatePrometheusMetricsPort = 9088 + QueueProxyAggregatePrometheusMetricsPort = "9088" DefaultPodPrometheusPort = "9091" + NodeGroupAnnotationKey = KServeAPIGroupName + "/nodegroup" ) // InferenceService Internal Annotations @@ -137,6 +140,8 @@ const ( ClusterLocalDomain = "svc.cluster.local" IsvcNameHeader = "KServe-Isvc-Name" IsvcNamespaceHeader = "KServe-Isvc-Namespace" + HostHeader = "Host" + GatewayName = "kserve-ingress-gateway" ) // StorageSpec Constants @@ -147,19 +152,21 @@ var ( // Controller Constants var ( - ControllerLabelName = KServeName + "-controller-manager" - DefaultIstioSidecarUID = int64(1337) - DefaultMinReplicas = 1 - IstioInitContainerName = "istio-init" - IstioInterceptModeRedirect = "REDIRECT" - IstioInterceptionModeAnnotation = "sidecar.istio.io/interceptionMode" - IstioSidecarUIDAnnotationKey = KServeAPIGroupName + "/storage-initializer-uid" - IstioSidecarStatusAnnotation = "sidecar.istio.io/status" + ControllerLabelName = KServeName + "-controller-manager" + DefaultIstioSidecarUID = int64(1337) + DefaultMinReplicas int32 = 1 + IstioInitContainerName = "istio-init" + IstioInterceptModeRedirect = "REDIRECT" + IstioInterceptionModeAnnotation = "sidecar.istio.io/interceptionMode" + IstioSidecarUIDAnnotationKey = KServeAPIGroupName + "/storage-initializer-uid" + IstioSidecarStatusAnnotation = "sidecar.istio.io/status" ) -type AutoscalerClassType string -type AutoscalerMetricsType string -type AutoScalerKPAMetricsType string +type ( + AutoscalerClassType string + AutoscalerMetricsType string + AutoScalerKPAMetricsType string +) var ( AutoScalerKPAMetricsRPS AutoScalerKPAMetricsType = "rps" @@ -224,9 +231,7 @@ const ( GaudiGPUResourceType = "habana.ai/gaudi" ) -var ( - CustomGPUResourceTypesAnnotationKey = KServeAPIGroupName + "/gpu-resource-types" -) +var CustomGPUResourceTypesAnnotationKey = KServeAPIGroupName + "/gpu-resource-types" var GPUResourceTypeList = []string{ NvidiaGPUResourceType, @@ -478,6 +483,9 @@ const ( const ( IstioVirtualServiceKind = "VirtualService" KnativeServiceKind = "Service" + HTTPRouteKind = "HTTPRoute" + GatewayKind = "Gateway" + ServiceKind = "Service" ) // Model Parallel Options @@ -529,11 +537,6 @@ func getEnvOrDefault(key string, fallback string) string { return fallback } -// nolint: unused -func isEnvVarMatched(envVar, matchtedValue string) bool { - return getEnvOrDefault(envVar, "") == matchtedValue -} - func InferenceServiceURL(scheme, name, namespace, domain string) string { return fmt.Sprintf("%s://%s.%s.%s%s", scheme, name, namespace, domain, InferenceServicePrefix(name)) } @@ -595,7 +598,7 @@ func ModelConfigName(inferenceserviceName string, shardId int) string { } func InferenceServicePrefix(name string) string { - return fmt.Sprintf("/v1/models/%s", name) + return "/v1/models/" + name } func PredictPath(name string, protocol InferenceServiceProtocol) string { @@ -620,6 +623,11 @@ func ExplainPrefix() string { return "^/v1/models/[\\w-]+:explain$" } +// FallbackPrefix returns the regex pattern to match any path +func FallbackPrefix() string { + return "^/.*$" +} + func PathBasedExplainPrefix() string { return "(/v1/models/[\\w-]+:explain)$" } @@ -698,3 +706,24 @@ func GetProtocolVersionString(protocol ProtocolVersion) InferenceServiceProtocol return ProtocolUnknown } } + +func GetRouterReadinessProbe() *corev1.Probe { + probe := &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: RouterReadinessEndpoint, + Port: intstr.IntOrString{ + Type: intstr.Int, + IntVal: RouterPort, + }, + Scheme: corev1.URISchemeHTTP, + }, + }, + InitialDelaySeconds: 5, + TimeoutSeconds: 2, + PeriodSeconds: 5, + SuccessThreshold: 1, + FailureThreshold: 3, + } + return probe +} diff --git a/pkg/controller/v1alpha1/inferencegraph/controller.go b/pkg/controller/v1alpha1/inferencegraph/controller.go index cbbeb78e6c1..eba16029c92 100644 --- a/pkg/controller/v1alpha1/inferencegraph/controller.go +++ b/pkg/controller/v1alpha1/inferencegraph/controller.go @@ -27,10 +27,9 @@ import ( "fmt" "github.com/go-logr/logr" - "github.com/kserve/kserve/pkg/utils" "github.com/pkg/errors" appsv1 "k8s.io/api/apps/v1" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/equality" apierr "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/resource" @@ -47,11 +46,11 @@ import ( "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/reconcile" - v1alpha1api "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" + "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" "github.com/kserve/kserve/pkg/apis/serving/v1beta1" - v1beta1api "github.com/kserve/kserve/pkg/apis/serving/v1beta1" "github.com/kserve/kserve/pkg/constants" isvcutils "github.com/kserve/kserve/pkg/controller/v1beta1/inferenceservice/utils" + "github.com/kserve/kserve/pkg/utils" ) // InferenceGraphReconciler reconciles a InferenceGraph object @@ -93,7 +92,7 @@ type RouterConfig struct { Headers map[string][]string `json:"headers"` } -func getRouterConfigs(configMap *v1.ConfigMap) (*RouterConfig, error) { +func getRouterConfigs(configMap *corev1.ConfigMap) (*RouterConfig, error) { routerConfig := &RouterConfig{} if agentConfigValue, ok := configMap.Data["router"]; ok { err := json.Unmarshal([]byte(agentConfigValue), &routerConfig) @@ -103,10 +102,12 @@ func getRouterConfigs(configMap *v1.ConfigMap) (*RouterConfig, error) { } // Ensure that we set proper values for CPU/Memory Limit/Request - resourceDefaults := []string{routerConfig.MemoryRequest, + resourceDefaults := []string{ + routerConfig.MemoryRequest, routerConfig.MemoryLimit, routerConfig.CpuRequest, - routerConfig.CpuLimit} + routerConfig.CpuLimit, + } for _, key := range resourceDefaults { _, err := resource.ParseQuantity(key) if err != nil { @@ -119,10 +120,8 @@ func getRouterConfigs(configMap *v1.ConfigMap) (*RouterConfig, error) { } func (r *InferenceGraphReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - _ = context.Background() - // Fetch the InferenceService instance - graph := &v1alpha1api.InferenceGraph{} + graph := &v1alpha1.InferenceGraph{} if err := r.Get(ctx, req.NamespacedName, graph); err != nil { if apierr.IsNotFound(err) { // Object not found, return. Created objects are automatically garbage collected. @@ -133,7 +132,7 @@ func (r *InferenceGraphReconciler) Reconcile(ctx context.Context, req ctrl.Reque } r.Log.Info("Reconciling inference graph", "apiVersion", graph.APIVersion, "graph", graph.Name) - configMap, err := r.Clientset.CoreV1().ConfigMaps(constants.KServeNamespace).Get(context.TODO(), constants.InferenceServiceConfigMapName, metav1.GetOptions{}) + configMap, err := r.Clientset.CoreV1().ConfigMaps(constants.KServeNamespace).Get(ctx, constants.InferenceServiceConfigMapName, metav1.GetOptions{}) if err != nil { r.Log.Error(err, "Failed to find config map", "name", constants.InferenceServiceConfigMapName) return reconcile.Result{}, err @@ -165,7 +164,12 @@ func (r *InferenceGraphReconciler) Reconcile(ctx context.Context, req ctrl.Reque } } } - deployConfig, err := v1beta1api.NewDeployConfig(r.Clientset) + isvcConfigMap, err := v1beta1.GetInferenceServiceConfigMap(ctx, r.Clientset) + if err != nil { + r.Log.Error(err, "unable to get configmap", "name", constants.InferenceServiceConfigMapName, "namespace", constants.KServeNamespace) + return reconcile.Result{}, err + } + deployConfig, err := v1beta1.NewDeployConfig(isvcConfigMap) if err != nil { return reconcile.Result{}, errors.Wrapf(err, "fails to create DeployConfig") } @@ -174,8 +178,7 @@ func (r *InferenceGraphReconciler) Reconcile(ctx context.Context, req ctrl.Reque r.Log.Info("Inference graph deployment ", "deployment mode ", deploymentMode) if deploymentMode == constants.RawDeployment { // Create inference graph resources such as deployment, service, hpa in raw deployment mode - deployment, url, err := handleInferenceGraphRawDeployment(r.Client, r.Clientset, r.Scheme, graph, routerConfig) - + deployment, url, err := handleInferenceGraphRawDeployment(ctx, r.Client, r.Clientset, r.Scheme, graph, routerConfig) if err != nil { return ctrl.Result{}, errors.Wrapf(err, "fails to reconcile inference graph raw deployment") } @@ -203,7 +206,7 @@ func (r *InferenceGraphReconciler) Reconcile(ctx context.Context, req ctrl.Reque } if !ksvcAvailable { - r.Recorder.Event(graph, v1.EventTypeWarning, "ServerlessModeRejected", + r.Recorder.Event(graph, corev1.EventTypeWarning, "ServerlessModeRejected", "It is not possible to use Serverless deployment mode when Knative Services are not available") return reconcile.Result{Requeue: false}, reconcile.TerminalError(fmt.Errorf("the resolved deployment mode of InferenceGraph '%s' is Serverless, but Knative Serving is not available", graph.Name)) } @@ -214,7 +217,7 @@ func (r *InferenceGraphReconciler) Reconcile(ctx context.Context, req ctrl.Reque return reconcile.Result{}, err } knativeReconciler := NewGraphKnativeServiceReconciler(r.Client, r.Scheme, desired) - ksvcStatus, err := knativeReconciler.Reconcile() + ksvcStatus, err := knativeReconciler.Reconcile(ctx) if err != nil { r.Log.Error(err, "failed to reconcile inference graph ksvc", "name", graph.GetName()) return reconcile.Result{}, errors.Wrapf(err, "fails to reconcile inference graph ksvc") @@ -234,18 +237,18 @@ func (r *InferenceGraphReconciler) Reconcile(ctx context.Context, req ctrl.Reque } } - if err := r.updateStatus(graph); err != nil { - r.Recorder.Eventf(graph, v1.EventTypeWarning, "InternalError", err.Error()) + if err := r.updateStatus(ctx, graph); err != nil { + r.Recorder.Eventf(graph, corev1.EventTypeWarning, "InternalError", err.Error()) return reconcile.Result{}, err } return ctrl.Result{}, nil } -func (r *InferenceGraphReconciler) updateStatus(desiredGraph *v1alpha1api.InferenceGraph) error { - graph := &v1alpha1api.InferenceGraph{} +func (r *InferenceGraphReconciler) updateStatus(ctx context.Context, desiredGraph *v1alpha1.InferenceGraph) error { + graph := &v1alpha1.InferenceGraph{} namespacedName := types.NamespacedName{Name: desiredGraph.Name, Namespace: desiredGraph.Namespace} - if err := r.Get(context.TODO(), namespacedName, graph); err != nil { + if err := r.Get(ctx, namespacedName, graph); err != nil { return err } @@ -255,9 +258,9 @@ func (r *InferenceGraphReconciler) updateStatus(desiredGraph *v1alpha1api.Infere // This is important because the copy we loaded from the informer's // cache may be stale and we don't want to overwrite a prior update // to status with this stale state. - } else if err := r.Status().Update(context.TODO(), desiredGraph); err != nil { + } else if err := r.Status().Update(ctx, desiredGraph); err != nil { r.Log.Error(err, "Failed to update InferenceGraph status", "InferenceGraph", desiredGraph.Name) - r.Recorder.Eventf(desiredGraph, v1.EventTypeWarning, "UpdateFailed", + r.Recorder.Eventf(desiredGraph, corev1.EventTypeWarning, "UpdateFailed", "Failed to update status for InferenceGraph %q: %v", desiredGraph.Name, err) return errors.Wrapf(err, "fails to update InferenceGraph status") } else { @@ -265,23 +268,23 @@ func (r *InferenceGraphReconciler) updateStatus(desiredGraph *v1alpha1api.Infere // If there was a difference and there was no error. isReady := inferenceGraphReadiness(desiredGraph.Status) if wasReady && !isReady { // Moved to NotReady State - r.Recorder.Eventf(desiredGraph, v1.EventTypeWarning, string(InferenceGraphNotReadyState), + r.Recorder.Eventf(desiredGraph, corev1.EventTypeWarning, string(InferenceGraphNotReadyState), fmt.Sprintf("InferenceGraph [%v] is no longer Ready", desiredGraph.GetName())) } else if !wasReady && isReady { // Moved to Ready State - r.Recorder.Eventf(desiredGraph, v1.EventTypeNormal, string(InferenceGraphReadyState), + r.Recorder.Eventf(desiredGraph, corev1.EventTypeNormal, string(InferenceGraphReadyState), fmt.Sprintf("InferenceGraph [%v] is Ready", desiredGraph.GetName())) } } return nil } -func inferenceGraphReadiness(status v1alpha1api.InferenceGraphStatus) bool { +func inferenceGraphReadiness(status v1alpha1.InferenceGraphStatus) bool { return status.Conditions != nil && status.GetCondition(apis.ConditionReady) != nil && - status.GetCondition(apis.ConditionReady).Status == v1.ConditionTrue + status.GetCondition(apis.ConditionReady).Status == corev1.ConditionTrue } -func (r *InferenceGraphReconciler) SetupWithManager(mgr ctrl.Manager, deployConfig *v1beta1api.DeployConfig) error { +func (r *InferenceGraphReconciler) SetupWithManager(mgr ctrl.Manager, deployConfig *v1beta1.DeployConfig) error { r.ClientConfig = mgr.GetConfig() ksvcFound, err := utils.IsCrdAvailable(r.ClientConfig, knservingv1.SchemeGroupVersion.String(), constants.KnativeServiceKind) @@ -290,7 +293,7 @@ func (r *InferenceGraphReconciler) SetupWithManager(mgr ctrl.Manager, deployConf } ctrlBuilder := ctrl.NewControllerManagedBy(mgr). - For(&v1alpha1api.InferenceGraph{}). + For(&v1alpha1.InferenceGraph{}). Owns(&appsv1.Deployment{}) if ksvcFound { diff --git a/pkg/controller/v1alpha1/inferencegraph/controller_test.go b/pkg/controller/v1alpha1/inferencegraph/controller_test.go index 5ee18d38dbd..2ab96032f1e 100644 --- a/pkg/controller/v1alpha1/inferencegraph/controller_test.go +++ b/pkg/controller/v1alpha1/inferencegraph/controller_test.go @@ -18,17 +18,13 @@ package inferencegraph import ( "context" - "fmt" "time" - "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" - "github.com/kserve/kserve/pkg/constants" - "github.com/kserve/kserve/pkg/utils" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "google.golang.org/protobuf/proto" appsv1 "k8s.io/api/apps/v1" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" @@ -36,10 +32,13 @@ import ( knservingv1 "knative.dev/serving/pkg/apis/serving/v1" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/reconcile" + + "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" + "github.com/kserve/kserve/pkg/constants" + "github.com/kserve/kserve/pkg/utils" ) var _ = Describe("Inference Graph controller test", func() { - // Define utility constants for object names and testing timeouts/durations and intervals. const ( timeout = time.Second * 10 @@ -47,9 +46,8 @@ var _ = Describe("Inference Graph controller test", func() { domain = "example.com" ) - var ( - configs = map[string]string{ - "router": `{ + configs := map[string]string{ + "router": `{ "image": "kserve/router:v0.10.0", "memoryRequest": "100Mi", "memoryLimit": "500Mi", @@ -62,13 +60,14 @@ var _ = Describe("Inference Graph controller test", func() { ] } }`, - } - ) + } + + expectedReadinessProbe := constants.GetRouterReadinessProbe() Context("When creating an inferencegraph with headers in global config", func() { It("Should create a knative service with headers as env var of podspec", func() { By("By creating a new InferenceGraph") - var configMap = &v1.ConfigMap{ + configMap := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: constants.InferenceServiceConfigMapName, Namespace: constants.KServeNamespace, @@ -78,8 +77,8 @@ var _ = Describe("Inference Graph controller test", func() { Expect(k8sClient.Create(context.TODO(), configMap)).NotTo(HaveOccurred()) defer k8sClient.Delete(context.TODO(), configMap) graphName := "singlenode1" - var expectedRequest = reconcile.Request{NamespacedName: types.NamespacedName{Name: graphName, Namespace: "default"}} - var serviceKey = expectedRequest.NamespacedName + expectedRequest := reconcile.Request{NamespacedName: types.NamespacedName{Name: graphName, Namespace: "default"}} + serviceKey := expectedRequest.NamespacedName ctx := context.Background() ig := &v1alpha1.InferenceGraph{ ObjectMeta: metav1.ObjectMeta{ @@ -109,10 +108,7 @@ var _ = Describe("Inference Graph controller test", func() { inferenceGraphSubmitted := &v1alpha1.InferenceGraph{} Eventually(func() bool { err := k8sClient.Get(ctx, serviceKey, inferenceGraphSubmitted) - if err != nil { - return false - } - return true + return err == nil }, timeout, interval).Should(BeTrue()) actualKnServiceCreated := &knservingv1.Service{} @@ -142,11 +138,11 @@ var _ = Describe("Inference Graph controller test", func() { Spec: knservingv1.RevisionSpec{ ContainerConcurrency: nil, TimeoutSeconds: nil, - PodSpec: v1.PodSpec{ - Containers: []v1.Container{ + PodSpec: corev1.PodSpec{ + Containers: []corev1.Container{ { Image: "kserve/router:v0.10.0", - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "PROPAGATE_HEADERS", Value: "Authorization,Intuit_tid", @@ -156,23 +152,24 @@ var _ = Describe("Inference Graph controller test", func() { "--graph-json", "{\"nodes\":{\"root\":{\"routerType\":\"Sequence\",\"steps\":[{\"serviceUrl\":\"http://someservice.exmaple.com\"}]}},\"resources\":{}}", }, - Resources: v1.ResourceRequirements{ - Limits: v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("100m"), - v1.ResourceMemory: resource.MustParse("500Mi"), + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("500Mi"), }, - Requests: v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("100m"), - v1.ResourceMemory: resource.MustParse("100Mi"), + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("100Mi"), }, }, - SecurityContext: &v1.SecurityContext{ + ReadinessProbe: expectedReadinessProbe, + SecurityContext: &corev1.SecurityContext{ Privileged: proto.Bool(false), RunAsNonRoot: proto.Bool(true), ReadOnlyRootFilesystem: proto.Bool(true), AllowPrivilegeEscalation: proto.Bool(false), - Capabilities: &v1.Capabilities{ - Drop: []v1.Capability{v1.Capability("ALL")}, + Capabilities: &corev1.Capabilities{ + Drop: []corev1.Capability{corev1.Capability("ALL")}, }, }, }, @@ -190,14 +187,14 @@ var _ = Describe("Inference Graph controller test", func() { // Do a dry-run update. This will populate our local knative service object with any default values // that are present on the remote version. err := k8sClient.Update(context.TODO(), expectedKnService, client.DryRunAll) - Expect(err).Should(BeNil()) + Expect(err).ShouldNot(HaveOccurred()) Expect(kmp.SafeDiff(actualKnServiceCreated.Spec, expectedKnService.Spec)).To(Equal("")) }) }) Context("When creating an IG with resource requirements in the spec", func() { It("Should propagate to underlying pod", func() { - var configMap = &v1.ConfigMap{ + configMap := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: constants.InferenceServiceConfigMapName, Namespace: constants.KServeNamespace, @@ -207,8 +204,8 @@ var _ = Describe("Inference Graph controller test", func() { Expect(k8sClient.Create(context.TODO(), configMap)).NotTo(HaveOccurred()) defer k8sClient.Delete(context.TODO(), configMap) graphName := "singlenode2" - var expectedRequest = reconcile.Request{NamespacedName: types.NamespacedName{Name: graphName, Namespace: "default"}} - var serviceKey = expectedRequest.NamespacedName + expectedRequest := reconcile.Request{NamespacedName: types.NamespacedName{Name: graphName, Namespace: "default"}} + serviceKey := expectedRequest.NamespacedName ctx := context.Background() ig := &v1alpha1.InferenceGraph{ ObjectMeta: metav1.ObjectMeta{ @@ -219,14 +216,14 @@ var _ = Describe("Inference Graph controller test", func() { }, }, Spec: v1alpha1.InferenceGraphSpec{ - Resources: v1.ResourceRequirements{ - Limits: v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("123m"), - v1.ResourceMemory: resource.MustParse("123Mi"), + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("123m"), + corev1.ResourceMemory: resource.MustParse("123Mi"), }, - Requests: v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("123m"), - v1.ResourceMemory: resource.MustParse("123Mi"), + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("123m"), + corev1.ResourceMemory: resource.MustParse("123Mi"), }, }, Nodes: map[string]v1alpha1.InferenceRouter{ @@ -278,11 +275,11 @@ var _ = Describe("Inference Graph controller test", func() { Spec: knservingv1.RevisionSpec{ ContainerConcurrency: nil, TimeoutSeconds: nil, - PodSpec: v1.PodSpec{ - Containers: []v1.Container{ + PodSpec: corev1.PodSpec{ + Containers: []corev1.Container{ { Image: "kserve/router:v0.10.0", - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "PROPAGATE_HEADERS", Value: "Authorization,Intuit_tid", @@ -292,23 +289,24 @@ var _ = Describe("Inference Graph controller test", func() { "--graph-json", "{\"nodes\":{\"root\":{\"routerType\":\"Sequence\",\"steps\":[{\"serviceUrl\":\"http://someservice.exmaple.com\"}]}},\"resources\":{\"limits\":{\"cpu\":\"123m\",\"memory\":\"123Mi\"},\"requests\":{\"cpu\":\"123m\",\"memory\":\"123Mi\"}}}", }, - Resources: v1.ResourceRequirements{ - Limits: v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("123m"), - v1.ResourceMemory: resource.MustParse("123Mi"), + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("123m"), + corev1.ResourceMemory: resource.MustParse("123Mi"), }, - Requests: v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("123m"), - v1.ResourceMemory: resource.MustParse("123Mi"), + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("123m"), + corev1.ResourceMemory: resource.MustParse("123Mi"), }, }, - SecurityContext: &v1.SecurityContext{ + ReadinessProbe: expectedReadinessProbe, + SecurityContext: &corev1.SecurityContext{ Privileged: proto.Bool(false), RunAsNonRoot: proto.Bool(true), ReadOnlyRootFilesystem: proto.Bool(true), AllowPrivilegeEscalation: proto.Bool(false), - Capabilities: &v1.Capabilities{ - Drop: []v1.Capability{v1.Capability("ALL")}, + Capabilities: &corev1.Capabilities{ + Drop: []corev1.Capability{corev1.Capability("ALL")}, }, }, }, @@ -326,14 +324,14 @@ var _ = Describe("Inference Graph controller test", func() { // Do a dry-run update. This will populate our local knative service object with any default values // that are present on the remote version. err := k8sClient.Update(context.TODO(), expectedKnService, client.DryRunAll) - Expect(err).Should(BeNil()) + Expect(err).ShouldNot(HaveOccurred()) Expect(kmp.SafeDiff(actualKnServiceCreated.Spec, expectedKnService.Spec)).To(Equal("")) }) }) Context("When creating an IG with podaffinity in the spec", func() { It("Should propagate to underlying pod", func() { - var configMap = &v1.ConfigMap{ + configMap := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: constants.InferenceServiceConfigMapName, Namespace: constants.KServeNamespace, @@ -343,8 +341,8 @@ var _ = Describe("Inference Graph controller test", func() { Expect(k8sClient.Create(context.TODO(), configMap)).NotTo(HaveOccurred()) defer k8sClient.Delete(context.TODO(), configMap) graphName := "singlenode3" - var expectedRequest = reconcile.Request{NamespacedName: types.NamespacedName{Name: graphName, Namespace: "default"}} - var serviceKey = expectedRequest.NamespacedName + expectedRequest := reconcile.Request{NamespacedName: types.NamespacedName{Name: graphName, Namespace: "default"}} + serviceKey := expectedRequest.NamespacedName ctx := context.Background() ig := &v1alpha1.InferenceGraph{ ObjectMeta: metav1.ObjectMeta{ @@ -356,12 +354,12 @@ var _ = Describe("Inference Graph controller test", func() { }, Spec: v1alpha1.InferenceGraphSpec{ - Affinity: &v1.Affinity{ - PodAffinity: &v1.PodAffinity{ - PreferredDuringSchedulingIgnoredDuringExecution: []v1.WeightedPodAffinityTerm{ + Affinity: &corev1.Affinity{ + PodAffinity: &corev1.PodAffinity{ + PreferredDuringSchedulingIgnoredDuringExecution: []corev1.WeightedPodAffinityTerm{ { Weight: 100, - PodAffinityTerm: v1.PodAffinityTerm{ + PodAffinityTerm: corev1.PodAffinityTerm{ LabelSelector: &metav1.LabelSelector{ MatchExpressions: []metav1.LabelSelectorRequirement{ { @@ -428,11 +426,11 @@ var _ = Describe("Inference Graph controller test", func() { Spec: knservingv1.RevisionSpec{ ContainerConcurrency: nil, TimeoutSeconds: nil, - PodSpec: v1.PodSpec{ - Containers: []v1.Container{ + PodSpec: corev1.PodSpec{ + Containers: []corev1.Container{ { Image: "kserve/router:v0.10.0", - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "PROPAGATE_HEADERS", Value: "Authorization,Intuit_tid", @@ -442,33 +440,34 @@ var _ = Describe("Inference Graph controller test", func() { "--graph-json", "{\"nodes\":{\"root\":{\"routerType\":\"Sequence\",\"steps\":[{\"serviceUrl\":\"http://someservice.exmaple.com\"}]}},\"resources\":{},\"affinity\":{\"podAffinity\":{\"preferredDuringSchedulingIgnoredDuringExecution\":[{\"weight\":100,\"podAffinityTerm\":{\"labelSelector\":{\"matchExpressions\":[{\"key\":\"serving.kserve.io/inferencegraph\",\"operator\":\"In\",\"values\":[\"singlenode3\"]}]},\"topologyKey\":\"topology.kubernetes.io/zone\"}}]}}}", }, - Resources: v1.ResourceRequirements{ - Limits: v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("100m"), - v1.ResourceMemory: resource.MustParse("500Mi"), + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("500Mi"), }, - Requests: v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("100m"), - v1.ResourceMemory: resource.MustParse("100Mi"), + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("100Mi"), }, }, - SecurityContext: &v1.SecurityContext{ + SecurityContext: &corev1.SecurityContext{ Privileged: proto.Bool(false), RunAsNonRoot: proto.Bool(true), ReadOnlyRootFilesystem: proto.Bool(true), AllowPrivilegeEscalation: proto.Bool(false), - Capabilities: &v1.Capabilities{ - Drop: []v1.Capability{v1.Capability("ALL")}, + Capabilities: &corev1.Capabilities{ + Drop: []corev1.Capability{corev1.Capability("ALL")}, }, }, + ReadinessProbe: expectedReadinessProbe, }, }, - Affinity: &v1.Affinity{ - PodAffinity: &v1.PodAffinity{ - PreferredDuringSchedulingIgnoredDuringExecution: []v1.WeightedPodAffinityTerm{ + Affinity: &corev1.Affinity{ + PodAffinity: &corev1.PodAffinity{ + PreferredDuringSchedulingIgnoredDuringExecution: []corev1.WeightedPodAffinityTerm{ { Weight: 100, - PodAffinityTerm: v1.PodAffinityTerm{ + PodAffinityTerm: corev1.PodAffinityTerm{ LabelSelector: &metav1.LabelSelector{ MatchExpressions: []metav1.LabelSelectorRequirement{ { @@ -499,7 +498,7 @@ var _ = Describe("Inference Graph controller test", func() { // Do a dry-run update. This will populate our local knative service object with any default values // that are present on the remote version. err := k8sClient.Update(context.TODO(), expectedKnService, client.DryRunAll) - Expect(err).Should(BeNil()) + Expect(err).ShouldNot(HaveOccurred()) Expect(kmp.SafeDiff(actualKnServiceCreated.Spec, expectedKnService.Spec)).To(Equal("")) }) }) @@ -507,7 +506,7 @@ var _ = Describe("Inference Graph controller test", func() { Context("When creating an inferencegraph in Raw deployment mode with annotations", func() { It("Should create a raw k8s resources with podspec", func() { By("By creating a new InferenceGraph") - var configMap = &v1.ConfigMap{ + configMap := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: constants.InferenceServiceConfigMapName, Namespace: constants.KServeNamespace, @@ -517,8 +516,8 @@ var _ = Describe("Inference Graph controller test", func() { Expect(k8sClient.Create(context.TODO(), configMap)).NotTo(HaveOccurred()) defer k8sClient.Delete(context.TODO(), configMap) graphName := "igraw1" - var expectedRequest = reconcile.Request{NamespacedName: types.NamespacedName{Name: graphName, Namespace: "default"}} - var serviceKey = expectedRequest.NamespacedName + expectedRequest := reconcile.Request{NamespacedName: types.NamespacedName{Name: graphName, Namespace: "default"}} + serviceKey := expectedRequest.NamespacedName ctx := context.Background() ig := &v1alpha1.InferenceGraph{ ObjectMeta: metav1.ObjectMeta{ @@ -560,12 +559,11 @@ var _ = Describe("Inference Graph controller test", func() { if err := k8sClient.Get(ctx, serviceKey, actualK8sDeploymentCreated); err != nil { return false } - fmt.Println(actualK8sDeploymentCreated) By("K8s Deployment retrieved") return true }, timeout, interval).Should(BeTrue()) - actualK8sServiceCreated := &v1.Service{} + actualK8sServiceCreated := &corev1.Service{} Eventually(func() bool { if err := k8sClient.Get(ctx, serviceKey, actualK8sServiceCreated); err != nil { return false @@ -574,7 +572,7 @@ var _ = Describe("Inference Graph controller test", func() { return true }, timeout, interval).Should(BeTrue()) - //No KNative Service should get created in Raw deployment mode + // No KNative Service should get created in Raw deployment mode actualKnServiceCreated := &knservingv1.Service{} Eventually(func() bool { if err := k8sClient.Get(context.TODO(), serviceKey, actualKnServiceCreated); err != nil { @@ -585,7 +583,7 @@ var _ = Describe("Inference Graph controller test", func() { }, timeout). Should(BeFalse()) - //No Knative Route should get created in Raw deployment mode + // No Knative Route should get created in Raw deployment mode actualKnRouteCreated := &knservingv1.Route{} Eventually(func() bool { if err := k8sClient.Get(context.TODO(), serviceKey, actualKnRouteCreated); err != nil { @@ -595,7 +593,7 @@ var _ = Describe("Inference Graph controller test", func() { }, timeout). Should(BeFalse()) - var result = int32(1) + result := int32(1) Expect(actualK8sDeploymentCreated.Name).To(Equal(graphName)) Expect(actualK8sDeploymentCreated.Spec.Replicas).To(Equal(&result)) Expect(actualK8sDeploymentCreated.Spec.Template.Spec.Containers).To(Not(BeNil())) @@ -608,12 +606,12 @@ var _ = Describe("Inference Graph controller test", func() { It("Should fail if Knative Serving is not installed", func() { // Simulate Knative Serving is absent by setting to false the relevant item in utils.gvResourcesCache variable servingResources, getServingResourcesErr := utils.GetAvailableResourcesForApi(cfg, knservingv1.SchemeGroupVersion.String()) - Expect(getServingResourcesErr).To(BeNil()) + Expect(getServingResourcesErr).ToNot(HaveOccurred()) defer utils.SetAvailableResourcesForApi(knservingv1.SchemeGroupVersion.String(), servingResources) utils.SetAvailableResourcesForApi(knservingv1.SchemeGroupVersion.String(), nil) By("By creating a new InferenceGraph") - var configMap = &v1.ConfigMap{ + configMap := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: constants.InferenceServiceConfigMapName, Namespace: constants.KServeNamespace, @@ -624,8 +622,8 @@ var _ = Describe("Inference Graph controller test", func() { defer k8sClient.Delete(context.TODO(), configMap) graphName := "singlenode1" - var expectedRequest = reconcile.Request{NamespacedName: types.NamespacedName{Name: graphName, Namespace: "default"}} - var serviceKey = expectedRequest.NamespacedName + expectedRequest := reconcile.Request{NamespacedName: types.NamespacedName{Name: graphName, Namespace: "default"}} + serviceKey := expectedRequest.NamespacedName ctx := context.Background() ig := &v1alpha1.InferenceGraph{ ObjectMeta: metav1.ObjectMeta{ @@ -654,7 +652,7 @@ var _ = Describe("Inference Graph controller test", func() { defer k8sClient.Delete(ctx, ig) Eventually(func() bool { - events := &v1.EventList{} + events := &corev1.EventList{} err := k8sClient.List(ctx, events, client.InNamespace(serviceKey.Namespace)) if err != nil { return false diff --git a/pkg/controller/v1alpha1/inferencegraph/knative_reconciler.go b/pkg/controller/v1alpha1/inferencegraph/knative_reconciler.go index b61790bbd1b..b2ae4c3913a 100644 --- a/pkg/controller/v1alpha1/inferencegraph/knative_reconciler.go +++ b/pkg/controller/v1alpha1/inferencegraph/knative_reconciler.go @@ -19,16 +19,13 @@ package inferencegraph import ( "context" "encoding/json" - "fmt" "reflect" + "strconv" "strings" - v1alpha1api "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" - "github.com/kserve/kserve/pkg/constants" - "github.com/kserve/kserve/pkg/utils" "github.com/pkg/errors" "google.golang.org/protobuf/proto" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/equality" apierr "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/resource" @@ -42,6 +39,10 @@ import ( knservingv1 "knative.dev/serving/pkg/apis/serving/v1" "sigs.k8s.io/controller-runtime/pkg/client" logf "sigs.k8s.io/controller-runtime/pkg/log" + + "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" + "github.com/kserve/kserve/pkg/constants" + "github.com/kserve/kserve/pkg/utils" ) var log = logf.Log.WithName("GraphKsvcReconciler") @@ -54,7 +55,8 @@ type GraphKnativeServiceReconciler struct { func NewGraphKnativeServiceReconciler(client client.Client, scheme *runtime.Scheme, - ksvc *knservingv1.Service) *GraphKnativeServiceReconciler { + ksvc *knservingv1.Service, +) *GraphKnativeServiceReconciler { return &GraphKnativeServiceReconciler{ client: client, scheme: scheme, @@ -87,13 +89,13 @@ func reconcileKsvc(desired *knservingv1.Service, existing *knservingv1.Service) return nil } -func (r *GraphKnativeServiceReconciler) Reconcile() (*knservingv1.ServiceStatus, error) { +func (r *GraphKnativeServiceReconciler) Reconcile(ctx context.Context) (*knservingv1.ServiceStatus, error) { desired := r.Service existing := &knservingv1.Service{} err := retry.RetryOnConflict(retry.DefaultBackoff, func() error { log.Info("Updating inference graph knative service", "namespace", desired.Namespace, "name", desired.Name) - if err := r.client.Get(context.TODO(), types.NamespacedName{Name: desired.Name, Namespace: desired.Namespace}, existing); err != nil { + if err := r.client.Get(ctx, types.NamespacedName{Name: desired.Name, Namespace: desired.Namespace}, existing); err != nil { return err } @@ -106,7 +108,7 @@ func (r *GraphKnativeServiceReconciler) Reconcile() (*knservingv1.ServiceStatus, // Do a dry-run update to avoid diffs generated by default values introduced by knative's defaulter webhook. // This will populate our local knative service object with any default values // that are present on the remote version. - if err := r.client.Update(context.TODO(), desired, client.DryRunAll); err != nil { + if err := r.client.Update(ctx, desired, client.DryRunAll); err != nil { // log only if it is not resource conflict error to avoid spamming if !apierr.IsConflict(err) { log.Error(err, "Failed to perform dry-run update of knative service", "service", desired.Name) @@ -117,12 +119,12 @@ func (r *GraphKnativeServiceReconciler) Reconcile() (*knservingv1.ServiceStatus, if err := reconcileKsvc(desired, existing); err != nil { return err } - return r.client.Update(context.TODO(), existing) + return r.client.Update(ctx, existing) }) if err != nil { if apierr.IsNotFound(err) { log.Info("Creating inference graph knative service", "namespace", desired.Namespace, "name", desired.Name) - return &desired.Status, r.client.Create(context.TODO(), desired) + return &desired.Status, r.client.Create(ctx, desired) } return &existing.Status, errors.Wrapf(err, "fails to reconcile inference graph knative service") } @@ -135,7 +137,7 @@ func semanticEquals(desiredService, service *knservingv1.Service) bool { equality.Semantic.DeepEqual(desiredService.Spec.RouteSpec, service.Spec.RouteSpec) } -func createKnativeService(componentMeta metav1.ObjectMeta, graph *v1alpha1api.InferenceGraph, config *RouterConfig) *knservingv1.Service { +func createKnativeService(componentMeta metav1.ObjectMeta, graph *v1alpha1.InferenceGraph, config *RouterConfig) *knservingv1.Service { bytes, err := json.Marshal(graph.Spec) if err != nil { return nil @@ -144,17 +146,13 @@ func createKnativeService(componentMeta metav1.ObjectMeta, graph *v1alpha1api.In if annotations == nil { annotations = make(map[string]string) } - labels := componentMeta.GetLabels() - if labels == nil { - labels = make(map[string]string) //nolint:ineffassign, staticcheck - } // User can pass down scaling class annotation to overwrite the default scaling KPA if _, ok := annotations[autoscaling.ClassAnnotationKey]; !ok { annotations[autoscaling.ClassAnnotationKey] = autoscaling.KPA } if _, ok := annotations[autoscaling.MinScaleAnnotationKey]; !ok { - annotations[autoscaling.MinScaleAnnotationKey] = fmt.Sprint(constants.DefaultMinReplicas) + annotations[autoscaling.MinScaleAnnotationKey] = strconv.Itoa(int(constants.DefaultMinReplicas)) } // ksvc metadata.annotations @@ -165,7 +163,7 @@ func createKnativeService(componentMeta metav1.ObjectMeta, graph *v1alpha1api.In delete(annotations, constants.KnativeOpenshiftEnablePassthroughKey) } - labels = utils.Filter(componentMeta.Labels, func(key string) bool { + labels := utils.Filter(componentMeta.Labels, func(key string) bool { return !utils.Includes(constants.RevisionTemplateLabelDisallowedList, key) }) labels[constants.InferenceGraphLabel] = componentMeta.Name @@ -185,8 +183,8 @@ func createKnativeService(componentMeta metav1.ObjectMeta, graph *v1alpha1api.In }, Spec: knservingv1.RevisionSpec{ TimeoutSeconds: graph.Spec.TimeoutSeconds, - PodSpec: v1.PodSpec{ - Containers: []v1.Container{ + PodSpec: corev1.PodSpec{ + Containers: []corev1.Container{ { Image: config.Image, Args: []string{ @@ -194,15 +192,16 @@ func createKnativeService(componentMeta metav1.ObjectMeta, graph *v1alpha1api.In string(bytes), }, Resources: constructResourceRequirements(*graph, *config), - SecurityContext: &v1.SecurityContext{ + SecurityContext: &corev1.SecurityContext{ Privileged: proto.Bool(false), RunAsNonRoot: proto.Bool(true), ReadOnlyRootFilesystem: proto.Bool(true), AllowPrivilegeEscalation: proto.Bool(false), - Capabilities: &v1.Capabilities{ - Drop: []v1.Capability{v1.Capability("ALL")}, + Capabilities: &corev1.Capabilities{ + Drop: []corev1.Capability{corev1.Capability("ALL")}, }, }, + ReadinessProbe: constants.GetRouterReadinessProbe(), }, }, Affinity: graph.Spec.Affinity, @@ -217,7 +216,7 @@ func createKnativeService(componentMeta metav1.ObjectMeta, graph *v1alpha1api.In // Only adding this env variable "PROPAGATE_HEADERS" if router's headers config has the key "propagate" value, exists := config.Headers["propagate"] if exists { - service.Spec.ConfigurationSpec.Template.Spec.PodSpec.Containers[0].Env = []v1.EnvVar{ + service.Spec.ConfigurationSpec.Template.Spec.PodSpec.Containers[0].Env = []corev1.EnvVar{ { Name: constants.RouterHeadersPropagateEnvVar, Value: strings.Join(value, ","), @@ -227,23 +226,23 @@ func createKnativeService(componentMeta metav1.ObjectMeta, graph *v1alpha1api.In return service } -func constructResourceRequirements(graph v1alpha1api.InferenceGraph, config RouterConfig) v1.ResourceRequirements { - var specResources v1.ResourceRequirements +func constructResourceRequirements(graph v1alpha1.InferenceGraph, config RouterConfig) corev1.ResourceRequirements { + var specResources corev1.ResourceRequirements if !reflect.ValueOf(graph.Spec.Resources).IsZero() { log.Info("Ignoring defaults for ResourceRequirements as spec has resources mentioned", "specResources", graph.Spec.Resources) - specResources = v1.ResourceRequirements{ + specResources = corev1.ResourceRequirements{ Limits: graph.Spec.Resources.Limits, Requests: graph.Spec.Resources.Requests, } } else { - specResources = v1.ResourceRequirements{ - Limits: v1.ResourceList{ - v1.ResourceCPU: resource.MustParse(config.CpuLimit), - v1.ResourceMemory: resource.MustParse(config.MemoryLimit), + specResources = corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse(config.CpuLimit), + corev1.ResourceMemory: resource.MustParse(config.MemoryLimit), }, - Requests: v1.ResourceList{ - v1.ResourceCPU: resource.MustParse(config.CpuRequest), - v1.ResourceMemory: resource.MustParse(config.MemoryRequest), + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse(config.CpuRequest), + corev1.ResourceMemory: resource.MustParse(config.MemoryRequest), }, } } diff --git a/pkg/controller/v1alpha1/inferencegraph/raw_ig.go b/pkg/controller/v1alpha1/inferencegraph/raw_ig.go index 49321f0e536..97285f26ffa 100644 --- a/pkg/controller/v1alpha1/inferencegraph/raw_ig.go +++ b/pkg/controller/v1alpha1/inferencegraph/raw_ig.go @@ -17,13 +17,14 @@ limitations under the License. package inferencegraph import ( + "context" "encoding/json" "strings" "github.com/pkg/errors" "google.golang.org/protobuf/proto" appsv1 "k8s.io/api/apps/v1" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/kubernetes" @@ -33,7 +34,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" logf "sigs.k8s.io/controller-runtime/pkg/log" - v1alpha1api "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" + "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" "github.com/kserve/kserve/pkg/apis/serving/v1beta1" "github.com/kserve/kserve/pkg/constants" "github.com/kserve/kserve/pkg/controller/v1beta1/inferenceservice/reconcilers/raw" @@ -47,15 +48,15 @@ Also propagates headers onto podspec container environment variables. This function makes sense to be used in raw k8s deployment mode */ -func createInferenceGraphPodSpec(graph *v1alpha1api.InferenceGraph, config *RouterConfig) *v1.PodSpec { +func createInferenceGraphPodSpec(graph *v1alpha1.InferenceGraph, config *RouterConfig) *corev1.PodSpec { bytes, err := json.Marshal(graph.Spec) if err != nil { return nil } // Pod spec with 'router container with resource requirements' and 'affinity' as well - podSpec := &v1.PodSpec{ - Containers: []v1.Container{ + podSpec := &corev1.PodSpec{ + Containers: []corev1.Container{ { Name: graph.ObjectMeta.Name, Image: config.Image, @@ -63,14 +64,15 @@ func createInferenceGraphPodSpec(graph *v1alpha1api.InferenceGraph, config *Rout "--graph-json", string(bytes), }, - Resources: constructResourceRequirements(*graph, *config), - SecurityContext: &v1.SecurityContext{ + Resources: constructResourceRequirements(*graph, *config), + ReadinessProbe: constants.GetRouterReadinessProbe(), + SecurityContext: &corev1.SecurityContext{ Privileged: proto.Bool(false), RunAsNonRoot: proto.Bool(true), ReadOnlyRootFilesystem: proto.Bool(true), AllowPrivilegeEscalation: proto.Bool(false), - Capabilities: &v1.Capabilities{ - Drop: []v1.Capability{v1.Capability("ALL")}, + Capabilities: &corev1.Capabilities{ + Drop: []corev1.Capability{corev1.Capability("ALL")}, }, }, }, @@ -82,7 +84,7 @@ func createInferenceGraphPodSpec(graph *v1alpha1api.InferenceGraph, config *Rout // Only adding this env variable "PROPAGATE_HEADERS" if router's headers config has the key "propagate" value, exists := config.Headers["propagate"] if exists { - podSpec.Containers[0].Env = []v1.EnvVar{ + podSpec.Containers[0].Env = []corev1.EnvVar{ { Name: constants.RouterHeadersPropagateEnvVar, Value: strings.Join(value, ","), @@ -96,7 +98,7 @@ func createInferenceGraphPodSpec(graph *v1alpha1api.InferenceGraph, config *Rout /* A simple utility to create a basic meta object given name and namespace; Can be extended to accept labels, annotations as well */ -func constructForRawDeployment(graph *v1alpha1api.InferenceGraph) (metav1.ObjectMeta, v1beta1.ComponentExtensionSpec) { +func constructForRawDeployment(graph *v1alpha1.InferenceGraph) (metav1.ObjectMeta, v1beta1.ComponentExtensionSpec) { name := graph.ObjectMeta.Name namespace := graph.ObjectMeta.Namespace annotations := graph.ObjectMeta.Annotations @@ -137,18 +139,18 @@ Handles bulk of raw deployment logic for Inference graph controller 4. Set controller references 5. Finally reconcile */ -func handleInferenceGraphRawDeployment(cl client.Client, clientset kubernetes.Interface, scheme *runtime.Scheme, - graph *v1alpha1api.InferenceGraph, routerConfig *RouterConfig) (*appsv1.Deployment, *knapis.URL, error) { +func handleInferenceGraphRawDeployment(ctx context.Context, cl client.Client, clientset kubernetes.Interface, scheme *runtime.Scheme, + graph *v1alpha1.InferenceGraph, routerConfig *RouterConfig, +) (*appsv1.Deployment, *knapis.URL, error) { // create desired service object. desiredSvc := createInferenceGraphPodSpec(graph, routerConfig) objectMeta, componentExtSpec := constructForRawDeployment(graph) // create the reconciler - reconciler, err := raw.NewRawKubeReconciler(cl, clientset, scheme, objectMeta, metav1.ObjectMeta{}, &componentExtSpec, desiredSvc, nil) - + reconciler, err := raw.NewRawKubeReconciler(ctx, cl, clientset, scheme, objectMeta, metav1.ObjectMeta{}, &componentExtSpec, desiredSvc, nil) if err != nil { - return nil, reconciler.URL, errors.Wrapf(err, "fails to create NewRawKubeReconciler for inference graph") + return nil, nil, errors.Wrapf(err, "fails to create NewRawKubeReconciler for inference graph") } // set Deployment Controller for _, deployments := range reconciler.Deployment.DeploymentList { @@ -169,7 +171,7 @@ func handleInferenceGraphRawDeployment(cl client.Client, clientset kubernetes.In } // reconcile - deployment, err := reconciler.Reconcile() + deployment, err := reconciler.Reconcile(ctx) logger.Info("Result of inference graph raw reconcile", "deployment", deployment[0]) // only 1 deployment exist (default deployment) logger.Info("Result of reconcile", "err", err) @@ -184,8 +186,9 @@ func handleInferenceGraphRawDeployment(cl client.Client, clientset kubernetes.In PropagateRawStatus Propagates deployment status onto Inference graph status. In raw deployment mode, deployment available denotes the ready status for IG */ -func PropagateRawStatus(graphStatus *v1alpha1api.InferenceGraphStatus, deployment *appsv1.Deployment, - url *apis.URL) { +func PropagateRawStatus(graphStatus *v1alpha1.InferenceGraphStatus, deployment *appsv1.Deployment, + url *apis.URL, +) { for _, con := range deployment.Status.Conditions { if con.Type == appsv1.DeploymentAvailable { graphStatus.URL = url @@ -193,7 +196,7 @@ func PropagateRawStatus(graphStatus *v1alpha1api.InferenceGraphStatus, deploymen conditions := []apis.Condition{ { Type: apis.ConditionReady, - Status: v1.ConditionTrue, + Status: corev1.ConditionTrue, }, } graphStatus.SetConditions(conditions) diff --git a/pkg/controller/v1alpha1/inferencegraph/raw_ig_test.go b/pkg/controller/v1alpha1/inferencegraph/raw_ig_test.go index 0a26c740b86..3312a6c1213 100644 --- a/pkg/controller/v1alpha1/inferencegraph/raw_ig_test.go +++ b/pkg/controller/v1alpha1/inferencegraph/raw_ig_test.go @@ -20,16 +20,18 @@ import ( "testing" "github.com/google/go-cmp/cmp" - . "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" - "github.com/kserve/kserve/pkg/apis/serving/v1beta1" - "github.com/kserve/kserve/pkg/constants" "google.golang.org/protobuf/proto" appsv1 "k8s.io/api/apps/v1" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" "knative.dev/pkg/apis" duckv1 "knative.dev/pkg/apis/duck/v1" + + . "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" + "github.com/kserve/kserve/pkg/apis/serving/v1beta1" + "github.com/kserve/kserve/pkg/constants" ) func TestCreateInferenceGraphPodSpec(t *testing.T) { @@ -60,6 +62,8 @@ func TestCreateInferenceGraphPodSpec(t *testing.T) { }, } + expectedReadinessProbe := constants.GetRouterReadinessProbe() + testIGSpecs := map[string]*InferenceGraph{ "basic": { ObjectMeta: metav1.ObjectMeta{ @@ -103,14 +107,14 @@ func TestCreateInferenceGraphPodSpec(t *testing.T) { }, }, }, - Resources: v1.ResourceRequirements{ - Limits: v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("100m"), - v1.ResourceMemory: resource.MustParse("500Mi"), + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("500Mi"), }, - Requests: v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("100m"), - v1.ResourceMemory: resource.MustParse("100Mi"), + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("100Mi"), }, }, }, @@ -142,9 +146,9 @@ func TestCreateInferenceGraphPodSpec(t *testing.T) { }, } - expectedPodSpecs := map[string]*v1.PodSpec{ + expectedPodSpecs := map[string]*corev1.PodSpec{ "basicgraph": { - Containers: []v1.Container{ + Containers: []corev1.Container{ { Image: "kserve/router:v0.10.0", Name: "basic-ig", @@ -152,23 +156,24 @@ func TestCreateInferenceGraphPodSpec(t *testing.T) { "--graph-json", "{\"nodes\":{\"root\":{\"routerType\":\"Sequence\",\"steps\":[{\"serviceUrl\":\"http://someservice.exmaple.com\"}]}},\"resources\":{}}", }, - Resources: v1.ResourceRequirements{ - Limits: v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("100m"), - v1.ResourceMemory: resource.MustParse("500Mi"), + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("500Mi"), }, - Requests: v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("100m"), - v1.ResourceMemory: resource.MustParse("100Mi"), + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("100Mi"), }, }, - SecurityContext: &v1.SecurityContext{ + ReadinessProbe: expectedReadinessProbe, + SecurityContext: &corev1.SecurityContext{ Privileged: proto.Bool(false), RunAsNonRoot: proto.Bool(true), ReadOnlyRootFilesystem: proto.Bool(true), AllowPrivilegeEscalation: proto.Bool(false), - Capabilities: &v1.Capabilities{ - Drop: []v1.Capability{v1.Capability("ALL")}, + Capabilities: &corev1.Capabilities{ + Drop: []corev1.Capability{corev1.Capability("ALL")}, }, }, }, @@ -176,7 +181,7 @@ func TestCreateInferenceGraphPodSpec(t *testing.T) { AutomountServiceAccountToken: proto.Bool(false), }, "basicgraphwithheaders": { - Containers: []v1.Container{ + Containers: []corev1.Container{ { Image: "kserve/router:v0.10.0", Name: "basic-ig", @@ -184,29 +189,30 @@ func TestCreateInferenceGraphPodSpec(t *testing.T) { "--graph-json", "{\"nodes\":{\"root\":{\"routerType\":\"Sequence\",\"steps\":[{\"serviceUrl\":\"http://someservice.exmaple.com\"}]}},\"resources\":{}}", }, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "PROPAGATE_HEADERS", Value: "Authorization,Intuit_tid", }, }, - Resources: v1.ResourceRequirements{ - Limits: v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("100m"), - v1.ResourceMemory: resource.MustParse("500Mi"), + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("500Mi"), }, - Requests: v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("100m"), - v1.ResourceMemory: resource.MustParse("100Mi"), + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("100Mi"), }, }, - SecurityContext: &v1.SecurityContext{ + ReadinessProbe: expectedReadinessProbe, + SecurityContext: &corev1.SecurityContext{ Privileged: proto.Bool(false), RunAsNonRoot: proto.Bool(true), ReadOnlyRootFilesystem: proto.Bool(true), AllowPrivilegeEscalation: proto.Bool(false), - Capabilities: &v1.Capabilities{ - Drop: []v1.Capability{v1.Capability("ALL")}, + Capabilities: &corev1.Capabilities{ + Drop: []corev1.Capability{corev1.Capability("ALL")}, }, }, }, @@ -214,7 +220,7 @@ func TestCreateInferenceGraphPodSpec(t *testing.T) { AutomountServiceAccountToken: proto.Bool(false), }, "withresource": { - Containers: []v1.Container{ + Containers: []corev1.Container{ { Image: "kserve/router:v0.10.0", Name: "resource-ig", @@ -222,23 +228,24 @@ func TestCreateInferenceGraphPodSpec(t *testing.T) { "--graph-json", "{\"nodes\":{\"root\":{\"routerType\":\"Sequence\",\"steps\":[{\"serviceUrl\":\"http://someservice.exmaple.com\"}]}},\"resources\":{\"limits\":{\"cpu\":\"100m\",\"memory\":\"500Mi\"},\"requests\":{\"cpu\":\"100m\",\"memory\":\"100Mi\"}}}", }, - Resources: v1.ResourceRequirements{ - Limits: v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("100m"), - v1.ResourceMemory: resource.MustParse("500Mi"), + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("500Mi"), }, - Requests: v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("100m"), - v1.ResourceMemory: resource.MustParse("100Mi"), + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("100Mi"), }, }, - SecurityContext: &v1.SecurityContext{ + ReadinessProbe: expectedReadinessProbe, + SecurityContext: &corev1.SecurityContext{ Privileged: proto.Bool(false), RunAsNonRoot: proto.Bool(true), ReadOnlyRootFilesystem: proto.Bool(true), AllowPrivilegeEscalation: proto.Bool(false), - Capabilities: &v1.Capabilities{ - Drop: []v1.Capability{v1.Capability("ALL")}, + Capabilities: &corev1.Capabilities{ + Drop: []corev1.Capability{corev1.Capability("ALL")}, }, }, }, @@ -250,7 +257,7 @@ func TestCreateInferenceGraphPodSpec(t *testing.T) { scenarios := []struct { name string args args - expected *v1.PodSpec + expected *corev1.PodSpec }{ { name: "Basic Inference graph", @@ -342,7 +349,7 @@ func TestConstructGraphObjectMeta(t *testing.T) { }, }, Spec: InferenceGraphSpec{ - MinReplicas: v1beta1.GetIntReference(2), + MinReplicas: ptr.To(int32(2)), MaxReplicas: 5, }, }, @@ -361,7 +368,7 @@ func TestConstructGraphObjectMeta(t *testing.T) { componentExt: v1beta1.ComponentExtensionSpec{ MaxReplicas: 5, - MinReplicas: v1beta1.GetIntReference(2), + MinReplicas: ptr.To(int32(2)), ScaleMetric: nil, ScaleTarget: nil, }, @@ -413,9 +420,9 @@ func TestConstructGraphObjectMeta(t *testing.T) { }, }, Spec: InferenceGraphSpec{ - MinReplicas: v1beta1.GetIntReference(5), + MinReplicas: ptr.To(int32(5)), MaxReplicas: 10, - ScaleTarget: v1beta1.GetIntReference(50), + ScaleTarget: ptr.To(int32(50)), ScaleMetric: (*ScaleMetric)(&cpuResource), }, }, @@ -433,9 +440,9 @@ func TestConstructGraphObjectMeta(t *testing.T) { }, }, componentExt: v1beta1.ComponentExtensionSpec{ - MinReplicas: v1beta1.GetIntReference(5), + MinReplicas: ptr.To(int32(5)), MaxReplicas: 10, - ScaleTarget: v1beta1.GetIntReference(50), + ScaleTarget: ptr.To(int32(50)), ScaleMetric: &cpuResource, }, }, @@ -468,14 +475,14 @@ func TestPropagateRawStatus(t *testing.T) { expected *InferenceGraphStatus }{ { - name: "Basic Inference graph with with graph status as ready and deployment available", + name: "Basic Inference graph with graph status as ready and deployment available", args: args{ graphStatus: &InferenceGraphStatus{ Status: duckv1.Status{ Conditions: duckv1.Conditions{ { Type: apis.ConditionReady, - Status: v1.ConditionTrue, + Status: corev1.ConditionTrue, }, }, }, @@ -495,7 +502,7 @@ func TestPropagateRawStatus(t *testing.T) { Conditions: duckv1.Conditions{ { Type: apis.ConditionReady, - Status: v1.ConditionTrue, + Status: corev1.ConditionTrue, }, }, }, @@ -503,14 +510,14 @@ func TestPropagateRawStatus(t *testing.T) { }, { - name: "Basic Inference graph with with Inferencegraph status as not ready and deployment unavailable", + name: "Basic Inference graph with Inferencegraph status as not ready and deployment unavailable", args: args{ graphStatus: &InferenceGraphStatus{ Status: duckv1.Status{ Conditions: duckv1.Conditions{ { Type: apis.ConditionReady, - Status: v1.ConditionFalse, + Status: corev1.ConditionFalse, }, }, }, @@ -526,7 +533,7 @@ func TestPropagateRawStatus(t *testing.T) { Conditions: duckv1.Conditions{ { Type: apis.ConditionReady, - Status: v1.ConditionFalse, + Status: corev1.ConditionFalse, }, }, }, diff --git a/pkg/controller/v1alpha1/inferencegraph/suite_test.go b/pkg/controller/v1alpha1/inferencegraph/suite_test.go index 9e653c69e89..e2ea6414421 100644 --- a/pkg/controller/v1alpha1/inferencegraph/suite_test.go +++ b/pkg/controller/v1alpha1/inferencegraph/suite_test.go @@ -23,21 +23,18 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - v1 "k8s.io/api/core/v1" - netv1 "k8s.io/api/networking/v1" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/rest" - knservingv1 "knative.dev/serving/pkg/apis/serving/v1" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/envtest" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/log/zap" metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" - kfservingv1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" + "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" "github.com/kserve/kserve/pkg/apis/serving/v1beta1" "github.com/kserve/kserve/pkg/constants" pkgtest "github.com/kserve/kserve/pkg/testing" @@ -49,54 +46,44 @@ import ( var ( cfg *rest.Config k8sClient client.Client - testEnv *envtest.Environment - cancel context.CancelFunc - ctx context.Context - clientset kubernetes.Interface ) func TestAPIs(t *testing.T) { RegisterFailHandler(Fail) - RunSpecs(t, "inferencegraph Controller Suite") + RunSpecs(t, "InferenceGraph Controller Suite") } var _ = BeforeSuite(func() { logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) - ctx, cancel = context.WithCancel(context.TODO()) + ctx, cancel := context.WithCancel(context.Background()) By("bootstrapping test environment") crdDirectoryPaths := []string{ filepath.Join("..", "..", "..", "..", "test", "crds"), } - testEnv = pkgtest.SetupEnvTest(crdDirectoryPaths) - cfg, err := testEnv.Start() + testEnv := pkgtest.SetupEnvTest(crdDirectoryPaths) + var err error + cfg, err = testEnv.Start() Expect(err).ToNot(HaveOccurred()) Expect(cfg).ToNot(BeNil()) - err = kfservingv1alpha1.AddToScheme(scheme.Scheme) + DeferCleanup(func() { + By("canceling the context") + cancel() + + By("tearing down the test environment") + err := testEnv.Stop() + Expect(err).ToNot(HaveOccurred()) + }) + + err = v1alpha1.AddToScheme(scheme.Scheme) Expect(err).NotTo(HaveOccurred()) err = v1beta1.AddToScheme(scheme.Scheme) Expect(err).NotTo(HaveOccurred()) - err = knservingv1.AddToScheme(scheme.Scheme) - Expect(err).NotTo(HaveOccurred()) - err = netv1.AddToScheme(scheme.Scheme) - Expect(err).NotTo(HaveOccurred()) - k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) - Expect(err).ToNot(HaveOccurred()) - Expect(k8sClient).ToNot(BeNil()) - - clientset, err = kubernetes.NewForConfig(cfg) + clientset, err := kubernetes.NewForConfig(cfg) Expect(err).ToNot(HaveOccurred()) Expect(clientset).ToNot(BeNil()) - //Create namespace - kfservingNamespaceObj := &v1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: constants.KServeNamespace, - }, - } - Expect(k8sClient.Create(context.Background(), kfservingNamespaceObj)).Should(Succeed()) - k8sManager, err := ctrl.NewManager(cfg, ctrl.Options{ Scheme: scheme.Scheme, Metrics: metricsserver.Options{ @@ -105,6 +92,17 @@ var _ = BeforeSuite(func() { }) Expect(err).ToNot(HaveOccurred()) + k8sClient = k8sManager.GetClient() + Expect(k8sClient).ToNot(BeNil()) + + // Create namespace + kserveNamespaceObj := &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: constants.KServeNamespace, + }, + } + Expect(k8sClient.Create(context.Background(), kserveNamespaceObj)).Should(Succeed()) + deployConfig := &v1beta1.DeployConfig{DefaultDeploymentMode: "Serverless"} err = (&InferenceGraphReconciler{ @@ -116,20 +114,10 @@ var _ = BeforeSuite(func() { }).SetupWithManager(k8sManager, deployConfig) Expect(err).ToNot(HaveOccurred()) + // Start the manager in a separate goroutine go func() { defer GinkgoRecover() err = k8sManager.Start(ctx) Expect(err).ToNot(HaveOccurred()) }() - - k8sClient = k8sManager.GetClient() - Expect(k8sClient).ToNot(BeNil()) - -}) - -var _ = AfterSuite(func() { - cancel() - By("tearing down the test environment") - err := testEnv.Stop() - Expect(err).ToNot(HaveOccurred()) }) diff --git a/pkg/controller/v1alpha1/localmodel/controller.go b/pkg/controller/v1alpha1/localmodel/controller.go index a55feee6dcd..209e6e95003 100644 --- a/pkg/controller/v1alpha1/localmodel/controller.go +++ b/pkg/controller/v1alpha1/localmodel/controller.go @@ -29,21 +29,17 @@ package localmodel import ( "context" + "maps" "reflect" + "slices" "github.com/go-logr/logr" - "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" - v1alpha1api "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" - "github.com/kserve/kserve/pkg/apis/serving/v1beta1" - "github.com/kserve/kserve/pkg/constants" - "github.com/kserve/kserve/pkg/utils" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" apierr "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/kubernetes" - "k8s.io/component-helpers/scheduling/corev1" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" @@ -52,6 +48,12 @@ import ( "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/predicate" "sigs.k8s.io/controller-runtime/pkg/reconcile" + + "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" + "github.com/kserve/kserve/pkg/apis/serving/v1beta1" + "github.com/kserve/kserve/pkg/constants" + controllerutils "github.com/kserve/kserve/pkg/controller/v1alpha1/utils" + "github.com/kserve/kserve/pkg/utils" ) type LocalModelReconciler struct { @@ -64,13 +66,15 @@ type LocalModelReconciler struct { var ( ownerKey = ".metadata.controller" localModelKey = ".localmodel" - apiGVStr = v1alpha1api.SchemeGroupVersion.String() + apiGVStr = v1alpha1.SchemeGroupVersion.String() modelCacheCRName = "LocalModelCache" finalizerName = "localmodel.kserve.io/finalizer" ) // The localmodel is being deleted -func (c *LocalModelReconciler) deleteModelFromNodes(ctx context.Context, localModel *v1alpha1.LocalModelCache, nodeGroup *v1alpha1.LocalModelNodeGroup) (ctrl.Result, error) { +func (c *LocalModelReconciler) deleteModelFromNodes(ctx context.Context, localModel *v1alpha1.LocalModelCache, + nodeGroups map[string]*v1alpha1.LocalModelNodeGroup, +) (ctrl.Result, error) { // finalizer does not exists, nothing to do here! if !utils.Includes(localModel.ObjectMeta.Finalizers, finalizerName) { return ctrl.Result{}, nil @@ -78,28 +82,29 @@ func (c *LocalModelReconciler) deleteModelFromNodes(ctx context.Context, localMo c.Log.Info("deleting model", "name", localModel.Name) // Todo: Prevent deletion if there are isvcs using this localmodel - - readyNodes, notReadyNodes, err := getNodesFromNodeGroup(ctx, nodeGroup, c.Client) - if err != nil { - c.Log.Error(err, "getNodesFromNodeGroup node error") - return ctrl.Result{}, err - } - for _, node := range append(readyNodes.Items, notReadyNodes.Items...) { - localModelNode := &v1alpha1.LocalModelNode{} - err := c.Client.Get(ctx, types.NamespacedName{Name: node.Name}, localModelNode) + for _, nodeGroup := range nodeGroups { + readyNodes, notReadyNodes, err := getNodesFromNodeGroup(ctx, nodeGroup, c.Client) if err != nil { - if apierr.IsNotFound(err) { - c.Log.Info("localmodelNode not found", "node", node.Name) - continue - } else { - c.Log.Error(err, "Failed to get localmodelnode", "name", node.Name) - return ctrl.Result{}, err - } + c.Log.Error(err, "getNodesFromNodeGroup node error") + return ctrl.Result{}, err } + for _, node := range append(readyNodes.Items, notReadyNodes.Items...) { + localModelNode := &v1alpha1.LocalModelNode{} + err := c.Client.Get(ctx, types.NamespacedName{Name: node.Name}, localModelNode) + if err != nil { + if apierr.IsNotFound(err) { + c.Log.Info("localmodelNode not found", "node", node.Name) + continue + } else { + c.Log.Error(err, "Failed to get localmodelnode", "name", node.Name) + return ctrl.Result{}, err + } + } - if err := c.DeleteModelFromNode(ctx, localModelNode, localModel); err != nil { - c.Log.Error(err, "failed to delete model from localModelNode", "localModelNode", localModelNode.Name) - return ctrl.Result{}, err + if err := c.DeleteModelFromNode(ctx, localModelNode, localModel); err != nil { + c.Log.Error(err, "failed to delete model from localModelNode", "localModelNode", localModelNode.Name) + return ctrl.Result{}, err + } } } @@ -115,7 +120,7 @@ func (c *LocalModelReconciler) deleteModelFromNodes(ctx context.Context, localMo } // Creates a PV and set the localModel as its controller -func (c *LocalModelReconciler) createPV(ctx context.Context, spec v1.PersistentVolume, localModel *v1alpha1.LocalModelCache) error { +func (c *LocalModelReconciler) createPV(ctx context.Context, spec corev1.PersistentVolume, localModel *v1alpha1.LocalModelCache) error { persistentVolumes := c.Clientset.CoreV1().PersistentVolumes() if _, err := persistentVolumes.Get(ctx, spec.Name, metav1.GetOptions{}); err != nil { if !apierr.IsNotFound(err) { @@ -136,7 +141,7 @@ func (c *LocalModelReconciler) createPV(ctx context.Context, spec v1.PersistentV } // Creates a PVC and sets the localModel as its controller -func (c *LocalModelReconciler) createPVC(ctx context.Context, spec v1.PersistentVolumeClaim, namespace string, localModel *v1alpha1.LocalModelCache) error { +func (c *LocalModelReconciler) createPVC(ctx context.Context, spec corev1.PersistentVolumeClaim, namespace string, localModel *v1alpha1.LocalModelCache) error { persistentVolumeClaims := c.Clientset.CoreV1().PersistentVolumeClaims(namespace) if _, err := persistentVolumeClaims.Get(ctx, spec.Name, metav1.GetOptions{}); err != nil { if !apierr.IsNotFound(err) { @@ -157,32 +162,54 @@ func (c *LocalModelReconciler) createPVC(ctx context.Context, spec v1.Persistent } // ReconcileForIsvcs Get all isvcs with model cache enabled, create pvs and pvcs, remove pvs and pvcs in namespaces without isvcs. -func (c *LocalModelReconciler) ReconcileForIsvcs(ctx context.Context, localModel *v1alpha1api.LocalModelCache, nodeGroup *v1alpha1api.LocalModelNodeGroup, jobNamespace string) error { +func (c *LocalModelReconciler) ReconcileForIsvcs(ctx context.Context, localModel *v1alpha1.LocalModelCache, + localModelNodeGroups map[string]*v1alpha1.LocalModelNodeGroup, defaultNodeGroup *v1alpha1.LocalModelNodeGroup, jobNamespace string, +) error { isvcs := &v1beta1.InferenceServiceList{} if err := c.Client.List(ctx, isvcs, client.MatchingFields{localModelKey: localModel.Name}); err != nil { c.Log.Error(err, "List isvc error") return err } isvcNames := []v1alpha1.NamespacedName{} - // namespaces with isvcs deployed - namespaces := make(map[string]struct{}) + // namespaces with isvcs deployed and their node groups + namespaceToNodeGroups := make(map[string]map[string]*v1alpha1.LocalModelNodeGroup) for _, isvc := range isvcs.Items { isvcNames = append(isvcNames, v1alpha1.NamespacedName{Name: isvc.Name, Namespace: isvc.Namespace}) - namespaces[isvc.Namespace] = struct{}{} + // isvc has nodegroup annotation + if isvcNodeGroup, ok := isvc.ObjectMeta.Annotations[constants.NodeGroupAnnotationKey]; ok { + if nodeGroup, ok := localModelNodeGroups[isvcNodeGroup]; ok { + if _, ok := namespaceToNodeGroups[isvc.Namespace]; !ok { + namespaceToNodeGroups[isvc.Namespace] = map[string]*v1alpha1.LocalModelNodeGroup{} + } + namespaceToNodeGroups[isvc.Namespace][nodeGroup.Name] = nodeGroup + } else { + c.Log.Info("Didn't find isvc node group in model cache node groups", "isvc name", isvc.Name, "isvc node group", isvcNodeGroup, "model cache node groups", slices.Collect(maps.Keys(localModelNodeGroups))) + } + // isvc does not have nodegroup annotation. Use default nodegroup + } else if _, ok := namespaceToNodeGroups[isvc.Namespace]; !ok { + c.Log.Info("Isvc does not have node group annotation", "isvc name", isvc.Name, "nodegroup annotation", constants.NodeGroupAnnotationKey) + namespaceToNodeGroups[isvc.Namespace] = map[string]*v1alpha1.LocalModelNodeGroup{defaultNodeGroup.Name: defaultNodeGroup} + } else { + namespaceToNodeGroups[isvc.Namespace][defaultNodeGroup.Name] = defaultNodeGroup + } } localModel.Status.InferenceServices = isvcNames if err := c.Status().Update(ctx, localModel); err != nil { c.Log.Error(err, "cannot update status", "name", localModel.Name) } - // Remove PVs and PVCs if the namespace does not have isvcs - pvcs := v1.PersistentVolumeClaimList{} + /* + Remove PVs and PVCs if the namespace does not have isvcs + It only deletes the pvc and pvs with ownerReference as the localModel + And the pv must be of the format pvc.Name+"-"+pvc.Namespace + */ + pvcs := corev1.PersistentVolumeClaimList{} if err := c.List(ctx, &pvcs, client.MatchingFields{ownerKey: localModel.Name}); err != nil { c.Log.Error(err, "unable to list PVCs", "name", localModel.Name) return err } for _, pvc := range pvcs.Items { - if _, ok := namespaces[pvc.Namespace]; !ok { + if _, ok := namespaceToNodeGroups[pvc.Namespace]; !ok { if pvc.Namespace == jobNamespace { // Keep PVCs in modelCacheNamespace as they don't have a corresponding inference service continue @@ -200,57 +227,71 @@ func (c *LocalModelReconciler) ReconcileForIsvcs(ctx context.Context, localModel } } - for namespace := range namespaces { - // TODO: node group needs to be retrieved from isvc node group annotation when we support multiple node groups - pvcName := localModel.Name + "-" + localModel.Spec.NodeGroups[0] - pv := v1.PersistentVolume{ - ObjectMeta: metav1.ObjectMeta{ - Name: pvcName + "-" + namespace, - }, - Spec: nodeGroup.Spec.PersistentVolumeSpec, - } - if err := c.createPV(ctx, pv, localModel); err != nil { - c.Log.Error(err, "Create PV err", "name", pv.Name) - } + for namespace, nodeGroups := range namespaceToNodeGroups { + for nodeGroupName, nodeGroup := range nodeGroups { + pvcName := localModel.Name + "-" + nodeGroupName + pv := corev1.PersistentVolume{ + ObjectMeta: metav1.ObjectMeta{ + Name: pvcName + "-" + namespace, + }, + Spec: nodeGroup.Spec.PersistentVolumeSpec, + } + if err := c.createPV(ctx, pv, localModel); err != nil { + c.Log.Error(err, "Create PV err", "name", pv.Name) + } - pvc := v1.PersistentVolumeClaim{ - ObjectMeta: metav1.ObjectMeta{ - Name: pvcName, - Namespace: namespace, - }, - Spec: nodeGroup.Spec.PersistentVolumeClaimSpec, - } - pvc.Spec.VolumeName = pv.Name - if err := c.createPVC(ctx, pvc, namespace, localModel); err != nil { - c.Log.Error(err, "Create PVC err", "name", pvc.Name) + pvc := corev1.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{ + Name: pvcName, + Namespace: namespace, + }, + Spec: nodeGroup.Spec.PersistentVolumeClaimSpec, + } + pvc.Spec.VolumeName = pv.Name + if err := c.createPVC(ctx, pvc, namespace, localModel); err != nil { + c.Log.Error(err, "Create PVC err", "name", pvc.Name) + } } } return nil } +// Reconcile // Step 1 - Checks if the CR is in the deletion process. Deletion completes when all LocalModelNodes have been updated // Step 2 - Adds this model to LocalModelNode resources in the node group // Step 3 - Creates PV & PVC for model download // Step 4 - Creates PV & PVCs for namespaces with isvcs using this cached model func (c *LocalModelReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { c.Log.Info("Reconciling localmodel", "name", req.Name) - - localModelConfig, err := v1beta1.NewLocalModelConfig(c.Clientset) + isvcConfigMap, err := v1beta1.GetInferenceServiceConfigMap(ctx, c.Clientset) + if err != nil { + c.Log.Error(err, "unable to get configmap", "name", constants.InferenceServiceConfigMapName, "namespace", constants.KServeNamespace) + return reconcile.Result{}, err + } + localModelConfig, err := v1beta1.NewLocalModelConfig(isvcConfigMap) if err != nil { c.Log.Error(err, "Failed to get local model config") return reconcile.Result{}, err } - localModel := &v1alpha1api.LocalModelCache{} + localModel := &v1alpha1.LocalModelCache{} if err := c.Get(ctx, req.NamespacedName, localModel); err != nil { // Ignore not-found errors, we can get them on deleted requests. return reconcile.Result{}, client.IgnoreNotFound(err) } - - nodeGroup := &v1alpha1api.LocalModelNodeGroup{} - nodeGroupNamespacedName := types.NamespacedName{Name: localModel.Spec.NodeGroups[0]} - if err := c.Get(ctx, nodeGroupNamespacedName, nodeGroup); err != nil { - return reconcile.Result{}, err + // Get all node groups of the local model + defaultNodeGroup := &v1alpha1.LocalModelNodeGroup{} + nodeGroups := map[string]*v1alpha1.LocalModelNodeGroup{} + for idx, nodeGroupName := range localModel.Spec.NodeGroups { + nodeGroup := &v1alpha1.LocalModelNodeGroup{} + nodeGroupNamespacedName := types.NamespacedName{Name: nodeGroupName} + if err := c.Get(ctx, nodeGroupNamespacedName, nodeGroup); err != nil { + return reconcile.Result{}, err + } + nodeGroups[nodeGroupName] = nodeGroup + if idx == 0 { + defaultNodeGroup = nodeGroup + } } // Step 1 - Checks if the CR is in the deletion process @@ -261,42 +302,48 @@ func (c *LocalModelReconciler) Reconcile(ctx context.Context, req ctrl.Request) if !utils.Includes(localModel.ObjectMeta.Finalizers, finalizerName) { patch := client.MergeFrom(localModel.DeepCopy()) localModel.ObjectMeta.Finalizers = append(localModel.ObjectMeta.Finalizers, finalizerName) - if err := c.Patch(context.Background(), localModel, patch); err != nil { + if err := c.Patch(ctx, localModel, patch); err != nil { return ctrl.Result{}, err } } } else { - return c.deleteModelFromNodes(ctx, localModel, nodeGroup) + return c.deleteModelFromNodes(ctx, localModel, nodeGroups) } // Step 2 - Adds this model to LocalModelNode resources in the node group - if err := c.ReconcileLocalModelNode(ctx, localModel, nodeGroup); err != nil { + if err := c.ReconcileLocalModelNode(ctx, localModel, nodeGroups); err != nil { c.Log.Error(err, "failed to reconcile LocalModelNode") } // Step 3 - Creates PV & PVC for model download - pvSpec := nodeGroup.Spec.PersistentVolumeSpec - pv := v1.PersistentVolume{Spec: pvSpec, ObjectMeta: metav1.ObjectMeta{ - Name: localModel.Name + "-download", - }} - if err := c.createPV(ctx, pv, localModel); err != nil { - c.Log.Error(err, "Create PV err", "name", pv.Name) - } + for _, nodeGroup := range nodeGroups { + pvSpec := nodeGroup.Spec.PersistentVolumeSpec + pv := corev1.PersistentVolume{Spec: pvSpec, ObjectMeta: metav1.ObjectMeta{ + Name: localModel.Name + "-" + nodeGroup.Name + "-download", + }} + if err := c.createPV(ctx, pv, localModel); err != nil { + c.Log.Error(err, "Create PV err", "name", pv.Name) + } - pvc := v1.PersistentVolumeClaim{ - ObjectMeta: metav1.ObjectMeta{ - Name: localModel.Name, - }, - Spec: nodeGroup.Spec.PersistentVolumeClaimSpec, + pvc := corev1.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{ + Name: localModel.Name + "-" + nodeGroup.Name, + }, + Spec: nodeGroup.Spec.PersistentVolumeClaimSpec, + } + pvc.Spec.VolumeName = pv.Name + + if err := c.createPVC(ctx, pvc, localModelConfig.JobNamespace, localModel); err != nil { + c.Log.Error(err, "Create PVC err", "name", pv.Name) + } } - pvc.Spec.VolumeName = pv.Name - if err := c.createPVC(ctx, pvc, localModelConfig.JobNamespace, localModel); err != nil { - c.Log.Error(err, "Create PVC err", "name", pv.Name) + if localModelConfig.DisableVolumeManagement { + return ctrl.Result{}, nil } // Step 4 - Creates PV & PVCs for namespaces with isvcs using this model - err = c.ReconcileForIsvcs(ctx, localModel, nodeGroup, localModelConfig.JobNamespace) + err = c.ReconcileForIsvcs(ctx, localModel, nodeGroups, defaultNodeGroup, localModelConfig.JobNamespace) return ctrl.Result{}, err } @@ -311,7 +358,7 @@ func (c *LocalModelReconciler) isvcFunc(ctx context.Context, obj client.Object) if modelName, ok = isvc.Labels[constants.LocalModelLabel]; !ok { return []reconcile.Request{} } - localModel := &v1alpha1api.LocalModelCache{} + localModel := &v1alpha1.LocalModelCache{} if err := c.Get(ctx, types.NamespacedName{Name: modelName}, localModel); err != nil { c.Log.Error(err, "error getting localModel", "name", modelName) return []reconcile.Request{} @@ -322,27 +369,28 @@ func (c *LocalModelReconciler) isvcFunc(ctx context.Context, obj client.Object) return []reconcile.Request{{ NamespacedName: types.NamespacedName{ Name: modelName, - }}} + }, + }} } // Given a node object, checks if it matches any node group CR, then reconcile all local models that has this node group to create download jobs. func (c *LocalModelReconciler) nodeFunc(ctx context.Context, obj client.Object) []reconcile.Request { - node := obj.(*v1.Node) + node := obj.(*corev1.Node) requests := []reconcile.Request{} models := &v1alpha1.LocalModelCacheList{} - if err := c.Client.List(context.TODO(), models); err != nil { + if err := c.Client.List(ctx, models); err != nil { c.Log.Error(err, "list models error when reconciling nodes") return []reconcile.Request{} } for _, model := range models.Items { - nodeGroup := &v1alpha1api.LocalModelNodeGroup{} + nodeGroup := &v1alpha1.LocalModelNodeGroup{} nodeGroupNamespacedName := types.NamespacedName{Name: model.Spec.NodeGroups[0]} if err := c.Get(ctx, nodeGroupNamespacedName, nodeGroup); err != nil { c.Log.Info("get nodegroup failed", "name", model.Spec.NodeGroups[0]) continue } - matches, err := checkNodeAffinity(&nodeGroup.Spec.PersistentVolumeSpec, *node) + matches, err := controllerutils.CheckNodeAffinity(&nodeGroup.Spec.PersistentVolumeSpec, *node) if err != nil { c.Log.Error(err, "checkNodeAffinity error", "node", node.Name) } @@ -351,7 +399,8 @@ func (c *LocalModelReconciler) nodeFunc(ctx context.Context, obj client.Object) requests = append(requests, reconcile.Request{ NamespacedName: types.NamespacedName{ Name: model.Name, - }}) + }, + }) } } return requests @@ -365,14 +414,25 @@ func (c *LocalModelReconciler) localmodelNodeFunc(ctx context.Context, obj clien requests = append(requests, reconcile.Request{ NamespacedName: types.NamespacedName{ Name: modelInfo.ModelName, - }}) + }, + }) } return requests } func (c *LocalModelReconciler) SetupWithManager(mgr ctrl.Manager) error { - if err := mgr.GetFieldIndexer().IndexField(context.Background(), &v1.PersistentVolumeClaim{}, ownerKey, func(rawObj client.Object) []string { - pvc := rawObj.(*v1.PersistentVolumeClaim) + isvcConfigMap, err := v1beta1.GetInferenceServiceConfigMap(context.Background(), c.Clientset) + if err != nil { + c.Log.Error(err, "unable to get configmap", "name", constants.InferenceServiceConfigMapName, "namespace", constants.KServeNamespace) + return err + } + localModelConfig, err := v1beta1.NewLocalModelConfig(isvcConfigMap) + if err != nil { + c.Log.Error(err, "Failed to get local model config during controller manager setup") + return err + } + if err := mgr.GetFieldIndexer().IndexField(context.Background(), &corev1.PersistentVolumeClaim{}, ownerKey, func(rawObj client.Object) []string { + pvc := rawObj.(*corev1.PersistentVolumeClaim) owner := metav1.GetControllerOf(pvc) if owner == nil { return nil @@ -418,8 +478,8 @@ func (c *LocalModelReconciler) SetupWithManager(mgr ctrl.Manager) error { UpdateFunc: func(e event.UpdateEvent) bool { // Only reconciles the local model crs when the node becomes ready from not ready // Todo: add tests - oldNode := e.ObjectNew.(*v1.Node) - newNode := e.ObjectNew.(*v1.Node) + oldNode := e.ObjectNew.(*corev1.Node) + newNode := e.ObjectNew.(*corev1.Node) return !isNodeReady(*oldNode) && isNodeReady(*newNode) }, CreateFunc: func(e event.CreateEvent) bool { @@ -452,53 +512,42 @@ func (c *LocalModelReconciler) SetupWithManager(mgr ctrl.Manager) error { }, } - return ctrl.NewControllerManagedBy(mgr). - For(&v1alpha1api.LocalModelCache{}). + controllerBuilder := ctrl.NewControllerManagedBy(mgr). + For(&v1alpha1.LocalModelCache{}). // Ownes PersistentVolumes and PersistentVolumeClaims that is created by this local model controller - Owns(&v1.PersistentVolume{}). - Owns(&v1.PersistentVolumeClaim{}). - // Creates or deletes pv/pvcs when isvcs got created or deleted - Watches(&v1beta1.InferenceService{}, handler.EnqueueRequestsFromMapFunc(c.isvcFunc), builder.WithPredicates(isvcPredicates)). - // Downloads models to new nodes - Watches(&v1.Node{}, handler.EnqueueRequestsFromMapFunc(c.nodeFunc), builder.WithPredicates(nodePredicates)). + Owns(&corev1.PersistentVolume{}). + Owns(&corev1.PersistentVolumeClaim{}) + + if !localModelConfig.DisableVolumeManagement { + controllerBuilder.Watches(&v1beta1.InferenceService{}, handler.EnqueueRequestsFromMapFunc(c.isvcFunc), builder.WithPredicates(isvcPredicates)) + } + + return controllerBuilder. + Watches(&corev1.Node{}, handler.EnqueueRequestsFromMapFunc(c.nodeFunc), builder.WithPredicates(nodePredicates)). // Updates model status when localmodelnode status changes Watches(&v1alpha1.LocalModelNode{}, handler.EnqueueRequestsFromMapFunc(c.localmodelNodeFunc), builder.WithPredicates(localModelNodePredicates)). Complete(c) } -func isNodeReady(node v1.Node) bool { +func isNodeReady(node corev1.Node) bool { for _, condition := range node.Status.Conditions { - if condition.Type == v1.NodeReady && condition.Status == v1.ConditionTrue { + if condition.Type == corev1.NodeReady && condition.Status == corev1.ConditionTrue { return true } } return false } -// Returns true if the node matches the node affinity specified in the PV Spec -func checkNodeAffinity(pvSpec *v1.PersistentVolumeSpec, node v1.Node) (bool, error) { - if pvSpec.NodeAffinity == nil || pvSpec.NodeAffinity.Required == nil { - return false, nil - } - - terms := pvSpec.NodeAffinity.Required - if matches, err := corev1.MatchNodeSelectorTerms(&node, terms); err != nil { - return matches, nil - } else { - return matches, err - } -} - // Returns a list of ready nodes, and not ready nodes that matches the node selector in the node group -func getNodesFromNodeGroup(ctx context.Context, nodeGroup *v1alpha1.LocalModelNodeGroup, c client.Client) (*v1.NodeList, *v1.NodeList, error) { - nodes := &v1.NodeList{} - readyNodes := &v1.NodeList{} - notReadyNodes := &v1.NodeList{} +func getNodesFromNodeGroup(ctx context.Context, nodeGroup *v1alpha1.LocalModelNodeGroup, c client.Client) (*corev1.NodeList, *corev1.NodeList, error) { + nodes := &corev1.NodeList{} + readyNodes := &corev1.NodeList{} + notReadyNodes := &corev1.NodeList{} if err := c.List(ctx, nodes); err != nil { return nil, nil, err } for _, node := range nodes.Items { - matches, err := checkNodeAffinity(&nodeGroup.Spec.PersistentVolumeSpec, node) + matches, err := controllerutils.CheckNodeAffinity(&nodeGroup.Spec.PersistentVolumeSpec, node) if err != nil { return nil, nil, err } @@ -514,7 +563,7 @@ func getNodesFromNodeGroup(ctx context.Context, nodeGroup *v1alpha1.LocalModelNo } // DeleteModelFromNode deletes the source model from the localmodelnode -func (c *LocalModelReconciler) DeleteModelFromNode(ctx context.Context, localmodelNode *v1alpha1.LocalModelNode, localModel *v1alpha1api.LocalModelCache) error { +func (c *LocalModelReconciler) DeleteModelFromNode(ctx context.Context, localmodelNode *v1alpha1.LocalModelNode, localModel *v1alpha1.LocalModelCache) error { var patch client.Patch for i, modelInfo := range localmodelNode.Spec.LocalModels { if modelInfo.ModelName == localModel.Name { @@ -531,7 +580,7 @@ func (c *LocalModelReconciler) DeleteModelFromNode(ctx context.Context, localmod } // UpdateLocalModelNode updates the source model uri of the localmodelnode from the localmodel -func (c *LocalModelReconciler) UpdateLocalModelNode(ctx context.Context, localmodelNode *v1alpha1.LocalModelNode, localModel *v1alpha1api.LocalModelCache) error { +func (c *LocalModelReconciler) UpdateLocalModelNode(ctx context.Context, localmodelNode *v1alpha1.LocalModelNode, localModel *v1alpha1.LocalModelCache) error { var patch client.Patch updated := false for i, modelInfo := range localmodelNode.Spec.LocalModels { @@ -549,7 +598,7 @@ func (c *LocalModelReconciler) UpdateLocalModelNode(ctx context.Context, localmo } if !updated { patch = client.MergeFrom(localmodelNode.DeepCopy()) - localmodelNode.Spec.LocalModels = append(localmodelNode.Spec.LocalModels, v1alpha1api.LocalModelInfo{ModelName: localModel.Name, SourceModelUri: localModel.Spec.SourceModelUri}) + localmodelNode.Spec.LocalModels = append(localmodelNode.Spec.LocalModels, v1alpha1.LocalModelInfo{ModelName: localModel.Name, SourceModelUri: localModel.Spec.SourceModelUri}) } if err := c.Client.Patch(ctx, localmodelNode, patch); err != nil { c.Log.Error(err, "Update localmodelnode", "name", localmodelNode.Name) @@ -558,81 +607,83 @@ func (c *LocalModelReconciler) UpdateLocalModelNode(ctx context.Context, localmo return nil } -func nodeStatusFromLocalModelStatus(modelStatus v1alpha1.ModelStatus) v1alpha1api.NodeStatus { +func nodeStatusFromLocalModelStatus(modelStatus v1alpha1.ModelStatus) v1alpha1.NodeStatus { switch modelStatus { - case v1alpha1api.ModelDownloadPending: - return v1alpha1api.NodeDownloadPending - case v1alpha1api.ModelDownloading: - return v1alpha1api.NodeDownloading - case v1alpha1api.ModelDownloadError: - return v1alpha1api.NodeDownloadError - case v1alpha1api.ModelDownloaded: - return v1alpha1api.NodeDownloaded - } - return v1alpha1api.NodeDownloadPending + case v1alpha1.ModelDownloadPending: + return v1alpha1.NodeDownloadPending + case v1alpha1.ModelDownloading: + return v1alpha1.NodeDownloading + case v1alpha1.ModelDownloadError: + return v1alpha1.NodeDownloadError + case v1alpha1.ModelDownloaded: + return v1alpha1.NodeDownloaded + } + return v1alpha1.NodeDownloadPending } // ReconcileLocalModelNode creates updates localmodelnode for each node in the node group. It adds and removes localmodels from the localmodelnode and updates the status on the localmodel from the localmodelnode. -func (c *LocalModelReconciler) ReconcileLocalModelNode(ctx context.Context, localModel *v1alpha1api.LocalModelCache, nodeGroup *v1alpha1api.LocalModelNodeGroup) error { - readyNodes, notReadyNodes, err := getNodesFromNodeGroup(ctx, nodeGroup, c.Client) - if err != nil { - c.Log.Error(err, "getNodesFromNodeGroup node error") - return err - } - if localModel.Status.NodeStatus == nil { - localModel.Status.NodeStatus = make(map[string]v1alpha1api.NodeStatus) - } - for _, node := range notReadyNodes.Items { - if _, ok := localModel.Status.NodeStatus[node.Name]; !ok { - localModel.Status.NodeStatus[node.Name] = v1alpha1api.NodeNotReady - } - } - for _, node := range readyNodes.Items { - localModelNode := &v1alpha1.LocalModelNode{} - err := c.Client.Get(ctx, types.NamespacedName{Name: node.Name}, localModelNode) - found := true +func (c *LocalModelReconciler) ReconcileLocalModelNode(ctx context.Context, localModel *v1alpha1.LocalModelCache, nodeGroups map[string]*v1alpha1.LocalModelNodeGroup) error { + for _, nodeGroup := range nodeGroups { + readyNodes, notReadyNodes, err := getNodesFromNodeGroup(ctx, nodeGroup, c.Client) if err != nil { - if apierr.IsNotFound(err) { - found = false - c.Log.Info("localmodelNode not found") - } else { - c.Log.Error(err, "Failed to get localmodelnode", "name", node.Name) - return err - } + c.Log.Error(err, "getNodesFromNodeGroup node error") + return err } - if !found { - localModelNode = &v1alpha1.LocalModelNode{ - ObjectMeta: metav1.ObjectMeta{ - Name: node.Name, - }, - Spec: v1alpha1api.LocalModelNodeSpec{LocalModels: []v1alpha1api.LocalModelInfo{{ModelName: localModel.Name, SourceModelUri: localModel.Spec.SourceModelUri}}}, + if localModel.Status.NodeStatus == nil { + localModel.Status.NodeStatus = make(map[string]v1alpha1.NodeStatus) + } + for _, node := range notReadyNodes.Items { + if _, ok := localModel.Status.NodeStatus[node.Name]; !ok { + localModel.Status.NodeStatus[node.Name] = v1alpha1.NodeNotReady } - if err := c.Client.Create(ctx, localModelNode); err != nil { - c.Log.Error(err, "Create localmodelnode", "name", node.Name) - return err + } + for _, node := range readyNodes.Items { + localModelNode := &v1alpha1.LocalModelNode{} + err := c.Client.Get(ctx, types.NamespacedName{Name: node.Name}, localModelNode) + found := true + if err != nil { + if apierr.IsNotFound(err) { + found = false + c.Log.Info("localmodelNode not found") + } else { + c.Log.Error(err, "Failed to get localmodelnode", "name", node.Name) + return err + } } - } else { - if err := c.UpdateLocalModelNode(ctx, localModelNode, localModel); err != nil { - return err + if !found { + localModelNode = &v1alpha1.LocalModelNode{ + ObjectMeta: metav1.ObjectMeta{ + Name: node.Name, + }, + Spec: v1alpha1.LocalModelNodeSpec{LocalModels: []v1alpha1.LocalModelInfo{{ModelName: localModel.Name, SourceModelUri: localModel.Spec.SourceModelUri}}}, + } + if err := c.Client.Create(ctx, localModelNode); err != nil { + c.Log.Error(err, "Create localmodelnode", "name", node.Name) + return err + } + } else { + if err := c.UpdateLocalModelNode(ctx, localModelNode, localModel); err != nil { + return err + } } + modelStatus := localModelNode.Status.ModelStatus[localModel.Name] + localModel.Status.NodeStatus[node.Name] = nodeStatusFromLocalModelStatus(modelStatus) } - modelStatus := localModelNode.Status.ModelStatus[localModel.Name] - localModel.Status.NodeStatus[node.Name] = nodeStatusFromLocalModelStatus(modelStatus) - } - successfulNodes := 0 - failedNodes := 0 - for _, status := range localModel.Status.NodeStatus { - switch status { - case v1alpha1api.NodeDownloaded: - successfulNodes += 1 - case v1alpha1api.NodeDownloadError: - failedNodes += 1 + successfulNodes := 0 + failedNodes := 0 + for _, status := range localModel.Status.NodeStatus { + switch status { + case v1alpha1.NodeDownloaded: + successfulNodes += 1 + case v1alpha1.NodeDownloadError: + failedNodes += 1 + } + } + localModel.Status.ModelCopies = &v1alpha1.ModelCopies{Total: len(localModel.Status.NodeStatus), Available: successfulNodes, Failed: failedNodes} + if err := c.Status().Update(ctx, localModel); err != nil { + c.Log.Error(err, "cannot update model status from node", "name", localModel.Name) } - } - localModel.Status.ModelCopies = &v1alpha1.ModelCopies{Total: len(localModel.Status.NodeStatus), Available: successfulNodes, Failed: failedNodes} - if err := c.Status().Update(ctx, localModel); err != nil { - c.Log.Error(err, "cannot update model status from node", "name", localModel.Name) } return nil } diff --git a/pkg/controller/v1alpha1/localmodel/controller_test.go b/pkg/controller/v1alpha1/localmodel/controller_test.go index a798fe457ff..5eae04d0bec 100644 --- a/pkg/controller/v1alpha1/localmodel/controller_test.go +++ b/pkg/controller/v1alpha1/localmodel/controller_test.go @@ -20,16 +20,23 @@ import ( "context" "time" - "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" - "github.com/kserve/kserve/pkg/apis/serving/v1beta1" - "github.com/kserve/kserve/pkg/constants" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" "k8s.io/utils/ptr" + ctrl "sigs.k8s.io/controller-runtime" + crconfig "sigs.k8s.io/controller-runtime/pkg/config" + metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" + + "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" + "github.com/kserve/kserve/pkg/apis/serving/v1beta1" + "github.com/kserve/kserve/pkg/constants" ) var _ = Describe("CachedModel controller", func() { @@ -44,43 +51,43 @@ var _ = Describe("CachedModel controller", func() { localModelSpec = v1alpha1.LocalModelCacheSpec{ SourceModelUri: sourceModelUri, ModelSize: resource.MustParse("123Gi"), - NodeGroups: []string{"gpu"}, + NodeGroups: []string{"gpu1", "gpu2"}, } clusterStorageContainerSpec = v1alpha1.StorageContainerSpec{ SupportedUriFormats: []v1alpha1.SupportedUriFormat{{Prefix: "s3://"}}, - Container: v1.Container{ + Container: corev1.Container{ Name: "name", Image: "image", Args: []string{ "srcURI", constants.DefaultModelLocalMountPath, }, - TerminationMessagePolicy: v1.TerminationMessageFallbackToLogsOnError, - VolumeMounts: []v1.VolumeMount{}, + TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, + VolumeMounts: []corev1.VolumeMount{}, }, } - localModelNodeGroupSpec = v1alpha1.LocalModelNodeGroupSpec{ - PersistentVolumeSpec: v1.PersistentVolumeSpec{ - AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce}, - VolumeMode: ptr.To(v1.PersistentVolumeFilesystem), - Capacity: v1.ResourceList{v1.ResourceStorage: resource.MustParse("2Gi")}, + localModelNodeGroupSpec1 = v1alpha1.LocalModelNodeGroupSpec{ + PersistentVolumeSpec: corev1.PersistentVolumeSpec{ + AccessModes: []corev1.PersistentVolumeAccessMode{corev1.ReadWriteOnce}, + VolumeMode: ptr.To(corev1.PersistentVolumeFilesystem), + Capacity: corev1.ResourceList{corev1.ResourceStorage: resource.MustParse("2Gi")}, StorageClassName: "standard", - PersistentVolumeReclaimPolicy: v1.PersistentVolumeReclaimDelete, - PersistentVolumeSource: v1.PersistentVolumeSource{ - HostPath: &v1.HostPathVolumeSource{ + PersistentVolumeReclaimPolicy: corev1.PersistentVolumeReclaimDelete, + PersistentVolumeSource: corev1.PersistentVolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ Path: "/models", - Type: ptr.To(v1.HostPathDirectory), + Type: ptr.To(corev1.HostPathDirectory), }, }, - NodeAffinity: &v1.VolumeNodeAffinity{ - Required: &v1.NodeSelector{ - NodeSelectorTerms: []v1.NodeSelectorTerm{ + NodeAffinity: &corev1.VolumeNodeAffinity{ + Required: &corev1.NodeSelector{ + NodeSelectorTerms: []corev1.NodeSelectorTerm{ { - MatchExpressions: []v1.NodeSelectorRequirement{ + MatchExpressions: []corev1.NodeSelectorRequirement{ { Key: "node.kubernetes.io/instance-type", - Operator: v1.NodeSelectorOpIn, - Values: []string{"gpu"}, + Operator: corev1.NodeSelectorOpIn, + Values: []string{"gpu1"}, }, }, }, @@ -88,9 +95,43 @@ var _ = Describe("CachedModel controller", func() { }, }, }, - PersistentVolumeClaimSpec: v1.PersistentVolumeClaimSpec{ - AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce}, - Resources: v1.VolumeResourceRequirements{Requests: v1.ResourceList{v1.ResourceStorage: resource.MustParse("2Gi")}}, + PersistentVolumeClaimSpec: corev1.PersistentVolumeClaimSpec{ + AccessModes: []corev1.PersistentVolumeAccessMode{corev1.ReadWriteOnce}, + Resources: corev1.VolumeResourceRequirements{Requests: corev1.ResourceList{corev1.ResourceStorage: resource.MustParse("2Gi")}}, + }, + } + localModelNodeGroupSpec2 = v1alpha1.LocalModelNodeGroupSpec{ + PersistentVolumeSpec: corev1.PersistentVolumeSpec{ + AccessModes: []corev1.PersistentVolumeAccessMode{corev1.ReadWriteOnce}, + VolumeMode: ptr.To(corev1.PersistentVolumeFilesystem), + Capacity: corev1.ResourceList{corev1.ResourceStorage: resource.MustParse("2Gi")}, + StorageClassName: "standard", + PersistentVolumeReclaimPolicy: corev1.PersistentVolumeReclaimDelete, + PersistentVolumeSource: corev1.PersistentVolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: "/models", + Type: ptr.To(corev1.HostPathDirectory), + }, + }, + NodeAffinity: &corev1.VolumeNodeAffinity{ + Required: &corev1.NodeSelector{ + NodeSelectorTerms: []corev1.NodeSelectorTerm{ + { + MatchExpressions: []corev1.NodeSelectorRequirement{ + { + Key: "node.kubernetes.io/instance-type", + Operator: corev1.NodeSelectorOpIn, + Values: []string{"gpu2"}, + }, + }, + }, + }, + }, + }, + }, + PersistentVolumeClaimSpec: corev1.PersistentVolumeClaimSpec{ + AccessModes: []corev1.PersistentVolumeAccessMode{corev1.ReadWriteOnce}, + Resources: corev1.VolumeResourceRequirements{Requests: corev1.ResourceList{corev1.ResourceStorage: resource.MustParse("2Gi")}}, }, } configs = map[string]string{ @@ -100,35 +141,49 @@ var _ = Describe("CachedModel controller", func() { }`, } ) + Context("When creating a local model", func() { + var ( + configMap *corev1.ConfigMap + clusterStorageContainer *v1alpha1.ClusterStorageContainer + ) + BeforeEach(func() { + ctx, cancel := context.WithCancel(context.Background()) + configMap, clusterStorageContainer = genericSetup(ctx, configs, clusterStorageContainerSpec) + initializeManager(ctx, cfg) + DeferCleanup(func() { + k8sClient.Delete(ctx, clusterStorageContainer) + k8sClient.Delete(ctx, configMap) + + By("canceling the context") + cancel() + }) + }) + It("Should create pv, pvc, localmodelnode, and update status from localmodelnode", func() { - var configMap = &v1.ConfigMap{ + defer GinkgoRecover() + ctx, cancel := context.WithCancel(context.Background()) + DeferCleanup(cancel) + configMap := &corev1.ConfigMap{} + err := k8sClient.Get(ctx, types.NamespacedName{Name: constants.InferenceServiceConfigMapName, Namespace: constants.KServeNamespace}, configMap) + Expect(err).ToNot(HaveOccurred(), "InferenceService ConfigMap should exist") + + nodeGroup1 := &v1alpha1.LocalModelNodeGroup{ ObjectMeta: metav1.ObjectMeta{ - Name: constants.InferenceServiceConfigMapName, - Namespace: constants.KServeNamespace, + Name: "gpu1", }, - Data: configs, + Spec: localModelNodeGroupSpec1, } - Expect(k8sClient.Create(context.TODO(), configMap)).NotTo(HaveOccurred()) - defer k8sClient.Delete(context.TODO(), configMap) - - clusterStorageContainer := &v1alpha1.ClusterStorageContainer{ + nodeGroup2 := &v1alpha1.LocalModelNodeGroup{ ObjectMeta: metav1.ObjectMeta{ - Name: "test", + Name: "gpu2", }, - Spec: clusterStorageContainerSpec, + Spec: localModelNodeGroupSpec2, } - Expect(k8sClient.Create(ctx, clusterStorageContainer)).Should(Succeed()) - defer k8sClient.Delete(ctx, clusterStorageContainer) - - nodeGroup := &v1alpha1.LocalModelNodeGroup{ - ObjectMeta: metav1.ObjectMeta{ - Name: "gpu", - }, - Spec: localModelNodeGroupSpec, - } - Expect(k8sClient.Create(ctx, nodeGroup)).Should(Succeed()) - defer k8sClient.Delete(ctx, nodeGroup) + Expect(k8sClient.Create(ctx, nodeGroup1)).Should(Succeed()) + defer k8sClient.Delete(ctx, nodeGroup1) + Expect(k8sClient.Create(ctx, nodeGroup2)).Should(Succeed()) + defer k8sClient.Delete(ctx, nodeGroup2) cachedModel := &v1alpha1.LocalModelCache{ ObjectMeta: metav1.ObjectMeta{ @@ -139,8 +194,10 @@ var _ = Describe("CachedModel controller", func() { Expect(k8sClient.Create(ctx, cachedModel)).Should(Succeed()) modelLookupKey := types.NamespacedName{Name: "iris"} - pvLookupKey := types.NamespacedName{Name: "iris-download"} - pvcLookupKey := types.NamespacedName{Name: "iris", Namespace: modelCacheNamespace} + pvLookupKey1 := types.NamespacedName{Name: "iris-gpu1-download"} + pvcLookupKey1 := types.NamespacedName{Name: "iris-gpu1", Namespace: modelCacheNamespace} + pvLookupKey2 := types.NamespacedName{Name: "iris-gpu2-download"} + pvcLookupKey2 := types.NamespacedName{Name: "iris-gpu2", Namespace: modelCacheNamespace} Eventually(func() bool { err := k8sClient.Get(ctx, modelLookupKey, cachedModel) return err == nil @@ -150,59 +207,99 @@ var _ = Describe("CachedModel controller", func() { return err == nil && cachedModel.Status.ModelCopies != nil }, timeout, interval).Should(BeTrue()) - persistentVolume := &v1.PersistentVolume{} + persistentVolume1 := &corev1.PersistentVolume{} Eventually(func() bool { - err := k8sClient.Get(ctx, pvLookupKey, persistentVolume) - return err == nil && persistentVolume != nil + err := k8sClient.Get(ctx, pvLookupKey1, persistentVolume1) + return err == nil && persistentVolume1 != nil + }, timeout, interval).Should(BeTrue()) + persistentVolume2 := &corev1.PersistentVolume{} + Eventually(func() bool { + err := k8sClient.Get(ctx, pvLookupKey2, persistentVolume2) + return err == nil && persistentVolume2 != nil }, timeout, interval).Should(BeTrue()) - persistentVolumeClaim := &v1.PersistentVolumeClaim{} + persistentVolumeClaim1 := &corev1.PersistentVolumeClaim{} Eventually(func() bool { - err := k8sClient.Get(ctx, pvcLookupKey, persistentVolumeClaim) - return err == nil && persistentVolumeClaim != nil + err := k8sClient.Get(ctx, pvcLookupKey1, persistentVolumeClaim1) + return err == nil && persistentVolumeClaim1 != nil + }, timeout, interval).Should(BeTrue()) + persistentVolumeClaim2 := &corev1.PersistentVolumeClaim{} + Eventually(func() bool { + err := k8sClient.Get(ctx, pvcLookupKey2, persistentVolumeClaim2) + return err == nil && persistentVolumeClaim2 != nil }, timeout, interval).Should(BeTrue()) - nodeName := "node-1" - node1 := &v1.Node{ + nodeName1 := "node-1" + nodeName2 := "node-2" + node1 := &corev1.Node{ ObjectMeta: metav1.ObjectMeta{ - Name: nodeName, + Name: nodeName1, Labels: map[string]string{ - "node.kubernetes.io/instance-type": "gpu", + "node.kubernetes.io/instance-type": "gpu1", }, }, - Status: v1.NodeStatus{ - Conditions: []v1.NodeCondition{ + Status: corev1.NodeStatus{ + Conditions: []corev1.NodeCondition{ { - Type: v1.NodeReady, - Status: v1.ConditionTrue, + Type: corev1.NodeReady, + Status: corev1.ConditionTrue, + }, + }, + }, + } + node2 := &corev1.Node{ + ObjectMeta: metav1.ObjectMeta{ + Name: nodeName2, + Labels: map[string]string{ + "node.kubernetes.io/instance-type": "gpu2", + }, + }, + Status: corev1.NodeStatus{ + Conditions: []corev1.NodeCondition{ + { + Type: corev1.NodeReady, + Status: corev1.ConditionTrue, }, }, }, } Expect(k8sClient.Create(ctx, node1)).Should(Succeed()) defer k8sClient.Delete(ctx, node1) + Expect(k8sClient.Create(ctx, node2)).Should(Succeed()) + defer k8sClient.Delete(ctx, node2) - localModelNode := &v1alpha1.LocalModelNode{} + localModelNode1 := &v1alpha1.LocalModelNode{} Eventually(func() bool { - err := k8sClient.Get(ctx, types.NamespacedName{Name: nodeName}, localModelNode) + err := k8sClient.Get(ctx, types.NamespacedName{Name: nodeName1}, localModelNode1) return err == nil }, timeout, interval).Should(BeTrue()) - Expect(localModelNode.Spec.LocalModels).Should(ContainElement(v1alpha1.LocalModelInfo{ModelName: cachedModel.Name, SourceModelUri: sourceModelUri})) + Expect(localModelNode1.Spec.LocalModels).Should(ContainElement(v1alpha1.LocalModelInfo{ModelName: cachedModel.Name, SourceModelUri: sourceModelUri})) + localModelNode2 := &v1alpha1.LocalModelNode{} + Eventually(func() bool { + err := k8sClient.Get(ctx, types.NamespacedName{Name: nodeName2}, localModelNode2) + return err == nil + }, timeout, interval).Should(BeTrue()) + Expect(localModelNode2.Spec.LocalModels).Should(ContainElement(v1alpha1.LocalModelInfo{ModelName: cachedModel.Name, SourceModelUri: sourceModelUri})) // Todo: Test agent download // Update the LocalModelNode status to be successful - localModelNode.Status.ModelStatus = map[string]v1alpha1.ModelStatus{cachedModel.Name: v1alpha1.ModelDownloaded} - Expect(k8sClient.Status().Update(ctx, localModelNode)).Should(Succeed()) + localModelNode1.Status.ModelStatus = map[string]v1alpha1.ModelStatus{cachedModel.Name: v1alpha1.ModelDownloaded} + Expect(k8sClient.Status().Update(ctx, localModelNode1)).Should(Succeed()) + localModelNode2.Status.ModelStatus = map[string]v1alpha1.ModelStatus{cachedModel.Name: v1alpha1.ModelDownloaded} + Expect(k8sClient.Status().Update(ctx, localModelNode2)).Should(Succeed()) Eventually(func() bool { err := k8sClient.Get(ctx, modelLookupKey, cachedModel) if err != nil { return false } - if !(cachedModel.Status.ModelCopies.Available == 1 && cachedModel.Status.ModelCopies.Total == 1 && cachedModel.Status.ModelCopies.Failed == 0) { + if !(cachedModel.Status.ModelCopies.Available == 2 && cachedModel.Status.ModelCopies.Total == 2 && cachedModel.Status.ModelCopies.Failed == 0) { return false } - if cachedModel.Status.NodeStatus[nodeName] != v1alpha1.NodeDownloaded { + if cachedModel.Status.NodeStatus[nodeName1] != v1alpha1.NodeDownloaded { + return false + } + if cachedModel.Status.NodeStatus[nodeName2] != v1alpha1.NodeDownloaded { return false } return true @@ -219,37 +316,30 @@ var _ = Describe("CachedModel controller", func() { }) It("Should create pvs and pvcs for inference services", func() { - var configMap = &v1.ConfigMap{ + defer GinkgoRecover() + ctx, cancel := context.WithCancel(context.Background()) + DeferCleanup(cancel) + nodeGroup1 := &v1alpha1.LocalModelNodeGroup{ ObjectMeta: metav1.ObjectMeta{ - Name: constants.InferenceServiceConfigMapName, - Namespace: constants.KServeNamespace, + Name: "gpu1", }, - Data: configs, + Spec: localModelNodeGroupSpec1, } - Expect(k8sClient.Create(context.TODO(), configMap)).NotTo(HaveOccurred()) - defer k8sClient.Delete(context.TODO(), configMap) - - clusterStorageContainer := &v1alpha1.ClusterStorageContainer{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - }, - Spec: clusterStorageContainerSpec, - } - Expect(k8sClient.Create(ctx, clusterStorageContainer)).Should(Succeed()) - defer k8sClient.Delete(ctx, clusterStorageContainer) - - nodeGroup := &v1alpha1.LocalModelNodeGroup{ + nodeGroup2 := &v1alpha1.LocalModelNodeGroup{ ObjectMeta: metav1.ObjectMeta{ - Name: "gpu", + Name: "gpu2", }, - Spec: localModelNodeGroupSpec, + Spec: localModelNodeGroupSpec1, } - Expect(k8sClient.Create(ctx, nodeGroup)).Should(Succeed()) - defer k8sClient.Delete(ctx, nodeGroup) + Expect(k8sClient.Create(ctx, nodeGroup1)).Should(Succeed()) + Expect(k8sClient.Create(ctx, nodeGroup2)).Should(Succeed()) + defer k8sClient.Delete(ctx, nodeGroup1) + defer k8sClient.Delete(ctx, nodeGroup2) modelName := "iris2" isvcNamespace := "default" - isvcName := "foo" + isvcName1 := "foo" + isvcName2 := "bar" cachedModel := &v1alpha1.LocalModelCache{ ObjectMeta: metav1.ObjectMeta{ Name: modelName, @@ -258,10 +348,11 @@ var _ = Describe("CachedModel controller", func() { } Expect(k8sClient.Create(ctx, cachedModel)).Should(Succeed()) defer k8sClient.Delete(ctx, cachedModel) - - isvc := &v1beta1.InferenceService{ + // No nodegroup annotation, should pick the default nodegroup, + // which is the first one in the model cache nodegroup list + isvc1 := &v1beta1.InferenceService{ ObjectMeta: metav1.ObjectMeta{ - Name: "foo", + Name: isvcName1, Namespace: isvcNamespace, }, Spec: v1beta1.InferenceServiceSpec{ @@ -275,36 +366,70 @@ var _ = Describe("CachedModel controller", func() { }, }, } + // Has nodegroup annotation, should pick the specified nodegroup + isvc2 := &v1beta1.InferenceService{ + ObjectMeta: metav1.ObjectMeta{ + Name: isvcName2, + Namespace: isvcNamespace, + Annotations: map[string]string{constants.NodeGroupAnnotationKey: "gpu2"}, + }, + Spec: v1beta1.InferenceServiceSpec{ + Predictor: v1beta1.PredictorSpec{ + Model: &v1beta1.ModelSpec{ + PredictorExtensionSpec: v1beta1.PredictorExtensionSpec{ + StorageURI: ptr.To(sourceModelUri), + }, + ModelFormat: v1beta1.ModelFormat{Name: "sklearn"}, + }, + }, + }, + } // Mutating webhook adds a local model label cachedModelList := &v1alpha1.LocalModelCacheList{} cachedModelList.Items = []v1alpha1.LocalModelCache{*cachedModel} - isvc.DefaultInferenceService(nil, nil, nil, cachedModelList) + isvc1.DefaultInferenceService(nil, nil, nil, cachedModelList) + isvc2.DefaultInferenceService(nil, nil, nil, cachedModelList) - Expect(k8sClient.Create(ctx, isvc)).Should(Succeed()) - inferenceService := &v1beta1.InferenceService{} + Expect(k8sClient.Create(ctx, isvc1)).Should(Succeed()) + inferenceService1 := &v1beta1.InferenceService{} Eventually(func() bool { - err := k8sClient.Get(ctx, types.NamespacedName{Name: isvcName, Namespace: isvcNamespace}, inferenceService) - if err != nil { - return false - } - return true + err := k8sClient.Get(ctx, types.NamespacedName{Name: isvcName1, Namespace: isvcNamespace}, inferenceService1) + return err == nil + }, timeout, interval).Should(BeTrue()) + Expect(k8sClient.Create(ctx, isvc2)).Should(Succeed()) + inferenceService2 := &v1beta1.InferenceService{} + Eventually(func() bool { + err := k8sClient.Get(ctx, types.NamespacedName{Name: isvcName2, Namespace: isvcNamespace}, inferenceService2) + return err == nil }, timeout, interval).Should(BeTrue()) // Expects a pv and a pvc are created in the isvcNamespace - pvLookupKey := types.NamespacedName{Name: modelName + "-" + nodeGroup.Name + "-" + isvcNamespace} - pvcLookupKey := types.NamespacedName{Name: modelName + "-" + nodeGroup.Name, Namespace: isvcNamespace} + pvLookupKey1 := types.NamespacedName{Name: modelName + "-" + nodeGroup1.Name + "-" + isvcNamespace} + pvLookupKey2 := types.NamespacedName{Name: modelName + "-" + nodeGroup2.Name + "-" + isvcNamespace} + pvcLookupKey1 := types.NamespacedName{Name: modelName + "-" + nodeGroup1.Name, Namespace: isvcNamespace} + pvcLookupKey2 := types.NamespacedName{Name: modelName + "-" + nodeGroup2.Name, Namespace: isvcNamespace} - persistentVolume := &v1.PersistentVolume{} + persistentVolume1 := &corev1.PersistentVolume{} Eventually(func() bool { - err := k8sClient.Get(ctx, pvLookupKey, persistentVolume) - return err == nil && persistentVolume != nil + err := k8sClient.Get(ctx, pvLookupKey1, persistentVolume1) + return err == nil && persistentVolume1 != nil + }, timeout, interval).Should(BeTrue()) + persistentVolume2 := &corev1.PersistentVolume{} + Eventually(func() bool { + err := k8sClient.Get(ctx, pvLookupKey2, persistentVolume2) + return err == nil && persistentVolume2 != nil }, timeout, interval).Should(BeTrue()) - persistentVolumeClaim := &v1.PersistentVolumeClaim{} + persistentVolumeClaim1 := &corev1.PersistentVolumeClaim{} Eventually(func() bool { - err := k8sClient.Get(ctx, pvcLookupKey, persistentVolumeClaim) - return err == nil && persistentVolumeClaim != nil + err := k8sClient.Get(ctx, pvcLookupKey1, persistentVolumeClaim1) + return err == nil && persistentVolumeClaim1 != nil + }, timeout, interval).Should(BeTrue()) + persistentVolumeClaim2 := &corev1.PersistentVolumeClaim{} + Eventually(func() bool { + err := k8sClient.Get(ctx, pvcLookupKey2, persistentVolumeClaim2) + return err == nil && persistentVolumeClaim2 != nil }, timeout, interval).Should(BeTrue()) Eventually(func() bool { @@ -312,110 +437,226 @@ var _ = Describe("CachedModel controller", func() { if err != nil { return false } - if len(cachedModel.Status.InferenceServices) != 1 { + if len(cachedModel.Status.InferenceServices) != 2 { return false } - isvcNamespacedName := cachedModel.Status.InferenceServices[0] - if isvcNamespacedName.Name == isvcName && isvcNamespacedName.Namespace == isvcNamespace { + isvcNamespacedName1 := cachedModel.Status.InferenceServices[0] + isvcNamespacedName2 := cachedModel.Status.InferenceServices[0] + if isvcNamespacedName1.Name == isvcName1 && isvcNamespacedName1.Namespace == isvcNamespace { + return true + } + if isvcNamespacedName2.Name == isvcName2 && isvcNamespacedName2.Namespace == isvcNamespace { return true } return false }, timeout, interval).Should(BeTrue(), "Node status should have the isvc") // Next we delete the isvc and make sure the pv and pvc are deleted - Expect(k8sClient.Delete(ctx, isvc)).Should(Succeed()) + Expect(k8sClient.Delete(ctx, isvc1)).Should(Succeed()) + Expect(k8sClient.Delete(ctx, isvc2)).Should(Succeed()) - newPersistentVolume := &v1.PersistentVolume{} - newPersistentVolumeClaim := &v1.PersistentVolumeClaim{} + newPersistentVolume1 := &corev1.PersistentVolume{} + newPersistentVolume2 := &corev1.PersistentVolume{} + newPersistentVolumeClaim1 := &corev1.PersistentVolumeClaim{} + newPersistentVolumeClaim2 := &corev1.PersistentVolumeClaim{} + Eventually(func() bool { + err := k8sClient.Get(ctx, pvLookupKey1, newPersistentVolume1) + if err != nil { + return false + } + if newPersistentVolume1.DeletionTimestamp != nil { + return true + } + return false + }, timeout, interval).Should(BeTrue()) Eventually(func() bool { - err := k8sClient.Get(ctx, pvLookupKey, newPersistentVolume) + err := k8sClient.Get(ctx, pvLookupKey2, newPersistentVolume2) if err != nil { return false } - if newPersistentVolume.DeletionTimestamp != nil { + if newPersistentVolume2.DeletionTimestamp != nil { return true } return false }, timeout, interval).Should(BeTrue()) Eventually(func() bool { - err := k8sClient.Get(ctx, pvcLookupKey, newPersistentVolumeClaim) + err := k8sClient.Get(ctx, pvcLookupKey1, newPersistentVolumeClaim1) + if err != nil { + return false + } + if newPersistentVolumeClaim1.DeletionTimestamp != nil { + return true + } + return false + }, timeout, interval).Should(BeTrue()) + Eventually(func() bool { + err := k8sClient.Get(ctx, pvcLookupKey2, newPersistentVolumeClaim2) if err != nil { return false } - if newPersistentVolumeClaim.DeletionTimestamp != nil { + if newPersistentVolumeClaim2.DeletionTimestamp != nil { return true } return false }, timeout, interval).Should(BeTrue()) }) }) - Context("When creating multiple localModels", func() { - // With two nodes and two local models, each node should have both local models - It("Should create localModelNode correctly", func() { - var configMap = &v1.ConfigMap{ + + Context("When DisableVolumeManagement is set to true", func() { + var ( + configMap *corev1.ConfigMap + clusterStorageContainer *v1alpha1.ClusterStorageContainer + ) + + BeforeEach(func() { + ctx, cancel := context.WithCancel(context.Background()) + configs = map[string]string{ + "localModel": `{ + "jobNamespace": "kserve-localmodel-jobs", + "defaultJobImage": "kserve/storage-initializer:latest", + "disableVolumeManagement": true + }`, + } + configMap, clusterStorageContainer = genericSetup(ctx, configs, clusterStorageContainerSpec) + initializeManager(ctx, cfg) + DeferCleanup(func() { + k8sClient.Delete(ctx, clusterStorageContainer) + k8sClient.Delete(ctx, configMap) + + By("canceling the context") + cancel() + }) + }) + + It("Should NOT create/delete pvs and pvcs if localmodel config value DisableVolumeManagement is true", func() { + defer GinkgoRecover() + ctx, cancel := context.WithCancel(context.Background()) + DeferCleanup(cancel) + testNamespace := "test-namespace" + namespaceObj := createTestNamespace(ctx, testNamespace) + defer k8sClient.Delete(ctx, namespaceObj) + + modelName := "iris3" + cachedModel := &v1alpha1.LocalModelCache{ ObjectMeta: metav1.ObjectMeta{ - Name: constants.InferenceServiceConfigMapName, - Namespace: constants.KServeNamespace, + Name: modelName, }, - Data: configs, + Spec: localModelSpec, + } + Expect(k8sClient.Create(ctx, cachedModel)).Should(Succeed()) + defer k8sClient.Delete(ctx, cachedModel) + + pvcName := "test-pvc" + pvName := pvcName + "-" + testNamespace + + pv := createTestPV(ctx, pvName, cachedModel) + defer k8sClient.Delete(ctx, pv) + + pvc := createTestPVC(ctx, pvcName, testNamespace, pvName, cachedModel) + defer k8sClient.Delete(ctx, pvc) + + // Expects test-pv and test-pvc to not get deleted + pvLookupKey := types.NamespacedName{Name: pvName} + pvcLookupKey := types.NamespacedName{Name: pvcName, Namespace: testNamespace} + + persistentVolume := &corev1.PersistentVolume{} + Eventually(func() bool { + err := k8sClient.Get(ctx, pvLookupKey, persistentVolume) + return err == nil && persistentVolume != nil + }, timeout, interval).Should(BeTrue()) + + persistentVolumeClaim := &corev1.PersistentVolumeClaim{} + Eventually(func() bool { + err := k8sClient.Get(ctx, pvcLookupKey, persistentVolumeClaim) + return err == nil && persistentVolumeClaim != nil + }, timeout, interval).Should(BeTrue()) + }) + }) + + Context("When creating multiple localModels", func() { + var ( + configMap *corev1.ConfigMap + clusterStorageContainer *v1alpha1.ClusterStorageContainer + + configs = map[string]string{ + "localModel": `{ + "jobNamespace": "kserve-localmodel-jobs", + "defaultJobImage": "kserve/storage-initializer:latest" + }`, } - Expect(k8sClient.Create(context.TODO(), configMap)).NotTo(HaveOccurred()) - defer k8sClient.Delete(context.TODO(), configMap) + ) + BeforeEach(func() { + ctx, cancel := context.WithCancel(context.TODO()) + configMap, clusterStorageContainer = genericSetup(ctx, configs, clusterStorageContainerSpec) + + initializeManager(ctx, cfg) + DeferCleanup(func() { + k8sClient.Delete(ctx, clusterStorageContainer) + k8sClient.Delete(ctx, configMap) + + By("canceling the context") + cancel() + }) + }) - clusterStorageContainer := &v1alpha1.ClusterStorageContainer{ + // With two nodes and two local models, each node should have both local models + It("Should create localModelNode correctly", func() { + defer GinkgoRecover() + ctx, cancel := context.WithCancel(context.Background()) + DeferCleanup(cancel) + nodeGroup1 := &v1alpha1.LocalModelNodeGroup{ ObjectMeta: metav1.ObjectMeta{ - Name: "test", + Name: "gpu1", }, - Spec: clusterStorageContainerSpec, + Spec: localModelNodeGroupSpec1, } - Expect(k8sClient.Create(ctx, clusterStorageContainer)).Should(Succeed()) - defer k8sClient.Delete(ctx, clusterStorageContainer) - - nodeGroup := &v1alpha1.LocalModelNodeGroup{ + Expect(k8sClient.Create(ctx, nodeGroup1)).Should(Succeed()) + defer k8sClient.Delete(ctx, nodeGroup1) + nodeGroup2 := &v1alpha1.LocalModelNodeGroup{ ObjectMeta: metav1.ObjectMeta{ - Name: "gpu", + Name: "gpu2", }, - Spec: localModelNodeGroupSpec, + Spec: localModelNodeGroupSpec2, } - Expect(k8sClient.Create(ctx, nodeGroup)).Should(Succeed()) - defer k8sClient.Delete(ctx, nodeGroup) + Expect(k8sClient.Create(ctx, nodeGroup2)).Should(Succeed()) + defer k8sClient.Delete(ctx, nodeGroup2) - node1 := &v1.Node{ + node1 := &corev1.Node{ ObjectMeta: metav1.ObjectMeta{ Name: "node1", Labels: map[string]string{ - "node.kubernetes.io/instance-type": "gpu", + "node.kubernetes.io/instance-type": "gpu1", }, }, - Status: v1.NodeStatus{ - Conditions: []v1.NodeCondition{ + Status: corev1.NodeStatus{ + Conditions: []corev1.NodeCondition{ { - Type: v1.NodeReady, - Status: v1.ConditionTrue, + Type: corev1.NodeReady, + Status: corev1.ConditionTrue, }, }, }, } - node2 := &v1.Node{ + node2 := &corev1.Node{ ObjectMeta: metav1.ObjectMeta{ Name: "node2", Labels: map[string]string{ - "node.kubernetes.io/instance-type": "gpu", + "node.kubernetes.io/instance-type": "gpu2", }, }, - Status: v1.NodeStatus{ - Conditions: []v1.NodeCondition{ + Status: corev1.NodeStatus{ + Conditions: []corev1.NodeCondition{ { - Type: v1.NodeReady, - Status: v1.ConditionTrue, + Type: corev1.NodeReady, + Status: corev1.ConditionTrue, }, }, }, } - nodes := []*v1.Node{node1, node2} + nodes := []*corev1.Node{node1, node2} for _, node := range nodes { Expect(k8sClient.Create(ctx, node)).Should(Succeed()) defer k8sClient.Delete(ctx, node) @@ -467,3 +708,133 @@ var _ = Describe("CachedModel controller", func() { }) }) }) + +func createTestNamespace(ctx context.Context, name string) *corev1.Namespace { + namespace := &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + } + Expect(k8sClient.Create(ctx, namespace)).Should(Succeed()) + return namespace +} + +func createTestPV(ctx context.Context, pvName string, cachedModel *v1alpha1.LocalModelCache) *corev1.PersistentVolume { + pv := &corev1.PersistentVolume{ + ObjectMeta: metav1.ObjectMeta{ + Name: pvName, + OwnerReferences: []metav1.OwnerReference{ + { + APIVersion: "serving.kserve.io/v1alpha1", + Kind: "LocalModelCache", + Name: cachedModel.Name, + UID: cachedModel.UID, + Controller: ptr.To(true), + }, + }, + }, + Spec: corev1.PersistentVolumeSpec{ + AccessModes: []corev1.PersistentVolumeAccessMode{corev1.ReadWriteOnce}, + Capacity: corev1.ResourceList{ + corev1.ResourceStorage: resource.MustParse("2Gi"), + }, + PersistentVolumeSource: corev1.PersistentVolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: "/mnt/data", + Type: ptr.To(corev1.HostPathDirectoryOrCreate), + }, + }, + }, + } + Expect(k8sClient.Create(ctx, pv)).Should(Succeed()) + return pv +} + +func createTestPVC(ctx context.Context, pvcName, namespace, pvName string, cachedModel *v1alpha1.LocalModelCache) *corev1.PersistentVolumeClaim { + pvc := &corev1.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{ + Name: pvcName, + Namespace: namespace, + OwnerReferences: []metav1.OwnerReference{ + { + APIVersion: "serving.kserve.io/v1alpha1", + Kind: "LocalModelCache", + Name: cachedModel.Name, + UID: cachedModel.UID, + Controller: ptr.To(true), + }, + }, + }, + Spec: corev1.PersistentVolumeClaimSpec{ + AccessModes: []corev1.PersistentVolumeAccessMode{corev1.ReadWriteOnce}, + Resources: corev1.VolumeResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceStorage: resource.MustParse("2Gi"), + }, + }, + VolumeName: pvName, + }, + } + Expect(k8sClient.Create(ctx, pvc)).Should(Succeed()) + return pvc +} + +func genericSetup(ctx context.Context, configs map[string]string, clusterStorageContainerSpec v1alpha1.StorageContainerSpec) (*corev1.ConfigMap, + *v1alpha1.ClusterStorageContainer, +) { + configMap := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: constants.InferenceServiceConfigMapName, + Namespace: constants.KServeNamespace, + }, + Data: configs, + } + Expect(k8sClient.Create(ctx, configMap)).NotTo(HaveOccurred()) + + clusterStorageContainer := &v1alpha1.ClusterStorageContainer{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + }, + Spec: clusterStorageContainerSpec, + } + Expect(k8sClient.Create(ctx, clusterStorageContainer)).Should(Succeed()) + + return configMap, clusterStorageContainer +} + +func initializeManager(ctx context.Context, cfg *rest.Config) { + clientset, err := kubernetes.NewForConfig(cfg) + Expect(err).ToNot(HaveOccurred()) + Expect(clientset).ToNot(BeNil()) + k8sManager, err := ctrl.NewManager(cfg, ctrl.Options{ + Scheme: scheme.Scheme, + Metrics: metricsserver.Options{ + BindAddress: "0", + }, + Controller: crconfig.Controller{ + SkipNameValidation: ptr.To(true), + }, + }) + Expect(err).ToNot(HaveOccurred()) + + k8sClient = k8sManager.GetClient() + Expect(k8sClient).ToNot(BeNil()) + //nolint: contextcheck + err = (&LocalModelReconciler{ + Client: k8sClient, + Clientset: clientset, + Scheme: scheme.Scheme, + Log: ctrl.Log.WithName("v1alpha1LocalModelController"), + }).SetupWithManager(k8sManager) + Expect(err).ToNot(HaveOccurred()) + + go func() { + err = k8sManager.Start(ctx) + Expect(err).ToNot(HaveOccurred()) + }() + // Wait for cache to start + // Ping the ConfigMap to ensure the cache is started + Eventually(func() bool { + return k8sClient.Get(ctx, types.NamespacedName{Name: constants.InferenceServiceConfigMapName, Namespace: constants.KServeNamespace}, &corev1.ConfigMap{}) == nil + }).Should(BeTrue()) +} diff --git a/pkg/controller/v1alpha1/localmodel/suite_test.go b/pkg/controller/v1alpha1/localmodel/suite_test.go index 530f119700c..485ec46adb2 100644 --- a/pkg/controller/v1alpha1/localmodel/suite_test.go +++ b/pkg/controller/v1alpha1/localmodel/suite_test.go @@ -23,21 +23,16 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/rest" - knservingv1 "knative.dev/serving/pkg/apis/serving/v1" - ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/envtest" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/log/zap" - metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" - kservev1beta1 "github.com/kserve/kserve/pkg/apis/serving/v1beta1" + "github.com/kserve/kserve/pkg/apis/serving/v1beta1" "github.com/kserve/kserve/pkg/constants" pkgtest "github.com/kserve/kserve/pkg/testing" // +kubebuilder:scaffold:imports @@ -49,9 +44,6 @@ import ( var ( cfg *rest.Config k8sClient client.Client - testEnv *envtest.Environment - cancel context.CancelFunc - ctx context.Context ) func TestAPIs(t *testing.T) { @@ -62,22 +54,25 @@ func TestAPIs(t *testing.T) { var _ = BeforeSuite(func() { logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) - ctx, cancel = context.WithCancel(context.TODO()) By("bootstrapping test environment") crdDirectoryPaths := []string{ filepath.Join("..", "..", "..", "..", "test", "crds"), } - testEnv = pkgtest.SetupEnvTest(crdDirectoryPaths) + testEnv := pkgtest.SetupEnvTest(crdDirectoryPaths) var err error cfg, err = testEnv.Start() Expect(err).ToNot(HaveOccurred()) Expect(cfg).ToNot(BeNil()) + DeferCleanup(func() { + By("tearing down the test environment") + err := testEnv.Stop() + Expect(err).ToNot(HaveOccurred()) + }) + err = v1alpha1.AddToScheme(scheme.Scheme) Expect(err).NotTo(HaveOccurred()) - err = kservev1beta1.AddToScheme(scheme.Scheme) - Expect(err).NotTo(HaveOccurred()) - err = knservingv1.AddToScheme(scheme.Scheme) + err = v1beta1.AddToScheme(scheme.Scheme) Expect(err).NotTo(HaveOccurred()) // +kubebuilder:scaffold:scheme @@ -85,49 +80,17 @@ var _ = BeforeSuite(func() { Expect(err).ToNot(HaveOccurred()) Expect(k8sClient).ToNot(BeNil()) - clientset, err := kubernetes.NewForConfig(cfg) - Expect(err).ToNot(HaveOccurred()) - Expect(clientset).ToNot(BeNil()) - // Creates namespace - kserveNamespaceObj := &v1.Namespace{ + kserveNamespaceObj := &corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ Name: constants.KServeNamespace, }, } - jobsNamespaceObj := &v1.Namespace{ + jobsNamespaceObj := &corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ Name: "kserve-localmodel-jobs", }, } Expect(k8sClient.Create(context.Background(), kserveNamespaceObj)).Should(Succeed()) Expect(k8sClient.Create(context.Background(), jobsNamespaceObj)).Should(Succeed()) - - k8sManager, err := ctrl.NewManager(cfg, ctrl.Options{ - Scheme: scheme.Scheme, - Metrics: metricsserver.Options{ - BindAddress: "0", - }, - }) - - Expect(err).ToNot(HaveOccurred()) - err = (&LocalModelReconciler{ - Client: k8sManager.GetClient(), - Clientset: clientset, - Scheme: scheme.Scheme, - Log: ctrl.Log.WithName("v1alpha1LocalModelController"), - }).SetupWithManager(k8sManager) - Expect(err).ToNot(HaveOccurred()) - defer GinkgoRecover() - go func() { - err = k8sManager.Start(ctx) - Expect(err).ToNot(HaveOccurred()) - }() -}) - -var _ = AfterSuite(func() { - cancel() - By("tearing down the test environment") - err := testEnv.Stop() - Expect(err).ToNot(HaveOccurred()) }) diff --git a/pkg/controller/v1alpha1/localmodelnode/controller.go b/pkg/controller/v1alpha1/localmodelnode/controller.go index 620dfbc41af..a7f7703ec71 100644 --- a/pkg/controller/v1alpha1/localmodelnode/controller.go +++ b/pkg/controller/v1alpha1/localmodelnode/controller.go @@ -34,18 +34,22 @@ import ( "time" "github.com/go-logr/logr" - v1alpha1api "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" - "github.com/kserve/kserve/pkg/apis/serving/v1beta1" batchv1 "k8s.io/api/batch/v1" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/kubernetes" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/reconcile" + + "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" + "github.com/kserve/kserve/pkg/apis/serving/v1beta1" + "github.com/kserve/kserve/pkg/constants" + "github.com/kserve/kserve/pkg/controller/v1alpha1/utils" ) type LocalModelNodeReconciler struct { @@ -72,15 +76,47 @@ var ( fsHelper FileSystemInterface ) -func (c *LocalModelNodeReconciler) launchJob(ctx context.Context, localModelNode v1alpha1api.LocalModelNode, modelInfo v1alpha1api.LocalModelInfo) (*batchv1.Job, error) { +// Returns the nodegroup of a node +// NOTE: Assuming a node could only belong to 1 nodegroup +func (c *LocalModelNodeReconciler) getNodeGroupFromNode(ctx context.Context, nodeName string) (*v1alpha1.LocalModelNodeGroup, error) { + node := &corev1.Node{} + if err := c.Get(ctx, types.NamespacedName{Name: nodeName}, node); err != nil { + return nil, err + } + nodeGroups := &v1alpha1.LocalModelNodeGroupList{} + if err := c.List(ctx, nodeGroups); err != nil { + return nil, err + } + for _, nodeGroup := range nodeGroups.Items { + matches, err := utils.CheckNodeAffinity(&nodeGroup.Spec.PersistentVolumeSpec, *node) + if err != nil { + return nil, err + } + if matches { + return &nodeGroup, nil + } + } + + return nil, fmt.Errorf("did not find matching nodegroup for node: %s", nodeName) +} + +func (c *LocalModelNodeReconciler) launchJob(ctx context.Context, localModelNode v1alpha1.LocalModelNode, modelInfo v1alpha1.LocalModelInfo) (*batchv1.Job, error) { jobName := modelInfo.ModelName + "-" + localModelNode.ObjectMeta.Name + nodeGroup, err := c.getNodeGroupFromNode(ctx, nodeName) + if nodeGroup == nil { + c.Log.Error(err, "Failed to get node group for current node", "node name", nodeName) + return nil, err + } + pvcName := modelInfo.ModelName + "-" + nodeGroup.Name + c.Log.Info("Found the nodegroup of current node. Using the following PVC name to create download job", "current node", nodeName, "node group", nodeGroup.Name, "PVC name", pvcName) + container, err := c.getContainerSpecForStorageUri(ctx, modelInfo.SourceModelUri) if err != nil { return nil, err } container.Args = []string{modelInfo.SourceModelUri, MountPath} - container.VolumeMounts = []v1.VolumeMount{ + container.VolumeMounts = []corev1.VolumeMount{ { MountPath: MountPath, Name: PvcSourceMountName, @@ -96,22 +132,22 @@ func (c *LocalModelNodeReconciler) launchJob(ctx context.Context, localModelNode }, Spec: batchv1.JobSpec{ TTLSecondsAfterFinished: &jobTTLSecondsAfterFinished, - Template: v1.PodTemplateSpec{ - Spec: v1.PodSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ NodeName: nodeName, - Containers: []v1.Container{*container}, - RestartPolicy: v1.RestartPolicyNever, - Volumes: []v1.Volume{ + Containers: []corev1.Container{*container}, + RestartPolicy: corev1.RestartPolicyNever, + Volumes: []corev1.Volume{ { Name: PvcSourceMountName, - VolumeSource: v1.VolumeSource{ - PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{ - ClaimName: modelInfo.ModelName, + VolumeSource: corev1.VolumeSource{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ + ClaimName: pvcName, }, }, }, }, - SecurityContext: &v1.PodSecurityContext{ + SecurityContext: &corev1.PodSecurityContext{ FSGroup: FSGroup, }, }, @@ -123,18 +159,19 @@ func (c *LocalModelNodeReconciler) launchJob(ctx context.Context, localModelNode return nil, err } jobs := c.Clientset.BatchV1().Jobs(jobNamespace) - job, err = jobs.Create(ctx, job, metav1.CreateOptions{}) - c.Log.Info("Creating job", "name", job.Name, "namespace", job.Namespace) + createdJob, err := jobs.Create(ctx, job, metav1.CreateOptions{}) if err != nil { c.Log.Error(err, "Failed to create job.", "name", job.Name) return nil, err } - return job, err + c.Log.Info("Created job", "name", createdJob.Name, "namespace", createdJob.Namespace, + "model", modelInfo.ModelName) + return createdJob, err } // Fetches container spec for model download container, use the default KServe image if not found -func (c *LocalModelNodeReconciler) getContainerSpecForStorageUri(ctx context.Context, storageUri string) (*v1.Container, error) { - storageContainers := &v1alpha1api.ClusterStorageContainerList{} +func (c *LocalModelNodeReconciler) getContainerSpecForStorageUri(ctx context.Context, storageUri string) (*corev1.Container, error) { + storageContainers := &v1alpha1.ClusterStorageContainerList{} if err := c.Client.List(ctx, storageContainers); err != nil { return nil, err } @@ -143,7 +180,7 @@ func (c *LocalModelNodeReconciler) getContainerSpecForStorageUri(ctx context.Con if sc.IsDisabled() { continue } - if sc.Spec.WorkloadType != v1alpha1api.LocalModelDownloadJob { + if sc.Spec.WorkloadType != v1alpha1.LocalModelDownloadJob { continue } supported, err := sc.Spec.IsStorageUriSupported(storageUri) @@ -155,15 +192,15 @@ func (c *LocalModelNodeReconciler) getContainerSpecForStorageUri(ctx context.Con } } - defaultContainer := &v1.Container{ + defaultContainer := &corev1.Container{ Name: DownloadContainerName, Image: defaultJobImage, - TerminationMessagePolicy: v1.TerminationMessageFallbackToLogsOnError, + TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, } return defaultContainer, nil } -func (c *LocalModelNodeReconciler) getLatestJob(ctx context.Context, modelName string, nodeName string, excludeSucceeded bool) (*batchv1.Job, error) { +func (c *LocalModelNodeReconciler) getLatestJob(ctx context.Context, modelName string, nodeName string) (*batchv1.Job, int, error) { jobList := &batchv1.JobList{} labelSelector := map[string]string{ "model": modelName, @@ -172,40 +209,39 @@ func (c *LocalModelNodeReconciler) getLatestJob(ctx context.Context, modelName s if err := c.Client.List(ctx, jobList, client.InNamespace(jobNamespace), client.MatchingLabels(labelSelector)); err != nil { if errors.IsNotFound(err) { c.Log.Info("Job not found", "model", modelName) - return nil, nil + return nil, 0, nil } - return nil, err + return nil, 0, err } c.Log.Info("Found jobs", "model", modelName, "num of jobs", len(jobList.Items)) var latestJob *batchv1.Job for i, job := range jobList.Items { - if excludeSucceeded && job.Status.Succeeded > 0 { - continue - } if latestJob == nil || job.CreationTimestamp.After(latestJob.CreationTimestamp.Time) { latestJob = &jobList.Items[i] } } - return latestJob, nil + return latestJob, len(jobList.Items), nil } -func getModelStatusFromJobStatus(jobStatus batchv1.JobStatus) v1alpha1api.ModelStatus { +func getModelStatusFromJobStatus(jobStatus batchv1.JobStatus) v1alpha1.ModelStatus { switch { case jobStatus.Succeeded > 0: - return v1alpha1api.ModelDownloaded + return v1alpha1.ModelDownloaded case jobStatus.Failed > 0: - return v1alpha1api.ModelDownloadError + return v1alpha1.ModelDownloadError case jobStatus.Ready != nil && *jobStatus.Ready > 0: - return v1alpha1api.ModelDownloading + return v1alpha1.ModelDownloading default: - return v1alpha1api.ModelDownloadPending + return v1alpha1.ModelDownloadPending } } // Create jobs to download models if the model is not present locally // Update the status of the LocalModelNode CR -func (c *LocalModelNodeReconciler) downloadModels(ctx context.Context, localModelNode *v1alpha1api.LocalModelNode) error { - newStatus := map[string]v1alpha1api.ModelStatus{} +func (c *LocalModelNodeReconciler) downloadModels(ctx context.Context, localModelNode *v1alpha1.LocalModelNode) error { + c.Log.Info("Downloading models to", "node", localModelNode.ObjectMeta.Name) + + newStatus := map[string]v1alpha1.ModelStatus{} for _, modelInfo := range localModelNode.Spec.LocalModels { c.Log.Info("checking model from spec", "model", modelInfo.ModelName) var job *batchv1.Job @@ -219,12 +255,12 @@ func (c *LocalModelNodeReconciler) downloadModels(ctx context.Context, localMode // If folder exists and the job has been successfully completed, do nothing // If the job is cleaned up, no new job is created because the status is already set to ModelDownloaded if status, ok := localModelNode.Status.ModelStatus[modelInfo.ModelName]; ok { - if status == v1alpha1api.ModelDownloaded { - newStatus[modelInfo.ModelName] = v1alpha1api.ModelDownloaded + if status == v1alpha1.ModelDownloaded { + newStatus[modelInfo.ModelName] = v1alpha1.ModelDownloaded continue } } - job, err = c.getLatestJob(ctx, modelInfo.ModelName, nodeName, false) + job, _, err = c.getLatestJob(ctx, modelInfo.ModelName, nodeName) if err != nil { c.Log.Error(err, "Failed to getLatestJob", "model", modelInfo.ModelName, "node", nodeName) return err @@ -238,10 +274,13 @@ func (c *LocalModelNodeReconciler) downloadModels(ctx context.Context, localMode return err } } + newStatus[modelInfo.ModelName] = getModelStatusFromJobStatus(job.Status) + c.Log.Info("model downloading status:", "model", modelInfo.ModelName, + "node", localModelNode.ObjectMeta.Name, "status", newStatus[modelInfo.ModelName]) } else { // Folder does not exist c.Log.Info("Model folder not found", "model", modelInfo.ModelName) - job, err = c.getLatestJob(ctx, modelInfo.ModelName, nodeName, true) + job, jobCount, err := c.getLatestJob(ctx, modelInfo.ModelName, nodeName) if err != nil { c.Log.Error(err, "Failed to getLatestJob", "model", modelInfo.ModelName, "node", nodeName) return err @@ -252,19 +291,18 @@ func (c *LocalModelNodeReconciler) downloadModels(ctx context.Context, localMode // Recreate job if it has been terminated because the model is missing locally // If the job has failed, we do not retry here because there are retries on the job. // To retry the download, users can manually fix the issue and delete the failed job. - if job == nil || job.Status.Succeeded > 0 { - c.Log.Info("Download model", "model", modelInfo.ModelName) + // Add the job count check for protection to ensure not creating more than 2 jobs including the previous one. + if job == nil || (job.Status.Succeeded > 0 && jobCount < 2) { job, err = c.launchJob(ctx, *localModelNode, modelInfo) if err != nil { - c.Log.Error(err, "Failed to create Job", "model", modelInfo.ModelName, "node", nodeName) + c.Log.Error(err, "Failed to create job", "model", modelInfo.ModelName, "node", nodeName) return err } } + newStatus[modelInfo.ModelName] = getModelStatusFromJobStatus(job.Status) + c.Log.Info("model downloading status:", "model", modelInfo.ModelName, + "node", localModelNode.ObjectMeta.Name, "status", newStatus[modelInfo.ModelName]) } - newStatus[modelInfo.ModelName] = getModelStatusFromJobStatus(job.Status) - - c.Log.Info("Downloading models:", "model", modelInfo.ModelName, - "node", localModelNode.ObjectMeta.Name, "status", newStatus[modelInfo.ModelName]) } // Skip update if no changes to status @@ -283,16 +321,14 @@ func (c *LocalModelNodeReconciler) downloadModels(ctx context.Context, localMode } // Delete models that are not in the spec -func (c *LocalModelNodeReconciler) deleteModels(localModelNode v1alpha1api.LocalModelNode) error { +func (c *LocalModelNodeReconciler) deleteModels(localModelNode v1alpha1.LocalModelNode) error { // 1. Scan model dir and get a list of existing folders representing downloaded models foldersToRemove := map[string]struct{}{} - if err := fsHelper.ensureModelRootFolderExists(); err != nil { - c.Log.Error(err, "Failed to ensure model root folder exists") - return err - } entries, err := fsHelper.getModelFolders() if err != nil { c.Log.Error(err, "Failed to list model folder") + // TODO Reviewer: Is the err ignored intentionally? + return err } for _, entry := range entries { // Models could only exist in sub dir @@ -313,13 +349,15 @@ func (c *LocalModelNodeReconciler) deleteModels(localModelNode v1alpha1api.Local c.Log.Info("Removing model", "model", modelName) if err := fsHelper.removeModel(modelName); err != nil { c.Log.Error(err, "Failed to remove model directory", "model", modelName) + // TODO Reviewer: Is the err ignored intentionally? + return err } } } return nil } -func (c *LocalModelNodeReconciler) cleanupJobs(ctx context.Context, localModelNode v1alpha1api.LocalModelNode) error { +func (c *LocalModelNodeReconciler) cleanupJobs(ctx context.Context, localModelNode v1alpha1.LocalModelNode) error { // 1. Get all jobs for the LocalModelNode jobs := &batchv1.JobList{} labelSelector := map[string]string{"node": localModelNode.Name} @@ -365,11 +403,16 @@ func (c *LocalModelNodeReconciler) Reconcile(ctx context.Context, req ctrl.Reque // fsHelper is a global variable to allow mocking in tests if fsHelper == nil { fsHelper = NewFileSystemHelper(modelsRootFolder) + // TODO we need a way to ensure that the local path on persistent volume is the same as the local path of the node agent DaemonSet. + err := fsHelper.ensureModelRootFolderExists() + if err != nil { + panic("Failed to ensure model root folder exists: " + err.Error()) + } } // Create Jobs to download models if the model is not present locally. // 1. Check if LocalModelNode CR is for current node - localModelNode := v1alpha1api.LocalModelNode{} + localModelNode := v1alpha1.LocalModelNode{} if err := c.Get(ctx, req.NamespacedName, &localModelNode); err != nil { c.Log.Error(err, "Error getting LocalModelNode", "name", req.Name) return reconcile.Result{}, client.IgnoreNotFound(err) @@ -382,7 +425,12 @@ func (c *LocalModelNodeReconciler) Reconcile(ctx context.Context, req ctrl.Reque } // 3. Kick off download jobs for all models in spec - localModelConfig, err := v1beta1.NewLocalModelConfig(c.Clientset) + isvcConfigMap, err := v1beta1.GetInferenceServiceConfigMap(ctx, c.Clientset) + if err != nil { + c.Log.Error(err, "unable to get configmap", "name", constants.InferenceServiceConfigMapName, "namespace", constants.KServeNamespace) + return reconcile.Result{}, err + } + localModelConfig, err := v1beta1.NewLocalModelConfig(isvcConfigMap) if err != nil { c.Log.Error(err, "Failed to get local model config") return reconcile.Result{}, err @@ -412,7 +460,7 @@ func (c *LocalModelNodeReconciler) Reconcile(ctx context.Context, req ctrl.Reque func (c *LocalModelNodeReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). - For(&v1alpha1api.LocalModelNode{}). + For(&v1alpha1.LocalModelNode{}). Owns(&batchv1.Job{}). Complete(c) } diff --git a/pkg/controller/v1alpha1/localmodelnode/controller_test.go b/pkg/controller/v1alpha1/localmodelnode/controller_test.go index 408694363c6..1af7143bb37 100644 --- a/pkg/controller/v1alpha1/localmodelnode/controller_test.go +++ b/pkg/controller/v1alpha1/localmodelnode/controller_test.go @@ -23,18 +23,19 @@ import ( "os" "time" - "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" - "github.com/kserve/kserve/pkg/constants" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "github.com/stretchr/testify/mock" batchv1 "k8s.io/api/batch/v1" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" + "github.com/kserve/kserve/pkg/constants" ) type MockFileInfo struct { @@ -121,38 +122,38 @@ var _ = Describe("LocalModelNode controller", func() { } clusterStorageContainerSpec = v1alpha1.StorageContainerSpec{ SupportedUriFormats: []v1alpha1.SupportedUriFormat{{Prefix: "s3://"}}, - Container: v1.Container{ + Container: corev1.Container{ Name: "name", Image: "image", Args: []string{ "srcURI", constants.DefaultModelLocalMountPath, }, - TerminationMessagePolicy: v1.TerminationMessageFallbackToLogsOnError, - VolumeMounts: []v1.VolumeMount{}, + TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, + VolumeMounts: []corev1.VolumeMount{}, }, } localModelNodeGroupSpec = v1alpha1.LocalModelNodeGroupSpec{ - PersistentVolumeSpec: v1.PersistentVolumeSpec{ - AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce}, - VolumeMode: ptr.To(v1.PersistentVolumeFilesystem), - Capacity: v1.ResourceList{v1.ResourceStorage: resource.MustParse("2Gi")}, + PersistentVolumeSpec: corev1.PersistentVolumeSpec{ + AccessModes: []corev1.PersistentVolumeAccessMode{corev1.ReadWriteOnce}, + VolumeMode: ptr.To(corev1.PersistentVolumeFilesystem), + Capacity: corev1.ResourceList{corev1.ResourceStorage: resource.MustParse("2Gi")}, StorageClassName: "standard", - PersistentVolumeReclaimPolicy: v1.PersistentVolumeReclaimDelete, - PersistentVolumeSource: v1.PersistentVolumeSource{ - HostPath: &v1.HostPathVolumeSource{ + PersistentVolumeReclaimPolicy: corev1.PersistentVolumeReclaimDelete, + PersistentVolumeSource: corev1.PersistentVolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ Path: "/models", - Type: ptr.To(v1.HostPathDirectory), + Type: ptr.To(corev1.HostPathDirectory), }, }, - NodeAffinity: &v1.VolumeNodeAffinity{ - Required: &v1.NodeSelector{ - NodeSelectorTerms: []v1.NodeSelectorTerm{ + NodeAffinity: &corev1.VolumeNodeAffinity{ + Required: &corev1.NodeSelector{ + NodeSelectorTerms: []corev1.NodeSelectorTerm{ { - MatchExpressions: []v1.NodeSelectorRequirement{ + MatchExpressions: []corev1.NodeSelectorRequirement{ { Key: "node.kubernetes.io/instance-type", - Operator: v1.NodeSelectorOpIn, + Operator: corev1.NodeSelectorOpIn, Values: []string{"gpu"}, }, }, @@ -161,9 +162,9 @@ var _ = Describe("LocalModelNode controller", func() { }, }, }, - PersistentVolumeClaimSpec: v1.PersistentVolumeClaimSpec{ - AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce}, - Resources: v1.VolumeResourceRequirements{Requests: v1.ResourceList{v1.ResourceStorage: resource.MustParse("2Gi")}}, + PersistentVolumeClaimSpec: corev1.PersistentVolumeClaimSpec{ + AccessModes: []corev1.PersistentVolumeAccessMode{corev1.ReadWriteOnce}, + Resources: corev1.VolumeResourceRequirements{Requests: corev1.ResourceList{corev1.ResourceStorage: resource.MustParse("2Gi")}}, }, } configs = map[string]string{ @@ -179,7 +180,7 @@ var _ = Describe("LocalModelNode controller", func() { ctx := context.Background() fsMock.clear() fsMock.mockModel(&MockFileInfo{name: modelName, isDir: true}) - var configMap = &v1.ConfigMap{ + configMap := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: constants.InferenceServiceConfigMapName, Namespace: constants.KServeNamespace, @@ -208,6 +209,25 @@ var _ = Describe("LocalModelNode controller", func() { defer k8sClient.Delete(ctx, nodeGroup) nodeName = "worker" + node := &corev1.Node{ + ObjectMeta: metav1.ObjectMeta{ + Name: nodeName, + Labels: map[string]string{ + "node.kubernetes.io/instance-type": "gpu", + }, + }, + Status: corev1.NodeStatus{ + Conditions: []corev1.NodeCondition{ + { + Type: corev1.NodeReady, + Status: corev1.ConditionTrue, + }, + }, + }, + } + Expect(k8sClient.Create(ctx, node)).Should(Succeed()) + defer k8sClient.Delete(ctx, node) + localModelNode := &v1alpha1.LocalModelNode{ ObjectMeta: metav1.ObjectMeta{ Name: nodeName, @@ -266,16 +286,17 @@ var _ = Describe("LocalModelNode controller", func() { }) It("Should recreate download jobs if the model is missing from local disk", func() { fsMock.clear() - - var configMap = &v1.ConfigMap{ + ctx, cancel := context.WithCancel(context.Background()) + DeferCleanup(cancel) + configMap := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: constants.InferenceServiceConfigMapName, Namespace: constants.KServeNamespace, }, Data: configs, } - Expect(k8sClient.Create(context.TODO(), configMap)).NotTo(HaveOccurred()) - defer k8sClient.Delete(context.TODO(), configMap) + Expect(k8sClient.Create(ctx, configMap)).NotTo(HaveOccurred()) + defer k8sClient.Delete(ctx, configMap) clusterStorageContainer := &v1alpha1.ClusterStorageContainer{ ObjectMeta: metav1.ObjectMeta{ @@ -296,6 +317,25 @@ var _ = Describe("LocalModelNode controller", func() { defer k8sClient.Delete(ctx, nodeGroup) nodeName = "worker2" + node := &corev1.Node{ + ObjectMeta: metav1.ObjectMeta{ + Name: nodeName, + Labels: map[string]string{ + "node.kubernetes.io/instance-type": "gpu", + }, + }, + Status: corev1.NodeStatus{ + Conditions: []corev1.NodeCondition{ + { + Type: corev1.NodeReady, + Status: corev1.ConditionTrue, + }, + }, + }, + } + Expect(k8sClient.Create(ctx, node)).Should(Succeed()) + defer k8sClient.Delete(ctx, node) + localModelNode := &v1alpha1.LocalModelNode{ ObjectMeta: metav1.ObjectMeta{ Name: nodeName, @@ -352,8 +392,10 @@ var _ = Describe("LocalModelNode controller", func() { }, timeout, interval).Should(BeTrue(), "New job should be created") }) It("Should delete models from local disk if the model is not in the spec", func() { + ctx, cancel := context.WithCancel(context.Background()) + DeferCleanup(cancel) fsMock.clear() - var configMap = &v1.ConfigMap{ + configMap := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: constants.InferenceServiceConfigMapName, Namespace: constants.KServeNamespace, @@ -366,7 +408,25 @@ var _ = Describe("LocalModelNode controller", func() { // Mock readDir to return a fake model folder fsMock.mockModel(&MockFileInfo{name: modelName, isDir: true}) - nodeName = "worker" // Definied in controller.go, representing the name of the curent node + nodeName = "worker" // Definied in controller.go, representing the name of the current node + node := &corev1.Node{ + ObjectMeta: metav1.ObjectMeta{ + Name: nodeName, + Labels: map[string]string{ + "node.kubernetes.io/instance-type": "gpu", + }, + }, + Status: corev1.NodeStatus{ + Conditions: []corev1.NodeCondition{ + { + Type: corev1.NodeReady, + Status: corev1.ConditionTrue, + }, + }, + }, + } + Expect(k8sClient.Create(ctx, node)).Should(Succeed()) + defer k8sClient.Delete(ctx, node) // Creates a LocalModelNode with no models but the controller should find a model from local disk and delete it localModelNode := &v1alpha1.LocalModelNode{ ObjectMeta: metav1.ObjectMeta{ @@ -394,8 +454,10 @@ var _ = Describe("LocalModelNode controller", func() { }) // This test creates a LocalModelNode with a model, then deletes the model from the spec and checks if the job is deleted It("Should delete jobs if the model is not present", func() { + ctx, cancel := context.WithCancel(context.Background()) + DeferCleanup(cancel) fsMock.clear() - var configMap = &v1.ConfigMap{ + configMap := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: constants.InferenceServiceConfigMapName, Namespace: constants.KServeNamespace, @@ -408,7 +470,35 @@ var _ = Describe("LocalModelNode controller", func() { // Mock readDir to return a fake model folder fsMock.mockModel(&MockFileInfo{name: modelName, isDir: true}) - nodeName = "test3" // Definied in controller.go, representing the name of the curent node + nodeGroup := &v1alpha1.LocalModelNodeGroup{ + ObjectMeta: metav1.ObjectMeta{ + Name: "gpu", + }, + Spec: localModelNodeGroupSpec, + } + Expect(k8sClient.Create(ctx, nodeGroup)).Should(Succeed()) + defer k8sClient.Delete(ctx, nodeGroup) + + nodeName = "test3" // Definied in controller.go, representing the name of the current node + node := &corev1.Node{ + ObjectMeta: metav1.ObjectMeta{ + Name: nodeName, + Labels: map[string]string{ + "node.kubernetes.io/instance-type": "gpu", + }, + }, + Status: corev1.NodeStatus{ + Conditions: []corev1.NodeCondition{ + { + Type: corev1.NodeReady, + Status: corev1.ConditionTrue, + }, + }, + }, + } + Expect(k8sClient.Create(ctx, node)).Should(Succeed()) + defer k8sClient.Delete(ctx, node) + // Creates a LocalModelNode with no models but the controller should find a model from local disk and delete it localModelNode := &v1alpha1.LocalModelNode{ ObjectMeta: metav1.ObjectMeta{ diff --git a/pkg/controller/v1alpha1/localmodelnode/file_utils.go b/pkg/controller/v1alpha1/localmodelnode/file_utils.go index 35565dd2ddd..2f584a768f9 100644 --- a/pkg/controller/v1alpha1/localmodelnode/file_utils.go +++ b/pkg/controller/v1alpha1/localmodelnode/file_utils.go @@ -39,12 +39,12 @@ func NewFileSystemHelper(modelsRootFolder string) *FileSystemHelper { } // should be used only in this struct -func (f *FileSystemHelper) getModelFolderPrivate(modelName string) string { - return filepath.Join(f.modelsRootFolder, modelName) +func getModelFolder(rootFolderName string, modelName string) string { + return filepath.Join(rootFolderName, modelName) } func (f *FileSystemHelper) removeModel(modelName string) error { - path := f.getModelFolderPrivate(modelName) + path := getModelFolder(f.modelsRootFolder, modelName) return os.RemoveAll(path) } @@ -53,7 +53,7 @@ func (f *FileSystemHelper) getModelFolders() ([]os.DirEntry, error) { } func (f *FileSystemHelper) hasModelFolder(modelName string) (bool, error) { - folder := f.getModelFolderPrivate(modelName) + folder := getModelFolder(f.modelsRootFolder, modelName) _, err := os.ReadDir(folder) if err == nil { return true, nil diff --git a/pkg/controller/v1alpha1/localmodelnode/suite_test.go b/pkg/controller/v1alpha1/localmodelnode/suite_test.go index c382106af11..8782481336e 100644 --- a/pkg/controller/v1alpha1/localmodelnode/suite_test.go +++ b/pkg/controller/v1alpha1/localmodelnode/suite_test.go @@ -23,21 +23,19 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/rest" - knservingv1 "knative.dev/serving/pkg/apis/serving/v1" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/envtest" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/log/zap" metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" - kservev1beta1 "github.com/kserve/kserve/pkg/apis/serving/v1beta1" + "github.com/kserve/kserve/pkg/apis/serving/v1beta1" "github.com/kserve/kserve/pkg/constants" pkgtest "github.com/kserve/kserve/pkg/testing" // +kubebuilder:scaffold:imports @@ -49,9 +47,6 @@ import ( var ( cfg *rest.Config k8sClient client.Client - testEnv *envtest.Environment - cancel context.CancelFunc - ctx context.Context fsMock *mockFileSystem ) @@ -63,40 +58,52 @@ func TestAPIs(t *testing.T) { var _ = BeforeSuite(func() { logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) - ctx, cancel = context.WithCancel(context.TODO()) + ctx, cancel := context.WithCancel(context.TODO()) By("bootstrapping test environment") crdDirectoryPaths := []string{ filepath.Join("..", "..", "..", "..", "test", "crds"), } - testEnv = pkgtest.SetupEnvTest(crdDirectoryPaths) + testEnv := pkgtest.SetupEnvTest(crdDirectoryPaths) var err error cfg, err = testEnv.Start() Expect(err).ToNot(HaveOccurred()) Expect(cfg).ToNot(BeNil()) + DeferCleanup(func() { + cancel() + By("tearing down the test environment") + err := testEnv.Stop() + Expect(err).ToNot(HaveOccurred()) + }) + err = v1alpha1.AddToScheme(scheme.Scheme) Expect(err).NotTo(HaveOccurred()) - err = kservev1beta1.AddToScheme(scheme.Scheme) - Expect(err).NotTo(HaveOccurred()) - err = knservingv1.AddToScheme(scheme.Scheme) + err = v1beta1.AddToScheme(scheme.Scheme) Expect(err).NotTo(HaveOccurred()) // +kubebuilder:scaffold:scheme - k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) - Expect(err).ToNot(HaveOccurred()) - Expect(k8sClient).ToNot(BeNil()) - clientset, err := kubernetes.NewForConfig(cfg) Expect(err).ToNot(HaveOccurred()) Expect(clientset).ToNot(BeNil()) + k8sManager, err := ctrl.NewManager(cfg, ctrl.Options{ + Scheme: scheme.Scheme, + Metrics: metricsserver.Options{ + BindAddress: "0", + }, + }) + Expect(err).ToNot(HaveOccurred()) + + k8sClient = k8sManager.GetClient() + Expect(k8sClient).ToNot(BeNil()) + // Creates namespace - kserveNamespaceObj := &v1.Namespace{ + kserveNamespaceObj := &corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ Name: constants.KServeNamespace, }, } - jobsNamespaceObj := &v1.Namespace{ + jobsNamespaceObj := &corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ Name: "kserve-localmodel-jobs", }, @@ -107,14 +114,6 @@ var _ = BeforeSuite(func() { fsMock = newMockFileSystem() fsHelper = fsMock - k8sManager, err := ctrl.NewManager(cfg, ctrl.Options{ - Scheme: scheme.Scheme, - Metrics: metricsserver.Options{ - BindAddress: "0", - }, - }) - - Expect(err).ToNot(HaveOccurred()) err = (&LocalModelNodeReconciler{ Client: k8sManager.GetClient(), Clientset: clientset, @@ -128,10 +127,3 @@ var _ = BeforeSuite(func() { Expect(err).ToNot(HaveOccurred()) }() }) - -var _ = AfterSuite(func() { - cancel() - By("tearing down the test environment") - err := testEnv.Stop() - Expect(err).ToNot(HaveOccurred()) -}) diff --git a/pkg/controller/v1alpha1/trainedmodel/controller.go b/pkg/controller/v1alpha1/trainedmodel/controller.go index 0ffa086ff91..529a762a775 100644 --- a/pkg/controller/v1alpha1/trainedmodel/controller.go +++ b/pkg/controller/v1alpha1/trainedmodel/controller.go @@ -30,7 +30,7 @@ import ( "fmt" "github.com/go-logr/logr" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/equality" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" @@ -44,8 +44,8 @@ import ( logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/reconcile" - v1alpha1api "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" - v1beta1api "github.com/kserve/kserve/pkg/apis/serving/v1beta1" + "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" + "github.com/kserve/kserve/pkg/apis/serving/v1beta1" "github.com/kserve/kserve/pkg/constants" "github.com/kserve/kserve/pkg/controller/v1alpha1/trainedmodel/reconcilers/modelconfig" v1beta1utils "github.com/kserve/kserve/pkg/controller/v1beta1/inferenceservice/utils" @@ -73,7 +73,7 @@ type TrainedModelReconciler struct { func (r *TrainedModelReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { // Fetch the TrainedModel instance - tm := &v1alpha1api.TrainedModel{} + tm := &v1alpha1.TrainedModel{} if err := r.Get(ctx, req.NamespacedName, tm); err != nil { if errors.IsNotFound(err) { // Object not found, return. Created objects are automatically garbage collected. @@ -84,11 +84,11 @@ func (r *TrainedModelReconciler) Reconcile(ctx context.Context, req ctrl.Request } // If the parent InferenceService does not exists, delete the trainedmodel - isvc := &v1beta1api.InferenceService{} - if err := r.Get(context.TODO(), types.NamespacedName{Namespace: req.Namespace, Name: tm.Spec.InferenceService}, isvc); err != nil { + isvc := &v1beta1.InferenceService{} + if err := r.Get(ctx, types.NamespacedName{Namespace: req.Namespace, Name: tm.Spec.InferenceService}, isvc); err != nil { if errors.IsNotFound(err) { log.Info("Parent InferenceService does not exists, deleting TrainedModel", "TrainedModel", tm.Name, "InferenceService", isvc.Name) - if err := r.Delete(context.TODO(), tm); err != nil { + if err := r.Delete(ctx, tm); err != nil { log.Error(err, "Error deleting resource") return reconcile.Result{}, err } @@ -117,7 +117,7 @@ func (r *TrainedModelReconciler) Reconcile(ctx context.Context, req ctrl.Request // registering our finalizer. if !utils.Includes(tm.GetFinalizers(), tmFinalizerName) { tm.SetFinalizers(append(tm.GetFinalizers(), tmFinalizerName)) - if err := r.Update(context.Background(), tm); err != nil { + if err := r.Update(ctx, tm); err != nil { return ctrl.Result{}, err } } @@ -125,12 +125,12 @@ func (r *TrainedModelReconciler) Reconcile(ctx context.Context, req ctrl.Request // The object is being deleted if utils.Includes(tm.GetFinalizers(), tmFinalizerName) { // reconcile configmap to remove the model - if err := r.ModelConfigReconciler.Reconcile(req, tm); err != nil { + if err := r.ModelConfigReconciler.Reconcile(ctx, req, tm); err != nil { return reconcile.Result{}, err } // remove our finalizer from the list and update it. tm.SetFinalizers(utils.RemoveString(tm.GetFinalizers(), tmFinalizerName)) - if err := r.Update(context.Background(), tm); err != nil { + if err := r.Update(ctx, tm); err != nil { return ctrl.Result{}, err } } @@ -140,26 +140,26 @@ func (r *TrainedModelReconciler) Reconcile(ctx context.Context, req ctrl.Request } // Check inferenceserviceready, and memoryavailability - if err := r.updateConditions(req, tm); err != nil { + if err := r.updateConditions(ctx, req, tm); err != nil { return reconcile.Result{}, err } // update URL and Address fo TrainedModel - if err := r.updateStatus(req, tm); err != nil { + if err := r.updateStatus(ctx, req, tm); err != nil { return ctrl.Result{}, err } // Reconcile modelconfig to add this TrainedModel to its parent InferenceService's configmap - if err := r.ModelConfigReconciler.Reconcile(req, tm); err != nil { + if err := r.ModelConfigReconciler.Reconcile(ctx, req, tm); err != nil { return ctrl.Result{}, err } return ctrl.Result{}, nil } -func (r *TrainedModelReconciler) updateStatus(req ctrl.Request, desiredModel *v1alpha1api.TrainedModel) error { +func (r *TrainedModelReconciler) updateStatus(ctx context.Context, req ctrl.Request, desiredModel *v1alpha1.TrainedModel) error { // Get the parent inference service - isvc := &v1beta1api.InferenceService{} - if err := r.Get(context.TODO(), types.NamespacedName{Namespace: req.Namespace, Name: desiredModel.Spec.InferenceService}, isvc); err != nil { + isvc := &v1beta1.InferenceService{} + if err := r.Get(ctx, types.NamespacedName{Namespace: req.Namespace, Name: desiredModel.Spec.InferenceService}, isvc); err != nil { return err } @@ -190,8 +190,8 @@ func (r *TrainedModelReconciler) updateStatus(req ctrl.Request, desiredModel *v1 } // Get the current model - existingModel := &v1alpha1api.TrainedModel{} - if err := r.Get(context.TODO(), req.NamespacedName, existingModel); err != nil { + existingModel := &v1alpha1.TrainedModel{} + if err := r.Get(ctx, req.NamespacedName, existingModel); err != nil { if errors.IsNotFound(err) { return nil } @@ -202,8 +202,8 @@ func (r *TrainedModelReconciler) updateStatus(req ctrl.Request, desiredModel *v1 // We did not update anything } else { // Try to update model - if err := r.Status().Update(context.TODO(), desiredModel); err != nil { - r.Recorder.Eventf(desiredModel, v1.EventTypeWarning, "UpdateFailed", + if err := r.Status().Update(ctx, desiredModel); err != nil { + r.Recorder.Eventf(desiredModel, corev1.EventTypeWarning, "UpdateFailed", "Failed to update status for TrainedModel %q: %v", desiredModel.Name, err) } } @@ -211,10 +211,10 @@ func (r *TrainedModelReconciler) updateStatus(req ctrl.Request, desiredModel *v1 return nil } -func (r *TrainedModelReconciler) updateConditions(req ctrl.Request, tm *v1alpha1api.TrainedModel) error { +func (r *TrainedModelReconciler) updateConditions(ctx context.Context, req ctrl.Request, tm *v1alpha1.TrainedModel) error { // Get the parent inference service - isvc := &v1beta1api.InferenceService{} - if err := r.Get(context.TODO(), types.NamespacedName{Namespace: req.Namespace, Name: tm.Spec.InferenceService}, isvc); err != nil { + isvc := &v1beta1.InferenceService{} + if err := r.Get(ctx, types.NamespacedName{Namespace: req.Namespace, Name: tm.Spec.InferenceService}, isvc); err != nil { return err } @@ -222,14 +222,14 @@ func (r *TrainedModelReconciler) updateConditions(req ctrl.Request, tm *v1alpha1 // Update Inference Service Ready condition if isvc.Status.IsReady() { log.Info("Parent InferenceService is ready", "TrainedModel", tm.Name, "InferenceService", isvc.Name) - tm.Status.SetCondition(v1alpha1api.InferenceServiceReady, &apis.Condition{ - Status: v1.ConditionTrue, + tm.Status.SetCondition(v1alpha1.InferenceServiceReady, &apis.Condition{ + Status: corev1.ConditionTrue, }) } else { log.Info("Parent InferenceService is not ready", "TrainedModel", tm.Name, "InferenceService", isvc.Name) - tm.Status.SetCondition(v1alpha1api.InferenceServiceReady, &apis.Condition{ - Type: v1alpha1api.InferenceServiceReady, - Status: v1.ConditionFalse, + tm.Status.SetCondition(v1alpha1.InferenceServiceReady, &apis.Condition{ + Type: v1alpha1.InferenceServiceReady, + Status: corev1.ConditionFalse, Reason: "InferenceServiceNotReady", Message: "Inference Service needs to be ready before Trained Model can be ready", }) @@ -240,13 +240,13 @@ func (r *TrainedModelReconciler) updateConditions(req ctrl.Request, tm *v1alpha1 // Update Is MMS Predictor condition implementations := isvc.Spec.Predictor.GetImplementations() if len(implementations) > 0 && v1beta1utils.IsMMSPredictor(&isvc.Spec.Predictor) { - tm.Status.SetCondition(v1alpha1api.IsMMSPredictor, &apis.Condition{ - Status: v1.ConditionTrue, + tm.Status.SetCondition(v1alpha1.IsMMSPredictor, &apis.Condition{ + Status: corev1.ConditionTrue, }) } else { - tm.Status.SetCondition(v1alpha1api.IsMMSPredictor, &apis.Condition{ - Type: v1alpha1api.IsMMSPredictor, - Status: v1.ConditionFalse, + tm.Status.SetCondition(v1alpha1.IsMMSPredictor, &apis.Condition{ + Type: v1alpha1.IsMMSPredictor, + Status: corev1.ConditionFalse, Reason: "IsNotMMSPredictor", Message: "Inference Service predictor is not configured for multi-model serving", }) @@ -255,8 +255,8 @@ func (r *TrainedModelReconciler) updateConditions(req ctrl.Request, tm *v1alpha1 } // Get trained models with same inference service - var trainedModels v1alpha1api.TrainedModelList - if err := r.List(context.TODO(), &trainedModels, client.InNamespace(tm.Namespace), client.MatchingLabels{constants.ParentInferenceServiceLabel: isvc.Name, constants.TrainedModelAllocated: isvc.Name}); err != nil { + var trainedModels v1alpha1.TrainedModelList + if err := r.List(ctx, &trainedModels, client.InNamespace(tm.Namespace), client.MatchingLabels{constants.ParentInferenceServiceLabel: isvc.Name, constants.TrainedModelAllocated: isvc.Name}); err != nil { return err } @@ -270,20 +270,20 @@ func (r *TrainedModelReconciler) updateConditions(req ctrl.Request, tm *v1alpha1 log.Info("Parent InferenceService memory resources are available", "TrainedModel", tm.Name, "InferenceService", isvc.Name) if _, ok := tm.Labels[constants.TrainedModelAllocated]; !ok { tm.Labels[constants.TrainedModelAllocated] = isvc.Name - if updateErr := r.Update(context.Background(), tm); updateErr != nil { + if updateErr := r.Update(ctx, tm); updateErr != nil { r.Log.Error(updateErr, "Failed to update TrainedModel label", "TrainedModel", tm.Name) return updateErr } } - tm.Status.SetCondition(v1alpha1api.MemoryResourceAvailable, &apis.Condition{ - Status: v1.ConditionTrue, + tm.Status.SetCondition(v1alpha1.MemoryResourceAvailable, &apis.Condition{ + Status: corev1.ConditionTrue, }) } else { log.Info("Parent InferenceService memory resources are not available", "TrainedModel", tm.Name, "InferenceService", isvc.Name) - tm.Status.SetCondition(v1alpha1api.MemoryResourceAvailable, &apis.Condition{ - Type: v1alpha1api.MemoryResourceAvailable, - Status: v1.ConditionFalse, + tm.Status.SetCondition(v1alpha1.MemoryResourceAvailable, &apis.Condition{ + Type: v1alpha1.MemoryResourceAvailable, + Status: corev1.ConditionFalse, Reason: "MemoryResourceNotAvailable", Message: "Inference Service does not have enough memory resources for Trained Model", }) @@ -291,9 +291,9 @@ func (r *TrainedModelReconciler) updateConditions(req ctrl.Request, tm *v1alpha1 conditionErr = fmt.Errorf(MemoryResourceNotAvailable, isvc.Name, tm.Name) } - if statusErr := r.Status().Update(context.TODO(), tm); statusErr != nil { + if statusErr := r.Status().Update(ctx, tm); statusErr != nil { r.Log.Error(statusErr, "Failed to update TrainedModel condition", "TrainedModel", tm.Name) - r.Recorder.Eventf(tm, v1.EventTypeWarning, "UpdateFailed", + r.Recorder.Eventf(tm, corev1.EventTypeWarning, "UpdateFailed", "Failed to update conditions for TrainedModel: %v", statusErr) return statusErr } @@ -303,6 +303,6 @@ func (r *TrainedModelReconciler) updateConditions(req ctrl.Request, tm *v1alpha1 func (r *TrainedModelReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). - For(&v1alpha1api.TrainedModel{}). + For(&v1alpha1.TrainedModel{}). Complete(r) } diff --git a/pkg/controller/v1alpha1/trainedmodel/controller_test.go b/pkg/controller/v1alpha1/trainedmodel/controller_test.go index 344ac8328f3..65e58c9dfef 100644 --- a/pkg/controller/v1alpha1/trainedmodel/controller_test.go +++ b/pkg/controller/v1alpha1/trainedmodel/controller_test.go @@ -20,20 +20,22 @@ import ( "context" "time" - v1alpha1api "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" - "github.com/kserve/kserve/pkg/apis/serving/v1beta1" - "github.com/kserve/kserve/pkg/constants" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "google.golang.org/protobuf/proto" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" + "k8s.io/utils/ptr" "knative.dev/pkg/apis" duckv1 "knative.dev/pkg/apis/duck/v1" knservingv1 "knative.dev/serving/pkg/apis/serving/v1" "sigs.k8s.io/controller-runtime/pkg/reconcile" + + "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" + "github.com/kserve/kserve/pkg/apis/serving/v1beta1" + "github.com/kserve/kserve/pkg/constants" ) var _ = Describe("v1beta1 TrainedModel controller", func() { @@ -47,14 +49,14 @@ var _ = Describe("v1beta1 TrainedModel controller", func() { ) var ( - defaultResource = v1.ResourceRequirements{ - Limits: v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("1"), - v1.ResourceMemory: resource.MustParse("2Gi"), + defaultResource = corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("2Gi"), }, - Requests: v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("1"), - v1.ResourceMemory: resource.MustParse("2Gi"), + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("2Gi"), }, } configs = map[string]string{ @@ -78,17 +80,17 @@ var _ = Describe("v1beta1 TrainedModel controller", func() { Conditions: duckv1.Conditions{ { Type: knservingv1.ServiceConditionReady, - Status: v1.ConditionTrue, + Status: corev1.ConditionTrue, LastTransitionTime: apis.VolatileTime{Inner: metav1.NewTime(time.Now())}, }, { Type: v1beta1.PredictorReady, - Status: v1.ConditionTrue, + Status: corev1.ConditionTrue, LastTransitionTime: apis.VolatileTime{Inner: metav1.NewTime(time.Now())}, }, { Type: v1beta1.IngressReady, - Status: v1.ConditionTrue, + Status: corev1.ConditionTrue, LastTransitionTime: apis.VolatileTime{Inner: metav1.NewTime(time.Now())}, }, }, @@ -108,7 +110,7 @@ var _ = Describe("v1beta1 TrainedModel controller", func() { tmKey := types.NamespacedName{Name: modelName, Namespace: namespace} // Create InferenceService configmap - var configMap = &v1.ConfigMap{ + configMap := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: constants.InferenceServiceConfigMapName, Namespace: constants.KServeNamespace, @@ -119,8 +121,8 @@ var _ = Describe("v1beta1 TrainedModel controller", func() { defer k8sClient.Delete(context.TODO(), configMap) // Create the parent InferenceService - var expectedRequest = reconcile.Request{NamespacedName: types.NamespacedName{Name: parentInferenceService, Namespace: namespace}} - var serviceKey = expectedRequest.NamespacedName + expectedRequest := reconcile.Request{NamespacedName: types.NamespacedName{Name: parentInferenceService, Namespace: namespace}} + serviceKey := expectedRequest.NamespacedName ctx := context.Background() isvc := &v1beta1.InferenceService{ ObjectMeta: metav1.ObjectMeta{ @@ -130,14 +132,14 @@ var _ = Describe("v1beta1 TrainedModel controller", func() { } Expect(k8sClient.Create(ctx, isvc)).Should(Succeed()) - tmInstance := &v1alpha1api.TrainedModel{ + tmInstance := &v1alpha1.TrainedModel{ ObjectMeta: metav1.ObjectMeta{ Name: modelName, Namespace: namespace, }, - Spec: v1alpha1api.TrainedModelSpec{ + Spec: v1alpha1.TrainedModelSpec{ InferenceService: parentInferenceService, - Model: v1alpha1api.ModelSpec{ + Model: v1alpha1.ModelSpec{ StorageURI: storageUri, Framework: framework, Memory: memory, @@ -149,19 +151,19 @@ var _ = Describe("v1beta1 TrainedModel controller", func() { defer k8sClient.Delete(context.TODO(), tmInstance) Eventually(func() bool { - tmInstanceUpdate := &v1alpha1api.TrainedModel{} + tmInstanceUpdate := &v1alpha1.TrainedModel{} if err := k8sClient.Get(context.TODO(), tmKey, tmInstanceUpdate); err != nil { return false } // Condition for inferenceserviceready should be false as isvc is not ready - isvcReadyCondition := tmInstanceUpdate.Status.GetCondition(v1alpha1api.InferenceServiceReady) + isvcReadyCondition := tmInstanceUpdate.Status.GetCondition(v1alpha1.InferenceServiceReady) // Condition for IsMMSPredictor should be false as isvc is not ready - isMMSPredictorCondition := tmInstanceUpdate.Status.GetCondition(v1alpha1api.IsMMSPredictor) + isMMSPredictorCondition := tmInstanceUpdate.Status.GetCondition(v1alpha1.IsMMSPredictor) - if isvcReadyCondition != nil && isvcReadyCondition.Status == v1.ConditionFalse { - return isMMSPredictorCondition != nil && isMMSPredictorCondition.Status == v1.ConditionFalse + if isvcReadyCondition != nil && isvcReadyCondition.Status == corev1.ConditionFalse { + return isMMSPredictorCondition != nil && isMMSPredictorCondition.Status == corev1.ConditionFalse } return false @@ -178,7 +180,7 @@ var _ = Describe("v1beta1 TrainedModel controller", func() { tmKey := types.NamespacedName{Name: modelName, Namespace: namespace} // Create InferenceService configmap - var configMap = &v1.ConfigMap{ + configMap := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: constants.InferenceServiceConfigMapName, Namespace: constants.KServeNamespace, @@ -189,8 +191,8 @@ var _ = Describe("v1beta1 TrainedModel controller", func() { defer k8sClient.Delete(context.TODO(), configMap) // Create the parent InferenceService - var expectedRequest = reconcile.Request{NamespacedName: types.NamespacedName{Name: parentInferenceService, Namespace: namespace}} - var serviceKey = expectedRequest.NamespacedName + expectedRequest := reconcile.Request{NamespacedName: types.NamespacedName{Name: parentInferenceService, Namespace: namespace}} + serviceKey := expectedRequest.NamespacedName ctx := context.Background() isvc := &v1beta1.InferenceService{ ObjectMeta: metav1.ObjectMeta{ @@ -200,13 +202,13 @@ var _ = Describe("v1beta1 TrainedModel controller", func() { Spec: v1beta1.InferenceServiceSpec{ Predictor: v1beta1.PredictorSpec{ ComponentExtensionSpec: v1beta1.ComponentExtensionSpec{ - MinReplicas: v1beta1.GetIntReference(1), + MinReplicas: ptr.To(int32(1)), MaxReplicas: 3, }, Tensorflow: &v1beta1.TFServingSpec{ PredictorExtensionSpec: v1beta1.PredictorExtensionSpec{ RuntimeVersion: proto.String("1.14.0"), - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, Resources: defaultResource, }, @@ -220,18 +222,15 @@ var _ = Describe("v1beta1 TrainedModel controller", func() { inferenceService := &v1beta1.InferenceService{} Eventually(func() bool { err := k8sClient.Get(ctx, serviceKey, inferenceService) - if err != nil { - return false - } - return true + return err == nil }, timeout, interval).Should(BeTrue()) inferenceService.Status.Status = readyConditions inferenceService.Status.ModelStatus = modelStatus - Expect(k8sClient.Status().Update(context.TODO(), inferenceService)).To(BeNil()) + Expect(k8sClient.Status().Update(context.TODO(), inferenceService)).To(Succeed()) // Create modelConfig - modelConfig := &v1.ConfigMap{ + modelConfig := &corev1.ConfigMap{ TypeMeta: metav1.TypeMeta{Kind: "ConfigMap", APIVersion: "v1"}, ObjectMeta: metav1.ObjectMeta{Name: modelConfigName, Namespace: namespace}, Data: map[string]string{ @@ -239,14 +238,14 @@ var _ = Describe("v1beta1 TrainedModel controller", func() { }, } - tmInstance := &v1alpha1api.TrainedModel{ + tmInstance := &v1alpha1.TrainedModel{ ObjectMeta: metav1.ObjectMeta{ Name: modelName, Namespace: namespace, }, - Spec: v1alpha1api.TrainedModelSpec{ + Spec: v1alpha1.TrainedModelSpec{ InferenceService: parentInferenceService, - Model: v1alpha1api.ModelSpec{ + Model: v1alpha1.ModelSpec{ StorageURI: storageUri, Framework: framework, Memory: memory, @@ -260,9 +259,9 @@ var _ = Describe("v1beta1 TrainedModel controller", func() { defer k8sClient.Delete(context.TODO(), tmInstance) // Verify that the model configmap is updated with the TrainedModel - configmapActual := &v1.ConfigMap{} - tmActual := &v1alpha1api.TrainedModel{} - expected := &v1.ConfigMap{ + configmapActual := &corev1.ConfigMap{} + tmActual := &v1alpha1.TrainedModel{} + expected := &corev1.ConfigMap{ TypeMeta: metav1.TypeMeta{Kind: "ConfigMap", APIVersion: "v1"}, ObjectMeta: metav1.ObjectMeta{Name: modelConfigName, Namespace: namespace}, Data: map[string]string{ @@ -287,7 +286,7 @@ var _ = Describe("v1beta1 TrainedModel controller", func() { tmKey := types.NamespacedName{Name: modelName, Namespace: namespace} // Create InferenceService configmap - var configMap = &v1.ConfigMap{ + configMap := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: constants.InferenceServiceConfigMapName, Namespace: constants.KServeNamespace, @@ -298,8 +297,8 @@ var _ = Describe("v1beta1 TrainedModel controller", func() { defer k8sClient.Delete(context.TODO(), configMap) // Create the parent InferenceService - var expectedRequest = reconcile.Request{NamespacedName: types.NamespacedName{Name: parentInferenceService, Namespace: namespace}} - var serviceKey = expectedRequest.NamespacedName + expectedRequest := reconcile.Request{NamespacedName: types.NamespacedName{Name: parentInferenceService, Namespace: namespace}} + serviceKey := expectedRequest.NamespacedName ctx := context.Background() isvc := &v1beta1.InferenceService{ ObjectMeta: metav1.ObjectMeta{ @@ -309,13 +308,13 @@ var _ = Describe("v1beta1 TrainedModel controller", func() { Spec: v1beta1.InferenceServiceSpec{ Predictor: v1beta1.PredictorSpec{ ComponentExtensionSpec: v1beta1.ComponentExtensionSpec{ - MinReplicas: v1beta1.GetIntReference(1), + MinReplicas: ptr.To(int32(1)), MaxReplicas: 3, }, Tensorflow: &v1beta1.TFServingSpec{ PredictorExtensionSpec: v1beta1.PredictorExtensionSpec{ RuntimeVersion: proto.String("1.14.0"), - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, Resources: defaultResource, }, @@ -329,10 +328,7 @@ var _ = Describe("v1beta1 TrainedModel controller", func() { inferenceService := &v1beta1.InferenceService{} Eventually(func() bool { err := k8sClient.Get(ctx, serviceKey, inferenceService) - if err != nil { - return false - } - return true + return err == nil }, timeout, interval).Should(BeTrue()) // Updates the url and address of inference service status @@ -344,16 +340,16 @@ var _ = Describe("v1beta1 TrainedModel controller", func() { } inferenceService.Status.Status = readyConditions inferenceService.Status.ModelStatus = modelStatus - Expect(k8sClient.Status().Update(context.TODO(), inferenceService)).To(BeNil()) + Expect(k8sClient.Status().Update(context.TODO(), inferenceService)).To(Succeed()) - tmInstance := &v1alpha1api.TrainedModel{ + tmInstance := &v1alpha1.TrainedModel{ ObjectMeta: metav1.ObjectMeta{ Name: modelName, Namespace: namespace, }, - Spec: v1alpha1api.TrainedModelSpec{ + Spec: v1alpha1.TrainedModelSpec{ InferenceService: parentInferenceService, - Model: v1alpha1api.ModelSpec{ + Model: v1alpha1.ModelSpec{ StorageURI: storageUri, Framework: framework, Memory: memory, @@ -361,7 +357,7 @@ var _ = Describe("v1beta1 TrainedModel controller", func() { }, } - modelConfig := &v1.ConfigMap{ + modelConfig := &corev1.ConfigMap{ TypeMeta: metav1.TypeMeta{Kind: "ConfigMap", APIVersion: "v1"}, ObjectMeta: metav1.ObjectMeta{Name: modelConfigName, Namespace: namespace}, Data: map[string]string{ @@ -373,24 +369,24 @@ var _ = Describe("v1beta1 TrainedModel controller", func() { defer k8sClient.Delete(context.TODO(), modelConfig) Expect(k8sClient.Create(context.TODO(), tmInstance)).NotTo(HaveOccurred()) defer k8sClient.Delete(context.TODO(), tmInstance) - tmInstanceUpdate := &v1alpha1api.TrainedModel{} + tmInstanceUpdate := &v1alpha1.TrainedModel{} Eventually(func() bool { if err := k8sClient.Get(context.TODO(), tmKey, tmInstanceUpdate); err != nil { return false } // Condition for inferenceserviceready should be true - if !tmInstanceUpdate.Status.IsConditionReady(v1alpha1api.InferenceServiceReady) { + if !tmInstanceUpdate.Status.IsConditionReady(v1alpha1.InferenceServiceReady) { return false } // Condition for IsMMSPredictor should be true - if !tmInstanceUpdate.Status.IsConditionReady(v1alpha1api.IsMMSPredictor) { + if !tmInstanceUpdate.Status.IsConditionReady(v1alpha1.IsMMSPredictor) { return false } // Condition for MemoryResourceAvailable should be true - if !tmInstanceUpdate.Status.IsConditionReady(v1alpha1api.MemoryResourceAvailable) { + if !tmInstanceUpdate.Status.IsConditionReady(v1alpha1.MemoryResourceAvailable) { return false } @@ -408,9 +404,9 @@ var _ = Describe("v1beta1 TrainedModel controller", func() { defer k8sClient.Delete(context.TODO(), tmInstanceUpdate) // Verify that the model configmap is updated with the TrainedModel - configmapActual := &v1.ConfigMap{} - tmActual := &v1alpha1api.TrainedModel{} - expected := &v1.ConfigMap{ + configmapActual := &corev1.ConfigMap{} + tmActual := &v1alpha1.TrainedModel{} + expected := &corev1.ConfigMap{ TypeMeta: metav1.TypeMeta{Kind: "ConfigMap", APIVersion: "v1"}, ObjectMeta: metav1.ObjectMeta{Name: modelConfigName, Namespace: namespace}, Data: map[string]string{ @@ -437,7 +433,7 @@ var _ = Describe("v1beta1 TrainedModel controller", func() { tmKey := types.NamespacedName{Name: modelName, Namespace: namespace} // Create InferenceService configmap - var configMap = &v1.ConfigMap{ + configMap := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: constants.InferenceServiceConfigMapName, Namespace: constants.KServeNamespace, @@ -448,8 +444,8 @@ var _ = Describe("v1beta1 TrainedModel controller", func() { defer k8sClient.Delete(context.TODO(), configMap) // Create the parent InferenceService - var expectedRequest = reconcile.Request{NamespacedName: types.NamespacedName{Name: parentInferenceService, Namespace: namespace}} - var serviceKey = expectedRequest.NamespacedName + expectedRequest := reconcile.Request{NamespacedName: types.NamespacedName{Name: parentInferenceService, Namespace: namespace}} + serviceKey := expectedRequest.NamespacedName ctx := context.Background() isvc := &v1beta1.InferenceService{ ObjectMeta: metav1.ObjectMeta{ @@ -459,13 +455,13 @@ var _ = Describe("v1beta1 TrainedModel controller", func() { Spec: v1beta1.InferenceServiceSpec{ Predictor: v1beta1.PredictorSpec{ ComponentExtensionSpec: v1beta1.ComponentExtensionSpec{ - MinReplicas: v1beta1.GetIntReference(1), + MinReplicas: ptr.To(int32(1)), MaxReplicas: 3, }, Tensorflow: &v1beta1.TFServingSpec{ PredictorExtensionSpec: v1beta1.PredictorExtensionSpec{ RuntimeVersion: proto.String("1.14.0"), - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, Resources: defaultResource, }, @@ -479,24 +475,21 @@ var _ = Describe("v1beta1 TrainedModel controller", func() { inferenceService := &v1beta1.InferenceService{} Eventually(func() bool { err := k8sClient.Get(ctx, serviceKey, inferenceService) - if err != nil { - return false - } - return true + return err == nil }, timeout, interval).Should(BeTrue()) inferenceService.Status.Status = readyConditions inferenceService.Status.ModelStatus = modelStatus - Expect(k8sClient.Status().Update(context.TODO(), inferenceService)).To(BeNil()) + Expect(k8sClient.Status().Update(context.TODO(), inferenceService)).To(Succeed()) - tmInstance := &v1alpha1api.TrainedModel{ + tmInstance := &v1alpha1.TrainedModel{ ObjectMeta: metav1.ObjectMeta{ Name: modelName, Namespace: namespace, }, - Spec: v1alpha1api.TrainedModelSpec{ + Spec: v1alpha1.TrainedModelSpec{ InferenceService: parentInferenceService, - Model: v1alpha1api.ModelSpec{ + Model: v1alpha1.ModelSpec{ StorageURI: storageUri, Framework: framework, Memory: memory, @@ -504,7 +497,7 @@ var _ = Describe("v1beta1 TrainedModel controller", func() { }, } - modelConfig := &v1.ConfigMap{ + modelConfig := &corev1.ConfigMap{ TypeMeta: metav1.TypeMeta{Kind: "ConfigMap", APIVersion: "v1"}, ObjectMeta: metav1.ObjectMeta{Name: modelConfigName, Namespace: namespace}, Data: map[string]string{ @@ -516,11 +509,11 @@ var _ = Describe("v1beta1 TrainedModel controller", func() { defer k8sClient.Delete(context.TODO(), modelConfig) Expect(k8sClient.Create(context.TODO(), tmInstance)).NotTo(HaveOccurred()) defer k8sClient.Delete(context.TODO(), tmInstance) - //tmInstanceUpdate := &v1beta1.TrainedModel{} - //Verify that the model configmap is updated with the new TrainedModel - configmapActual := &v1.ConfigMap{} - tmActual := &v1alpha1api.TrainedModel{} - expected := &v1.ConfigMap{ + // tmInstanceUpdate := &v1beta1.TrainedModel{} + // Verify that the model configmap is updated with the new TrainedModel + configmapActual := &corev1.ConfigMap{} + tmActual := &v1alpha1.TrainedModel{} + expected := &corev1.ConfigMap{ TypeMeta: metav1.TypeMeta{Kind: "ConfigMap", APIVersion: "v1"}, ObjectMeta: metav1.ObjectMeta{Name: modelConfigName, Namespace: namespace}, Data: map[string]string{ @@ -539,9 +532,9 @@ var _ = Describe("v1beta1 TrainedModel controller", func() { defer k8sClient.Delete(context.TODO(), tmActual) // Verify that the model is removed from the configmap - configmapActual = &v1.ConfigMap{} - tmActual = &v1alpha1api.TrainedModel{} - expected = &v1.ConfigMap{ + configmapActual = &corev1.ConfigMap{} + tmActual = &v1alpha1.TrainedModel{} + expected = &corev1.ConfigMap{ TypeMeta: metav1.TypeMeta{Kind: "ConfigMap", APIVersion: "v1"}, ObjectMeta: metav1.ObjectMeta{Name: modelConfigName, Namespace: namespace}, Data: map[string]string{ @@ -566,7 +559,7 @@ var _ = Describe("v1beta1 TrainedModel controller", func() { tmKey := types.NamespacedName{Name: modelName, Namespace: namespace} // Create InferenceService configmap - var configMap = &v1.ConfigMap{ + configMap := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: constants.InferenceServiceConfigMapName, Namespace: constants.KServeNamespace, @@ -577,8 +570,8 @@ var _ = Describe("v1beta1 TrainedModel controller", func() { defer k8sClient.Delete(context.TODO(), configMap) // Create the parent InferenceService - var expectedRequest = reconcile.Request{NamespacedName: types.NamespacedName{Name: parentInferenceService, Namespace: namespace}} - var serviceKey = expectedRequest.NamespacedName + expectedRequest := reconcile.Request{NamespacedName: types.NamespacedName{Name: parentInferenceService, Namespace: namespace}} + serviceKey := expectedRequest.NamespacedName ctx := context.Background() isvc := &v1beta1.InferenceService{ ObjectMeta: metav1.ObjectMeta{ @@ -588,13 +581,13 @@ var _ = Describe("v1beta1 TrainedModel controller", func() { Spec: v1beta1.InferenceServiceSpec{ Predictor: v1beta1.PredictorSpec{ ComponentExtensionSpec: v1beta1.ComponentExtensionSpec{ - MinReplicas: v1beta1.GetIntReference(1), + MinReplicas: ptr.To(int32(1)), MaxReplicas: 3, }, Tensorflow: &v1beta1.TFServingSpec{ PredictorExtensionSpec: v1beta1.PredictorExtensionSpec{ RuntimeVersion: proto.String("1.14.0"), - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, Resources: defaultResource, }, @@ -608,18 +601,15 @@ var _ = Describe("v1beta1 TrainedModel controller", func() { inferenceService := &v1beta1.InferenceService{} Eventually(func() bool { err := k8sClient.Get(ctx, serviceKey, inferenceService) - if err != nil { - return false - } - return true + return err == nil }, timeout, interval).Should(BeTrue()) inferenceService.Status.Status = readyConditions inferenceService.Status.ModelStatus = modelStatus - Expect(k8sClient.Status().Update(context.TODO(), inferenceService)).To(BeNil()) + Expect(k8sClient.Status().Update(context.TODO(), inferenceService)).To(Succeed()) // Create modelConfig - modelConfig := &v1.ConfigMap{ + modelConfig := &corev1.ConfigMap{ TypeMeta: metav1.TypeMeta{Kind: "ConfigMap", APIVersion: "v1"}, ObjectMeta: metav1.ObjectMeta{Name: modelConfigName, Namespace: namespace}, Data: map[string]string{ @@ -627,14 +617,14 @@ var _ = Describe("v1beta1 TrainedModel controller", func() { }, } - tmInstance := &v1alpha1api.TrainedModel{ + tmInstance := &v1alpha1.TrainedModel{ ObjectMeta: metav1.ObjectMeta{ Name: modelName, Namespace: namespace, }, - Spec: v1alpha1api.TrainedModelSpec{ + Spec: v1alpha1.TrainedModelSpec{ InferenceService: parentInferenceService, - Model: v1alpha1api.ModelSpec{ + Model: v1alpha1.ModelSpec{ StorageURI: storageUri, Framework: framework, Memory: resource.MustParse("3Gi"), @@ -648,30 +638,29 @@ var _ = Describe("v1beta1 TrainedModel controller", func() { defer k8sClient.Delete(context.TODO(), tmInstance) Eventually(func() bool { - tmInstanceUpdate := &v1alpha1api.TrainedModel{} + tmInstanceUpdate := &v1alpha1.TrainedModel{} if err := k8sClient.Get(context.TODO(), tmKey, tmInstanceUpdate); err != nil { return false } // Condition for inferenceserviceready should be true - if !tmInstanceUpdate.Status.IsConditionReady(v1alpha1api.InferenceServiceReady) { + if !tmInstanceUpdate.Status.IsConditionReady(v1alpha1.InferenceServiceReady) { return false } // Condition for IsMMSPredictor should be true - if !tmInstanceUpdate.Status.IsConditionReady(v1alpha1api.IsMMSPredictor) { + if !tmInstanceUpdate.Status.IsConditionReady(v1alpha1.IsMMSPredictor) { return false } // Condition for MemoryResourceAvailable should be false - return !tmInstanceUpdate.Status.IsConditionReady(v1alpha1api.MemoryResourceAvailable) - + return !tmInstanceUpdate.Status.IsConditionReady(v1alpha1.MemoryResourceAvailable) }, timeout).Should(BeTrue()) // Verify that the model configmap is updated with the TrainedModel - configmapActual := &v1.ConfigMap{} - tmActual := &v1alpha1api.TrainedModel{} - expected := &v1.ConfigMap{ + configmapActual := &corev1.ConfigMap{} + tmActual := &v1alpha1.TrainedModel{} + expected := &corev1.ConfigMap{ TypeMeta: metav1.TypeMeta{Kind: "ConfigMap", APIVersion: "v1"}, ObjectMeta: metav1.ObjectMeta{Name: modelConfigName, Namespace: namespace}, Data: map[string]string{ @@ -696,7 +685,7 @@ var _ = Describe("v1beta1 TrainedModel controller", func() { tmKey := types.NamespacedName{Name: modelName, Namespace: namespace} // Create InferenceService configmap - var configMap = &v1.ConfigMap{ + configMap := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: constants.InferenceServiceConfigMapName, Namespace: constants.KServeNamespace, @@ -707,8 +696,8 @@ var _ = Describe("v1beta1 TrainedModel controller", func() { defer k8sClient.Delete(context.TODO(), configMap) // Create the parent InferenceService - var expectedRequest = reconcile.Request{NamespacedName: types.NamespacedName{Name: parentInferenceService, Namespace: namespace}} - var serviceKey = expectedRequest.NamespacedName + expectedRequest := reconcile.Request{NamespacedName: types.NamespacedName{Name: parentInferenceService, Namespace: namespace}} + serviceKey := expectedRequest.NamespacedName ctx := context.Background() isvc := &v1beta1.InferenceService{ ObjectMeta: metav1.ObjectMeta{ @@ -718,13 +707,13 @@ var _ = Describe("v1beta1 TrainedModel controller", func() { Spec: v1beta1.InferenceServiceSpec{ Predictor: v1beta1.PredictorSpec{ ComponentExtensionSpec: v1beta1.ComponentExtensionSpec{ - MinReplicas: v1beta1.GetIntReference(1), + MinReplicas: ptr.To(int32(1)), MaxReplicas: 3, }, Tensorflow: &v1beta1.TFServingSpec{ PredictorExtensionSpec: v1beta1.PredictorExtensionSpec{ RuntimeVersion: proto.String("1.14.0"), - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, Resources: defaultResource, }, @@ -739,18 +728,15 @@ var _ = Describe("v1beta1 TrainedModel controller", func() { inferenceService := &v1beta1.InferenceService{} Eventually(func() bool { err := k8sClient.Get(ctx, serviceKey, inferenceService) - if err != nil { - return false - } - return true + return err == nil }, timeout, interval).Should(BeTrue()) inferenceService.Status.Status = readyConditions inferenceService.Status.ModelStatus = modelStatus - Expect(k8sClient.Status().Update(context.TODO(), inferenceService)).To(BeNil()) + Expect(k8sClient.Status().Update(context.TODO(), inferenceService)).To(Succeed()) // Create modelConfig - modelConfig := &v1.ConfigMap{ + modelConfig := &corev1.ConfigMap{ TypeMeta: metav1.TypeMeta{Kind: "ConfigMap", APIVersion: "v1"}, ObjectMeta: metav1.ObjectMeta{Name: modelConfigName, Namespace: namespace}, Data: map[string]string{ @@ -758,14 +744,14 @@ var _ = Describe("v1beta1 TrainedModel controller", func() { }, } - tmInstance := &v1alpha1api.TrainedModel{ + tmInstance := &v1alpha1.TrainedModel{ ObjectMeta: metav1.ObjectMeta{ Name: modelName, Namespace: namespace, }, - Spec: v1alpha1api.TrainedModelSpec{ + Spec: v1alpha1.TrainedModelSpec{ InferenceService: parentInferenceService, - Model: v1alpha1api.ModelSpec{ + Model: v1alpha1.ModelSpec{ StorageURI: storageUri, Framework: framework, Memory: memory, @@ -779,21 +765,19 @@ var _ = Describe("v1beta1 TrainedModel controller", func() { defer k8sClient.Delete(context.TODO(), tmInstance) Eventually(func() bool { - tmInstanceUpdate := &v1alpha1api.TrainedModel{} + tmInstanceUpdate := &v1alpha1.TrainedModel{} if err := k8sClient.Get(context.TODO(), tmKey, tmInstanceUpdate); err != nil { return false } // Condition for inferenceserviceready should be true - if !tmInstanceUpdate.Status.IsConditionReady(v1alpha1api.InferenceServiceReady) { + if !tmInstanceUpdate.Status.IsConditionReady(v1alpha1.InferenceServiceReady) { return false } // Condition for IsMMSPredictor should be true - return !tmInstanceUpdate.Status.IsConditionReady(v1alpha1api.IsMMSPredictor) - + return !tmInstanceUpdate.Status.IsConditionReady(v1alpha1.IsMMSPredictor) }, timeout).Should(BeTrue()) - }) }) }) diff --git a/pkg/controller/v1alpha1/trainedmodel/reconcilers/modelconfig/modelconfig_reconciler.go b/pkg/controller/v1alpha1/trainedmodel/reconcilers/modelconfig/modelconfig_reconciler.go index 1f1d009b0ed..a493ca00099 100644 --- a/pkg/controller/v1alpha1/trainedmodel/reconcilers/modelconfig/modelconfig_reconciler.go +++ b/pkg/controller/v1alpha1/trainedmodel/reconcilers/modelconfig/modelconfig_reconciler.go @@ -27,7 +27,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" logf "sigs.k8s.io/controller-runtime/pkg/log" - v1alpha1api "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" + "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" "github.com/kserve/kserve/pkg/constants" "github.com/kserve/kserve/pkg/controller/v1alpha1/trainedmodel/sharding/memory" "github.com/kserve/kserve/pkg/modelconfig" @@ -49,14 +49,14 @@ func NewModelConfigReconciler(client client.Client, clientset kubernetes.Interfa } } -func (c *ModelConfigReconciler) Reconcile(req ctrl.Request, tm *v1alpha1api.TrainedModel) error { +func (c *ModelConfigReconciler) Reconcile(ctx context.Context, req ctrl.Request, tm *v1alpha1.TrainedModel) error { log.Info("Reconciling TrainedModel", "apiVersion", tm.APIVersion, "trainedmodel", tm.Spec) shardStrategy := memory.MemoryStrategy{} shardId := shardStrategy.GetOrAssignShard(tm) // Use tm's parent InferenceService field to get the model modelConfig modelConfigName := constants.ModelConfigName(tm.Spec.InferenceService, shardId) log.Info("Reconciling modelConfig", "modelConfigName", modelConfigName, "namespace", req.Namespace) - desiredModelConfig, err := c.clientset.CoreV1().ConfigMaps(req.Namespace).Get(context.TODO(), modelConfigName, metav1.GetOptions{}) + desiredModelConfig, err := c.clientset.CoreV1().ConfigMaps(req.Namespace).Get(ctx, modelConfigName, metav1.GetOptions{}) if err != nil { log.Error(err, "Failed to find model ConfigMap to reconcile for InferenceService", "name", tm.Spec.Model, "namespace", req.Namespace) // Error reading the object - requeue the request. @@ -71,7 +71,7 @@ func (c *ModelConfigReconciler) Reconcile(req ctrl.Request, tm *v1alpha1api.Trai return fmt.Errorf("Can not remove model %v from config because of error %w", tm.Name, err) } // Update the model Config created by the InferenceService controller - err = c.client.Update(context.TODO(), desiredModelConfig) + err = c.client.Update(ctx, desiredModelConfig) if err != nil { return err } @@ -85,7 +85,7 @@ func (c *ModelConfigReconciler) Reconcile(req ctrl.Request, tm *v1alpha1api.Trai return fmt.Errorf("Can not add or update a model %v from config because of error %w", tm.Name, err) } // Update the model Config created by the InferenceService controller - err = c.client.Update(context.TODO(), desiredModelConfig) + err = c.client.Update(ctx, desiredModelConfig) if err != nil { return err } diff --git a/pkg/controller/v1alpha1/trainedmodel/sharding/memory/strategy.go b/pkg/controller/v1alpha1/trainedmodel/sharding/memory/strategy.go index 6c3c9444fd2..4453009ed0b 100644 --- a/pkg/controller/v1alpha1/trainedmodel/sharding/memory/strategy.go +++ b/pkg/controller/v1alpha1/trainedmodel/sharding/memory/strategy.go @@ -17,22 +17,21 @@ limitations under the License. package memory import ( - v1alpha1api "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" - v1beta1api "github.com/kserve/kserve/pkg/apis/serving/v1beta1" + "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" + "github.com/kserve/kserve/pkg/apis/serving/v1beta1" ) // TODO MemoryStrategy will be implemented in another PR -type MemoryStrategy struct { -} +type MemoryStrategy struct{} // Return a TrainedModel's shardId -func (v *MemoryStrategy) GetOrAssignShard(tm *v1alpha1api.TrainedModel) int { +func (v *MemoryStrategy) GetOrAssignShard(tm *v1alpha1.TrainedModel) int { // TODO to be implemented in another PR // Currently each InferenceService only has one shard with id=0 return 0 } -func (v *MemoryStrategy) GetShard(isvc *v1beta1api.InferenceService) []int { +func (v *MemoryStrategy) GetShard(isvc *v1beta1.InferenceService) []int { // TODO to be implemented in another PR // Currently each InferenceService only has one shard with id=0 return []int{0} diff --git a/pkg/controller/v1alpha1/trainedmodel/sharding/strategy.go b/pkg/controller/v1alpha1/trainedmodel/sharding/strategy.go index 15b3fd1f7fc..1649829f95f 100644 --- a/pkg/controller/v1alpha1/trainedmodel/sharding/strategy.go +++ b/pkg/controller/v1alpha1/trainedmodel/sharding/strategy.go @@ -16,7 +16,7 @@ limitations under the License. package main -import v1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" +import "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" type Strategy interface { GetOrAssignShard(trainedModel *v1alpha1.TrainedModel) int diff --git a/pkg/controller/v1alpha1/trainedmodel/suite_test.go b/pkg/controller/v1alpha1/trainedmodel/suite_test.go index ebc47369b21..07b43e6899b 100644 --- a/pkg/controller/v1alpha1/trainedmodel/suite_test.go +++ b/pkg/controller/v1alpha1/trainedmodel/suite_test.go @@ -23,7 +23,7 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes/scheme" @@ -32,13 +32,12 @@ import ( knservingv1 "knative.dev/serving/pkg/apis/serving/v1" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/envtest" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/log/zap" metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" - kfservingv1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" - kfservingv1beta1 "github.com/kserve/kserve/pkg/apis/serving/v1beta1" + "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" + "github.com/kserve/kserve/pkg/apis/serving/v1beta1" "github.com/kserve/kserve/pkg/constants" "github.com/kserve/kserve/pkg/controller/v1alpha1/trainedmodel/reconcilers/modelconfig" pkgtest "github.com/kserve/kserve/pkg/testing" @@ -51,10 +50,6 @@ import ( var ( cfg *rest.Config k8sClient client.Client - testEnv *envtest.Environment - cancel context.CancelFunc - ctx context.Context - clientset kubernetes.Interface ) func TestAPIs(t *testing.T) { @@ -65,41 +60,36 @@ func TestAPIs(t *testing.T) { var _ = BeforeSuite(func() { logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) - ctx, cancel = context.WithCancel(context.TODO()) + ctx, cancel := context.WithCancel(context.TODO()) By("bootstrapping test environment") crdDirectoryPaths := []string{ filepath.Join("..", "..", "..", "..", "test", "crds"), } - testEnv = pkgtest.SetupEnvTest(crdDirectoryPaths) + testEnv := pkgtest.SetupEnvTest(crdDirectoryPaths) var err error cfg, err = testEnv.Start() Expect(err).ToNot(HaveOccurred()) Expect(cfg).ToNot(BeNil()) - err = kfservingv1alpha1.AddToScheme(scheme.Scheme) + DeferCleanup(func() { + cancel() + By("tearing down the test environment") + err := testEnv.Stop() + Expect(err).ToNot(HaveOccurred()) + }) + + err = v1alpha1.AddToScheme(scheme.Scheme) Expect(err).NotTo(HaveOccurred()) - err = kfservingv1beta1.AddToScheme(scheme.Scheme) + err = v1beta1.AddToScheme(scheme.Scheme) Expect(err).NotTo(HaveOccurred()) err = knservingv1.AddToScheme(scheme.Scheme) Expect(err).NotTo(HaveOccurred()) // +kubebuilder:scaffold:scheme - k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) - Expect(err).ToNot(HaveOccurred()) - Expect(k8sClient).ToNot(BeNil()) - - clientset, err = kubernetes.NewForConfig(cfg) + clientset, err := kubernetes.NewForConfig(cfg) Expect(err).ToNot(HaveOccurred()) Expect(clientset).ToNot(BeNil()) - //Create namespace - kfservingNamespaceObj := &v1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: constants.KServeNamespace, - }, - } - Expect(k8sClient.Create(context.Background(), kfservingNamespaceObj)).Should(Succeed()) - k8sManager, err := ctrl.NewManager(cfg, ctrl.Options{ Scheme: scheme.Scheme, Metrics: metricsserver.Options{ @@ -107,12 +97,24 @@ var _ = BeforeSuite(func() { }, }) Expect(err).ToNot(HaveOccurred()) + + k8sClient = k8sManager.GetClient() + Expect(k8sClient).ToNot(BeNil()) + + // Create namespace + kserveNamespaceObj := &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: constants.KServeNamespace, + }, + } + Expect(k8sClient.Create(context.Background(), kserveNamespaceObj)).Should(Succeed()) + err = (&TrainedModelReconciler{ Client: k8sManager.GetClient(), Clientset: clientset, Scheme: scheme.Scheme, Log: ctrl.Log.WithName("v1beta1TrainedModelController"), - Recorder: record.NewBroadcaster().NewRecorder(scheme.Scheme, v1.EventSource{Component: "v1betaController"}), + Recorder: record.NewBroadcaster().NewRecorder(scheme.Scheme, corev1.EventSource{Component: "v1betaController"}), ModelConfigReconciler: modelconfig.NewModelConfigReconciler(k8sManager.GetClient(), clientset, scheme.Scheme), }).SetupWithManager(k8sManager) Expect(err).ToNot(HaveOccurred()) @@ -121,14 +123,4 @@ var _ = BeforeSuite(func() { err = k8sManager.Start(ctx) Expect(err).ToNot(HaveOccurred()) }() - - k8sClient = k8sManager.GetClient() - Expect(k8sClient).ToNot(BeNil()) -}) - -var _ = AfterSuite(func() { - cancel() - By("tearing down the test environment") - err := testEnv.Stop() - Expect(err).ToNot(HaveOccurred()) }) diff --git a/pkg/controller/v1alpha1/utils/utils.go b/pkg/controller/v1alpha1/utils/utils.go new file mode 100644 index 00000000000..4ed35a75481 --- /dev/null +++ b/pkg/controller/v1alpha1/utils/utils.go @@ -0,0 +1,32 @@ +/* +Copyright 2021 The KServe Authors. + +Licensed 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. +*/ + +package utils + +import ( + corev1 "k8s.io/api/core/v1" + corev1helpers "k8s.io/component-helpers/scheduling/corev1" +) + +// CheckNodeAffinity returns true if the node matches the node affinity specified in the PV Spec +func CheckNodeAffinity(pvSpec *corev1.PersistentVolumeSpec, node corev1.Node) (bool, error) { + if pvSpec.NodeAffinity == nil || pvSpec.NodeAffinity.Required == nil { + return false, nil + } + + terms := pvSpec.NodeAffinity.Required + return corev1helpers.MatchNodeSelectorTerms(&node, terms) +} diff --git a/pkg/controller/v1beta1/inferenceservice/components/component.go b/pkg/controller/v1beta1/inferenceservice/components/component.go index 4203ec7b298..7f987445697 100644 --- a/pkg/controller/v1beta1/inferenceservice/components/component.go +++ b/pkg/controller/v1beta1/inferenceservice/components/component.go @@ -17,22 +17,24 @@ limitations under the License. package components import ( + "context" "encoding/json" "fmt" "strconv" "strings" + ctrl "sigs.k8s.io/controller-runtime" + "github.com/kserve/kserve/pkg/apis/serving/v1beta1" "github.com/kserve/kserve/pkg/constants" "github.com/kserve/kserve/pkg/controller/v1alpha1/trainedmodel/sharding/memory" v1beta1utils "github.com/kserve/kserve/pkg/controller/v1beta1/inferenceservice/utils" "github.com/kserve/kserve/pkg/credentials" - ctrl "sigs.k8s.io/controller-runtime" ) // Component can be reconciled to create underlying resources for an InferenceService type Component interface { - Reconcile(isvc *v1beta1.InferenceService) (ctrl.Result, error) + Reconcile(ctx context.Context, isvc *v1beta1.InferenceService) (ctrl.Result, error) } func addStorageSpecAnnotations(storageSpec *v1beta1.StorageSpec, annotations map[string]string) bool { @@ -49,9 +51,8 @@ func addStorageSpecAnnotations(storageSpec *v1beta1.StorageSpec, annotations map annotations[constants.StorageSpecKeyAnnotationKey] = *storageSpec.StorageKey } if storageSpec.Path != nil { - annotations[constants.StorageInitializerSourceUriInternalAnnotationKey] = - fmt.Sprintf("%s://%s", credentials.UriSchemePlaceholder, - strings.TrimPrefix(*storageSpec.Path, "/")) + annotations[constants.StorageInitializerSourceUriInternalAnnotationKey] = fmt.Sprintf("%s://%s", credentials.UriSchemePlaceholder, + strings.TrimPrefix(*storageSpec.Path, "/")) } return true } diff --git a/pkg/controller/v1beta1/inferenceservice/components/explainer.go b/pkg/controller/v1beta1/inferenceservice/components/explainer.go index 6e168070c45..9bba29b2900 100644 --- a/pkg/controller/v1beta1/inferenceservice/components/explainer.go +++ b/pkg/controller/v1beta1/inferenceservice/components/explainer.go @@ -22,7 +22,7 @@ import ( "github.com/go-logr/logr" "github.com/pkg/errors" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" @@ -36,7 +36,6 @@ import ( "github.com/kserve/kserve/pkg/controller/v1beta1/inferenceservice/reconcilers/knative" "github.com/kserve/kserve/pkg/controller/v1beta1/inferenceservice/reconcilers/raw" isvcutils "github.com/kserve/kserve/pkg/controller/v1beta1/inferenceservice/utils" - "github.com/kserve/kserve/pkg/credentials" "github.com/kserve/kserve/pkg/utils" "github.com/kserve/kserve/pkg/apis/serving/v1beta1" @@ -50,13 +49,13 @@ type Explainer struct { clientset kubernetes.Interface scheme *runtime.Scheme inferenceServiceConfig *v1beta1.InferenceServicesConfig - credentialBuilder *credentials.CredentialBuilder //nolint: unused deploymentMode constants.DeploymentModeType Log logr.Logger } func NewExplainer(client client.Client, clientset kubernetes.Interface, scheme *runtime.Scheme, - inferenceServiceConfig *v1beta1.InferenceServicesConfig, deploymentMode constants.DeploymentModeType) Component { + inferenceServiceConfig *v1beta1.InferenceServicesConfig, deploymentMode constants.DeploymentModeType, +) Component { return &Explainer{ client: client, clientset: clientset, @@ -68,17 +67,17 @@ func NewExplainer(client client.Client, clientset kubernetes.Interface, scheme * } // Reconcile observes the explainer and attempts to drive the status towards the desired state. -func (e *Explainer) Reconcile(isvc *v1beta1.InferenceService) (ctrl.Result, error) { +func (e *Explainer) Reconcile(ctx context.Context, isvc *v1beta1.InferenceService) (ctrl.Result, error) { e.Log.Info("Reconciling Explainer", "ExplainerSpec", isvc.Spec.Explainer) explainer := isvc.Spec.Explainer.GetImplementation() annotations := utils.Filter(isvc.Annotations, func(key string) bool { - return !utils.Includes(constants.ServiceAnnotationDisallowedList, key) + return !utils.Includes(e.inferenceServiceConfig.ServiceAnnotationDisallowedList, key) }) // KNative does not support INIT containers or mounting, so we add annotations that trigger the // StorageInitializer injector to mutate the underlying deployment to provision model data if sourceURI := explainer.GetStorageUri(); sourceURI != nil { annotations[constants.StorageInitializerSourceUriInternalAnnotationKey] = *sourceURI - err := isvcutils.ValidateStorageURI(sourceURI, e.client) + err := isvcutils.ValidateStorageURI(ctx, sourceURI, e.client) if err != nil { return ctrl.Result{}, fmt.Errorf("StorageURI not supported: %w", err) } @@ -88,15 +87,15 @@ func (e *Explainer) Reconcile(isvc *v1beta1.InferenceService) (ctrl.Result, erro explainerName := constants.ExplainerServiceName(isvc.Name) predictorName := constants.PredictorServiceName(isvc.Name) if e.deploymentMode == constants.RawDeployment { - existing := &v1.Service{} - err := e.client.Get(context.TODO(), types.NamespacedName{Name: constants.DefaultExplainerServiceName(isvc.Name), Namespace: isvc.Namespace}, existing) + existing := &corev1.Service{} + err := e.client.Get(ctx, types.NamespacedName{Name: constants.DefaultExplainerServiceName(isvc.Name), Namespace: isvc.Namespace}, existing) if err == nil { explainerName = constants.DefaultExplainerServiceName(isvc.Name) predictorName = constants.DefaultPredictorServiceName(isvc.Name) } } else { existing := &knservingv1.Service{} - err := e.client.Get(context.TODO(), types.NamespacedName{Name: constants.DefaultExplainerServiceName(isvc.Name), Namespace: isvc.Namespace}, existing) + err := e.client.Get(ctx, types.NamespacedName{Name: constants.DefaultExplainerServiceName(isvc.Name), Namespace: isvc.Namespace}, existing) if err == nil { explainerName = constants.DefaultExplainerServiceName(isvc.Name) predictorName = constants.DefaultPredictorServiceName(isvc.Name) @@ -107,7 +106,7 @@ func (e *Explainer) Reconcile(isvc *v1beta1.InferenceService) (ctrl.Result, erro // Label filter will be handled in ksvc_reconciler explainerLabels := isvc.Spec.Explainer.Labels explainerAnnotations := utils.Filter(isvc.Spec.Explainer.Annotations, func(key string) bool { - return !utils.Includes(constants.ServiceAnnotationDisallowedList, key) + return !utils.Includes(e.inferenceServiceConfig.ServiceAnnotationDisallowedList, key) }) // Labels and annotations priority: explainer component > isvc @@ -130,18 +129,18 @@ func (e *Explainer) Reconcile(isvc *v1beta1.InferenceService) (ctrl.Result, erro } container := explainer.GetContainer(isvc.ObjectMeta, isvc.Spec.Explainer.GetExtensions(), e.inferenceServiceConfig, predictorName) if len(isvc.Spec.Explainer.PodSpec.Containers) == 0 { - isvc.Spec.Explainer.PodSpec.Containers = []v1.Container{ + isvc.Spec.Explainer.PodSpec.Containers = []corev1.Container{ *container, } } else { isvc.Spec.Explainer.PodSpec.Containers[0] = *container } - podSpec := v1.PodSpec(isvc.Spec.Explainer.PodSpec) + podSpec := corev1.PodSpec(isvc.Spec.Explainer.PodSpec) // Here we allow switch between knative and vanilla deployment if e.deploymentMode == constants.RawDeployment { - r, err := raw.NewRawKubeReconciler(e.client, e.clientset, e.scheme, objectMeta, metav1.ObjectMeta{}, + r, err := raw.NewRawKubeReconciler(ctx, e.client, e.clientset, e.scheme, objectMeta, metav1.ObjectMeta{}, &isvc.Spec.Explainer.ComponentExtensionSpec, &podSpec, nil) if err != nil { return ctrl.Result{}, errors.Wrapf(err, "fails to create NewRawKubeReconciler for explainer") @@ -163,19 +162,19 @@ func (e *Explainer) Reconcile(isvc *v1beta1.InferenceService) (ctrl.Result, erro return ctrl.Result{}, errors.Wrapf(err, "fails to set autoscaler owner references for explainer") } - deployment, err := r.Reconcile() + deployment, err := r.Reconcile(ctx) if err != nil { return ctrl.Result{}, errors.Wrapf(err, "fails to reconcile explainer") } isvc.Status.PropagateRawStatus(v1beta1.ExplainerComponent, deployment, r.URL) } else { r := knative.NewKsvcReconciler(e.client, e.scheme, objectMeta, &isvc.Spec.Explainer.ComponentExtensionSpec, - &podSpec, isvc.Status.Components[v1beta1.ExplainerComponent]) + &podSpec, isvc.Status.Components[v1beta1.ExplainerComponent], e.inferenceServiceConfig.ServiceLabelDisallowedList) if err := controllerutil.SetControllerReference(isvc, r.Service, e.scheme); err != nil { return ctrl.Result{}, errors.Wrapf(err, "fails to set owner reference for explainer") } - status, err := r.Reconcile() + status, err := r.Reconcile(ctx) if err != nil { return ctrl.Result{}, errors.Wrapf(err, "fails to reconcile explainer") } diff --git a/pkg/controller/v1beta1/inferenceservice/components/predictor.go b/pkg/controller/v1beta1/inferenceservice/components/predictor.go index f3192ab6026..62f0dc3c163 100644 --- a/pkg/controller/v1beta1/inferenceservice/components/predictor.go +++ b/pkg/controller/v1beta1/inferenceservice/components/predictor.go @@ -24,7 +24,7 @@ import ( "github.com/go-logr/logr" "github.com/pkg/errors" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" @@ -41,7 +41,6 @@ import ( modelconfig "github.com/kserve/kserve/pkg/controller/v1beta1/inferenceservice/reconcilers/modelconfig" "github.com/kserve/kserve/pkg/controller/v1beta1/inferenceservice/reconcilers/raw" isvcutils "github.com/kserve/kserve/pkg/controller/v1beta1/inferenceservice/utils" - "github.com/kserve/kserve/pkg/credentials" "github.com/kserve/kserve/pkg/utils" ) @@ -53,13 +52,13 @@ type Predictor struct { clientset kubernetes.Interface scheme *runtime.Scheme inferenceServiceConfig *v1beta1.InferenceServicesConfig - credentialBuilder *credentials.CredentialBuilder //nolint: unused deploymentMode constants.DeploymentModeType Log logr.Logger } func NewPredictor(client client.Client, clientset kubernetes.Interface, scheme *runtime.Scheme, - inferenceServiceConfig *v1beta1.InferenceServicesConfig, deploymentMode constants.DeploymentModeType) Component { + inferenceServiceConfig *v1beta1.InferenceServicesConfig, deploymentMode constants.DeploymentModeType, +) Component { return &Predictor{ client: client, clientset: clientset, @@ -71,10 +70,10 @@ func NewPredictor(client client.Client, clientset kubernetes.Interface, scheme * } // Reconcile observes the predictor and attempts to drive the status towards the desired state. -func (p *Predictor) Reconcile(isvc *v1beta1.InferenceService) (ctrl.Result, error) { - var container *v1.Container - var podSpec v1.PodSpec - var workerPodSpec *v1.PodSpec +func (p *Predictor) Reconcile(ctx context.Context, isvc *v1beta1.InferenceService) (ctrl.Result, error) { + var container *corev1.Container + var podSpec corev1.PodSpec + var workerPodSpec *corev1.PodSpec var workerObjectMeta metav1.ObjectMeta var sRuntime v1alpha1.ServingRuntimeSpec var sRuntimeLabels map[string]string @@ -90,9 +89,12 @@ func (p *Predictor) Reconcile(isvc *v1beta1.InferenceService) (ctrl.Result, erro } annotations := utils.Filter(isvc.Annotations, func(key string) bool { - return !utils.Includes(constants.ServiceAnnotationDisallowedList, key) + return !utils.Includes(p.inferenceServiceConfig.ServiceAnnotationDisallowedList, key) }) + p.Log.V(1).Info("Predictor custom annotations", "annotations", p.inferenceServiceConfig.ServiceAnnotationDisallowedList) + p.Log.V(1).Info("Predictor custom labels", "labels", p.inferenceServiceConfig.ServiceLabelDisallowedList) + addLoggerAnnotations(isvc.Spec.Predictor.Logger, annotations) addBatcherAnnotations(isvc.Spec.Predictor.Batcher, annotations) // Add StorageSpec annotations so mutator will mount storage credentials to InferenceService's predictor @@ -102,7 +104,7 @@ func (p *Predictor) Reconcile(isvc *v1beta1.InferenceService) (ctrl.Result, erro // Reconcile modelConfig configMapReconciler := modelconfig.NewModelConfigReconciler(p.client, p.clientset, p.scheme) - if err := configMapReconciler.Reconcile(isvc); err != nil { + if err := configMapReconciler.Reconcile(ctx, isvc); err != nil { return ctrl.Result{}, err } @@ -115,7 +117,7 @@ func (p *Predictor) Reconcile(isvc *v1beta1.InferenceService) (ctrl.Result, erro return ctrl.Result{}, errors.New("must provide only one of storageUri and storage.path") } annotations[constants.StorageInitializerSourceUriInternalAnnotationKey] = *sourceURI - err := isvcutils.ValidateStorageURI(sourceURI, p.client) + err := isvcutils.ValidateStorageURI(ctx, sourceURI, p.client) if err != nil { return ctrl.Result{}, fmt.Errorf("StorageURI not supported: %w", err) } @@ -128,7 +130,7 @@ func (p *Predictor) Reconcile(isvc *v1beta1.InferenceService) (ctrl.Result, erro if isvc.Spec.Predictor.Model.Runtime != nil { // set runtime defaults isvc.SetRuntimeDefaults() - r, err := isvcutils.GetServingRuntime(p.client, *isvc.Spec.Predictor.Model.Runtime, isvc.Namespace) + r, err := isvcutils.GetServingRuntime(ctx, p.client, *isvc.Spec.Predictor.Model.Runtime, isvc.Namespace) if err != nil { isvc.Status.UpdateModelTransitionStatus(v1beta1.InvalidSpec, &v1beta1.FailureInfo{ Reason: v1beta1.RuntimeNotRecognized, @@ -165,7 +167,7 @@ func (p *Predictor) Reconcile(isvc *v1beta1.InferenceService) (ctrl.Result, erro sRuntime = *r } else { - runtimes, err := isvc.Spec.Predictor.Model.GetSupportingRuntimes(p.client, isvc.Namespace, false, multiNodeEnabled) + runtimes, err := isvc.Spec.Predictor.Model.GetSupportingRuntimes(ctx, p.client, isvc.Namespace, false, multiNodeEnabled) if err != nil { return ctrl.Result{}, err } @@ -201,7 +203,7 @@ func (p *Predictor) Reconcile(isvc *v1beta1.InferenceService) (ctrl.Result, erro return ctrl.Result{}, errors.New("no container configuration found in selected serving runtime") } var kserveContainerIdx int - var mergedPodSpec *v1.PodSpec + var mergedPodSpec *corev1.PodSpec kserveContainerIdx, container, mergedPodSpec, err = isvcutils.MergeServingRuntimeAndInferenceServiceSpecs(sRuntime.Containers, isvc.Spec.Predictor.Model.Container, isvc, constants.InferenceServiceContainerName, sRuntime.ServingRuntimePodSpec, isvc.Spec.Predictor.PodSpec) if err != nil { return ctrl.Result{}, err @@ -220,7 +222,7 @@ func (p *Predictor) Reconcile(isvc *v1beta1.InferenceService) (ctrl.Result, erro isvcutils.UpdateImageTag(container, isvc.Spec.Predictor.Model.RuntimeVersion, isvc.Spec.Predictor.Model.Runtime) podSpec = *mergedPodSpec - podSpec.Containers = []v1.Container{ + podSpec.Containers = []corev1.Container{ *container, } podSpec.Containers = append(podSpec.Containers, sRuntime.Containers[:kserveContainerIdx]...) @@ -229,14 +231,14 @@ func (p *Predictor) Reconcile(isvc *v1beta1.InferenceService) (ctrl.Result, erro // Label filter will be handled in ksvc_reconciler sRuntimeLabels = sRuntime.ServingRuntimePodSpec.Labels sRuntimeAnnotations = utils.Filter(sRuntime.ServingRuntimePodSpec.Annotations, func(key string) bool { - return !utils.Includes(constants.ServiceAnnotationDisallowedList, key) + return !utils.Includes(p.inferenceServiceConfig.ServiceAnnotationDisallowedList, key) }) } else { container = predictor.GetContainer(isvc.ObjectMeta, isvc.Spec.Predictor.GetExtensions(), p.inferenceServiceConfig) - podSpec = v1.PodSpec(isvc.Spec.Predictor.PodSpec) + podSpec = corev1.PodSpec(isvc.Spec.Predictor.PodSpec) if len(podSpec.Containers) == 0 { - podSpec.Containers = []v1.Container{ + podSpec.Containers = []corev1.Container{ *container, } } else { @@ -246,14 +248,14 @@ func (p *Predictor) Reconcile(isvc *v1beta1.InferenceService) (ctrl.Result, erro predictorName := constants.PredictorServiceName(isvc.Name) if p.deploymentMode == constants.RawDeployment { - existing := &v1.Service{} - err := p.client.Get(context.TODO(), types.NamespacedName{Name: constants.DefaultPredictorServiceName(isvc.Name), Namespace: isvc.Namespace}, existing) + existing := &corev1.Service{} + err := p.client.Get(ctx, types.NamespacedName{Name: constants.DefaultPredictorServiceName(isvc.Name), Namespace: isvc.Namespace}, existing) if err == nil { predictorName = constants.DefaultPredictorServiceName(isvc.Name) } } else { existing := &knservingv1.Service{} - err := p.client.Get(context.TODO(), types.NamespacedName{Name: constants.DefaultPredictorServiceName(isvc.Name), Namespace: isvc.Namespace}, existing) + err := p.client.Get(ctx, types.NamespacedName{Name: constants.DefaultPredictorServiceName(isvc.Name), Namespace: isvc.Namespace}, existing) if err == nil { predictorName = constants.DefaultPredictorServiceName(isvc.Name) } @@ -263,7 +265,7 @@ func (p *Predictor) Reconcile(isvc *v1beta1.InferenceService) (ctrl.Result, erro // Label filter will be handled in ksvc_reconciler predictorLabels := isvc.Spec.Predictor.Labels predictorAnnotations := utils.Filter(isvc.Spec.Predictor.Annotations, func(key string) bool { - return !utils.Includes(constants.ServiceAnnotationDisallowedList, key) + return !utils.Includes(p.inferenceServiceConfig.ServiceAnnotationDisallowedList, key) }) // Labels and annotations priority: predictor component > isvc > ServingRuntimePodSpec @@ -337,7 +339,7 @@ func (p *Predictor) Reconcile(isvc *v1beta1.InferenceService) (ctrl.Result, erro rawDeployment = true podLabelKey = constants.RawDeploymentAppLabel // This is main RawKubeReconciler to create objects (deployment, svc, scaler) - r, err := raw.NewRawKubeReconciler(p.client, p.clientset, p.scheme, objectMeta, workerObjectMeta, &isvc.Spec.Predictor.ComponentExtensionSpec, + r, err := raw.NewRawKubeReconciler(ctx, p.client, p.clientset, p.scheme, objectMeta, workerObjectMeta, &isvc.Spec.Predictor.ComponentExtensionSpec, &podSpec, workerPodSpec) if err != nil { return ctrl.Result{}, errors.Wrapf(err, "fails to create NewRawKubeReconciler for predictor") @@ -360,7 +362,7 @@ func (p *Predictor) Reconcile(isvc *v1beta1.InferenceService) (ctrl.Result, erro return ctrl.Result{}, errors.Wrapf(err, "fails to set autoscaler owner references for predictor") } - deploymentList, err := r.Reconcile() + deploymentList, err := r.Reconcile(ctx) if err != nil { return ctrl.Result{}, errors.Wrapf(err, "fails to reconcile predictor") } @@ -368,13 +370,13 @@ func (p *Predictor) Reconcile(isvc *v1beta1.InferenceService) (ctrl.Result, erro } else { podLabelKey = constants.RevisionLabel r := knative.NewKsvcReconciler(p.client, p.scheme, objectMeta, &isvc.Spec.Predictor.ComponentExtensionSpec, - &podSpec, isvc.Status.Components[v1beta1.PredictorComponent]) + &podSpec, isvc.Status.Components[v1beta1.PredictorComponent], p.inferenceServiceConfig.ServiceLabelDisallowedList) if err := controllerutil.SetControllerReference(isvc, r.Service, p.scheme); err != nil { return ctrl.Result{}, errors.Wrapf(err, "fails to set owner reference for predictor") } var err error - kstatus, err = r.Reconcile() + kstatus, err = r.Reconcile(ctx) if err != nil { return ctrl.Result{}, errors.Wrapf(err, "fails to reconcile predictor") } @@ -387,7 +389,7 @@ func (p *Predictor) Reconcile(isvc *v1beta1.InferenceService) (ctrl.Result, erro } else { podLabelValue = statusSpec.LatestCreatedRevision } - predictorPods, err := isvcutils.ListPodsByLabel(p.client, isvc.ObjectMeta.Namespace, podLabelKey, podLabelValue) + predictorPods, err := isvcutils.ListPodsByLabel(ctx, p.client, isvc.ObjectMeta.Namespace, podLabelKey, podLabelValue) if err != nil { return ctrl.Result{}, errors.Wrapf(err, "fails to list inferenceservice pods by label") } @@ -398,10 +400,10 @@ func (p *Predictor) Reconcile(isvc *v1beta1.InferenceService) (ctrl.Result, erro } } -func multiNodeProcess(sRuntime v1alpha1.ServingRuntimeSpec, isvc *v1beta1.InferenceService, podSpec *v1.PodSpec, annotations map[string]string, isvcGeneration string) (*v1.PodSpec, error) { +func multiNodeProcess(sRuntime v1alpha1.ServingRuntimeSpec, isvc *v1beta1.InferenceService, podSpec *corev1.PodSpec, annotations map[string]string, isvcGeneration string) (*corev1.PodSpec, error) { if sRuntime.WorkerSpec == nil { errMsg := "you cannot set WorkerSpec in the InferenceService if the ServingRuntime does not have a WorkerSpec" - isvc.Status.PropagateRawStatusWithMessages(v1beta1.PredictorComponent, v1beta1.InvalidWorkerSpecNotSet, errMsg, v1.ConditionFalse) + isvc.Status.PropagateRawStatusWithMessages(v1beta1.PredictorComponent, v1beta1.InvalidWorkerSpecNotSet, errMsg, corev1.ConditionFalse) return nil, errors.New(errMsg) } // Check if workerSpec in ServingRuntime does not have worker containers information, it should return errors @@ -414,11 +416,11 @@ func multiNodeProcess(sRuntime v1alpha1.ServingRuntimeSpec, isvc *v1beta1.Infere return nil, errors.New(errMsg) } - var workerContainer *v1.Container - var mergedWorkerPodSpec *v1.PodSpec + var workerContainer *corev1.Container + var mergedWorkerPodSpec *corev1.PodSpec var err error - targetisvcContainer := v1.Container{} + targetisvcContainer := corev1.Container{} if isvc.Spec.Predictor.WorkerSpec.Containers != nil { targetisvcContainer = isvc.Spec.Predictor.WorkerSpec.Containers[0] } @@ -437,7 +439,7 @@ func multiNodeProcess(sRuntime v1alpha1.ServingRuntimeSpec, isvc *v1beta1.Infere sRuntime.WorkerSpec.TensorParallelSize = isvc.Spec.Predictor.WorkerSpec.TensorParallelSize } - mergedWorkerPodSpec.Containers = []v1.Container{ + mergedWorkerPodSpec.Containers = []corev1.Container{ *workerContainer, } diff --git a/pkg/controller/v1beta1/inferenceservice/components/transformer.go b/pkg/controller/v1beta1/inferenceservice/components/transformer.go index 2e632edf535..eca17b9df10 100644 --- a/pkg/controller/v1beta1/inferenceservice/components/transformer.go +++ b/pkg/controller/v1beta1/inferenceservice/components/transformer.go @@ -39,7 +39,6 @@ import ( "github.com/kserve/kserve/pkg/controller/v1beta1/inferenceservice/reconcilers/knative" raw "github.com/kserve/kserve/pkg/controller/v1beta1/inferenceservice/reconcilers/raw" isvcutils "github.com/kserve/kserve/pkg/controller/v1beta1/inferenceservice/utils" - "github.com/kserve/kserve/pkg/credentials" "github.com/kserve/kserve/pkg/utils" ) @@ -51,13 +50,13 @@ type Transformer struct { clientset kubernetes.Interface scheme *runtime.Scheme inferenceServiceConfig *v1beta1.InferenceServicesConfig - credentialBuilder *credentials.CredentialBuilder //nolint: unused deploymentMode constants.DeploymentModeType Log logr.Logger } func NewTransformer(client client.Client, clientset kubernetes.Interface, scheme *runtime.Scheme, - inferenceServiceConfig *v1beta1.InferenceServicesConfig, deploymentMode constants.DeploymentModeType) Component { + inferenceServiceConfig *v1beta1.InferenceServicesConfig, deploymentMode constants.DeploymentModeType, +) Component { return &Transformer{ client: client, clientset: clientset, @@ -69,17 +68,17 @@ func NewTransformer(client client.Client, clientset kubernetes.Interface, scheme } // Reconcile observes the world and attempts to drive the status towards the desired state. -func (p *Transformer) Reconcile(isvc *v1beta1.InferenceService) (ctrl.Result, error) { +func (p *Transformer) Reconcile(ctx context.Context, isvc *v1beta1.InferenceService) (ctrl.Result, error) { p.Log.Info("Reconciling Transformer", "TransformerSpec", isvc.Spec.Transformer) transformer := isvc.Spec.Transformer.GetImplementation() annotations := utils.Filter(isvc.Annotations, func(key string) bool { - return !utils.Includes(constants.ServiceAnnotationDisallowedList, key) + return !utils.Includes(p.inferenceServiceConfig.ServiceAnnotationDisallowedList, key) }) // KNative does not support INIT containers or mounting, so we add annotations that trigger the // StorageInitializer injector to mutate the underlying deployment to provision model data if sourceURI := transformer.GetStorageUri(); sourceURI != nil { annotations[constants.StorageInitializerSourceUriInternalAnnotationKey] = *sourceURI - err := isvcutils.ValidateStorageURI(sourceURI, p.client) + err := isvcutils.ValidateStorageURI(ctx, sourceURI, p.client) if err != nil { return ctrl.Result{}, fmt.Errorf("StorageURI not supported: %w", err) } @@ -91,14 +90,14 @@ func (p *Transformer) Reconcile(isvc *v1beta1.InferenceService) (ctrl.Result, er predictorName := constants.PredictorServiceName(isvc.Name) if p.deploymentMode == constants.RawDeployment { existing := &corev1.Service{} - err := p.client.Get(context.TODO(), types.NamespacedName{Name: constants.DefaultTransformerServiceName(isvc.Name), Namespace: isvc.Namespace}, existing) + err := p.client.Get(ctx, types.NamespacedName{Name: constants.DefaultTransformerServiceName(isvc.Name), Namespace: isvc.Namespace}, existing) if err == nil { transformerName = constants.DefaultTransformerServiceName(isvc.Name) predictorName = constants.DefaultPredictorServiceName(isvc.Name) } } else { existing := &knservingv1.Service{} - err := p.client.Get(context.TODO(), types.NamespacedName{Name: constants.DefaultTransformerServiceName(isvc.Name), Namespace: isvc.Namespace}, existing) + err := p.client.Get(ctx, types.NamespacedName{Name: constants.DefaultTransformerServiceName(isvc.Name), Namespace: isvc.Namespace}, existing) if err == nil { transformerName = constants.DefaultTransformerServiceName(isvc.Name) predictorName = constants.DefaultPredictorServiceName(isvc.Name) @@ -109,7 +108,7 @@ func (p *Transformer) Reconcile(isvc *v1beta1.InferenceService) (ctrl.Result, er // Label filter will be handled in ksvc_reconciler transformerLabels := isvc.Spec.Transformer.Labels transformerAnnotations := utils.Filter(isvc.Spec.Transformer.Annotations, func(key string) bool { - return !utils.Includes(constants.ServiceAnnotationDisallowedList, key) + return !utils.Includes(p.inferenceServiceConfig.ServiceAnnotationDisallowedList, key) }) // Labels and annotations priority: transformer component > isvc @@ -170,7 +169,7 @@ func (p *Transformer) Reconcile(isvc *v1beta1.InferenceService) (ctrl.Result, er // Here we allow switch between knative and vanilla deployment if p.deploymentMode == constants.RawDeployment { - r, err := raw.NewRawKubeReconciler(p.client, p.clientset, p.scheme, objectMeta, metav1.ObjectMeta{}, + r, err := raw.NewRawKubeReconciler(ctx, p.client, p.clientset, p.scheme, objectMeta, metav1.ObjectMeta{}, &isvc.Spec.Transformer.ComponentExtensionSpec, &podSpec, nil) if err != nil { return ctrl.Result{}, errors.Wrapf(err, "fails to create NewRawKubeReconciler for transformer") @@ -192,18 +191,18 @@ func (p *Transformer) Reconcile(isvc *v1beta1.InferenceService) (ctrl.Result, er return ctrl.Result{}, errors.Wrapf(err, "fails to set autoscaler owner references for transformer") } - deployment, err := r.Reconcile() + deployment, err := r.Reconcile(ctx) if err != nil { return ctrl.Result{}, errors.Wrapf(err, "fails to reconcile transformer") } isvc.Status.PropagateRawStatus(v1beta1.TransformerComponent, deployment, r.URL) } else { r := knative.NewKsvcReconciler(p.client, p.scheme, objectMeta, &isvc.Spec.Transformer.ComponentExtensionSpec, - &podSpec, isvc.Status.Components[v1beta1.TransformerComponent]) + &podSpec, isvc.Status.Components[v1beta1.TransformerComponent], p.inferenceServiceConfig.ServiceLabelDisallowedList) if err := controllerutil.SetControllerReference(isvc, r.Service, p.scheme); err != nil { return ctrl.Result{}, errors.Wrapf(err, "fails to set owner reference for transformer") } - status, err := r.Reconcile() + status, err := r.Reconcile(ctx) if err != nil { return ctrl.Result{}, errors.Wrapf(err, "fails to reconcile transformer") } diff --git a/pkg/controller/v1beta1/inferenceservice/controller.go b/pkg/controller/v1beta1/inferenceservice/controller.go index 008515d77f2..668f3be24b2 100644 --- a/pkg/controller/v1beta1/inferenceservice/controller.go +++ b/pkg/controller/v1beta1/inferenceservice/controller.go @@ -26,7 +26,8 @@ import ( "github.com/pkg/errors" istioclientv1beta1 "istio.io/client-go/pkg/apis/networking/v1beta1" appsv1 "k8s.io/api/apps/v1" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" + netv1 "k8s.io/api/networking/v1" "k8s.io/apimachinery/pkg/api/equality" apierr "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -43,8 +44,10 @@ import ( "sigs.k8s.io/controller-runtime/pkg/reconcile" "sigs.k8s.io/yaml" - v1alpha1api "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" - v1beta1api "github.com/kserve/kserve/pkg/apis/serving/v1beta1" + gatewayapiv1 "sigs.k8s.io/gateway-api/apis/v1" + + "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" + "github.com/kserve/kserve/pkg/apis/serving/v1beta1" "github.com/kserve/kserve/pkg/constants" "github.com/kserve/kserve/pkg/controller/v1beta1/inferenceservice/components" "github.com/kserve/kserve/pkg/controller/v1beta1/inferenceservice/reconcilers/cabundleconfigmap" @@ -79,6 +82,7 @@ import ( // +kubebuilder:rbac:groups=core,resources=namespaces,verbs=get;list;watch // +kubebuilder:rbac:groups=core,resources=events,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=core,resources=pods,verbs=get;list;watch +// +kubebuilder:rbac:groups=gateway.networking.k8s.io,resources=httproutes,verbs=get;list;watch;create;update;patch;delete // InferenceServiceState describes the Readiness of the InferenceService type InferenceServiceState string @@ -101,7 +105,7 @@ type InferenceServiceReconciler struct { func (r *InferenceServiceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { // Fetch the InferenceService instance - isvc := &v1beta1api.InferenceService{} + isvc := &v1beta1.InferenceService{} if err := r.Get(ctx, req.NamespacedName, isvc); err != nil { if apierr.IsNotFound(err) { // Object not found, return. Created objects are automatically garbage collected. @@ -110,12 +114,22 @@ func (r *InferenceServiceReconciler) Reconcile(ctx context.Context, req ctrl.Req } return reconcile.Result{}, err } + isvcConfigMap, err := v1beta1.GetInferenceServiceConfigMap(ctx, r.Clientset) + if err != nil { + r.Log.Error(err, "unable to get configmap", "name", constants.InferenceServiceConfigMapName, "namespace", constants.KServeNamespace) + return reconcile.Result{}, err + } + isvcConfig, err := v1beta1.NewInferenceServicesConfig(isvcConfigMap) + if err != nil { + return reconcile.Result{}, errors.Wrapf(err, "fails to create InferenceServicesConfig") + } + // get annotations from isvc annotations := utils.Filter(isvc.Annotations, func(key string) bool { - return !utils.Includes(constants.ServiceAnnotationDisallowedList, key) + return !utils.Includes(isvcConfig.ServiceAnnotationDisallowedList, key) }) - deployConfig, err := v1beta1api.NewDeployConfig(r.Clientset) + deployConfig, err := v1beta1.NewDeployConfig(isvcConfigMap) if err != nil { return reconcile.Result{}, errors.Wrapf(err, "fails to create DeployConfig") } @@ -154,7 +168,7 @@ func (r *InferenceServiceReconciler) Reconcile(ctx context.Context, req ctrl.Req // The object is being deleted if controllerutil.ContainsFinalizer(isvc, finalizerName) { // our finalizer is present, so lets handle any external dependency - if err := r.deleteExternalResources(isvc); err != nil { + if err := r.deleteExternalResources(ctx, isvc); err != nil { // if fail to delete the external dependency here, return with error // so that it can be retried return ctrl.Result{}, err @@ -181,7 +195,7 @@ func (r *InferenceServiceReconciler) Reconcile(ctx context.Context, req ctrl.Req } if !ksvcAvailable { - r.Recorder.Event(isvc, v1.EventTypeWarning, "ServerlessModeRejected", + r.Recorder.Event(isvc, corev1.EventTypeWarning, "ServerlessModeRejected", "It is not possible to use Serverless deployment mode when Knative Services are not available") return reconcile.Result{Requeue: false}, reconcile.TerminalError(fmt.Errorf("the resolved deployment mode of InferenceService '%s' is Serverless, but Knative Serving is not available", isvc.Name)) } @@ -189,14 +203,10 @@ func (r *InferenceServiceReconciler) Reconcile(ctx context.Context, req ctrl.Req // Setup reconcilers r.Log.Info("Reconciling inference service", "apiVersion", isvc.APIVersion, "isvc", isvc.Name) - isvcConfig, err := v1beta1api.NewInferenceServicesConfig(r.Clientset) - if err != nil { - return reconcile.Result{}, errors.Wrapf(err, "fails to create InferenceServicesConfig") - } // Reconcile cabundleConfigMap caBundleConfigMapReconciler := cabundleconfigmap.NewCaBundleConfigMapReconciler(r.Client, r.Clientset, r.Scheme) - if err := caBundleConfigMapReconciler.Reconcile(isvc); err != nil { + if err := caBundleConfigMapReconciler.Reconcile(ctx, isvc); err != nil { return reconcile.Result{}, err } @@ -211,11 +221,11 @@ func (r *InferenceServiceReconciler) Reconcile(ctx context.Context, req ctrl.Req reconcilers = append(reconcilers, components.NewExplainer(r.Client, r.Clientset, r.Scheme, isvcConfig, deploymentMode)) } for _, reconciler := range reconcilers { - result, err := reconciler.Reconcile(isvc) + result, err := reconciler.Reconcile(ctx, isvc) if err != nil { r.Log.Error(err, "Failed to reconcile", "reconciler", reflect.ValueOf(reconciler), "Name", isvc.Name) - r.Recorder.Eventf(isvc, v1.EventTypeWarning, "InternalError", err.Error()) - if err := r.updateStatus(isvc, deploymentMode); err != nil { + r.Recorder.Eventf(isvc, corev1.EventTypeWarning, "InternalError", err.Error()) + if err := r.updateStatus(ctx, isvc, deploymentMode); err != nil { r.Log.Error(err, "Error updating status") return result, err } @@ -227,57 +237,67 @@ func (r *InferenceServiceReconciler) Reconcile(ctx context.Context, req ctrl.Req } // reconcile RoutesReady and LatestDeploymentReady conditions for serverless deployment if deploymentMode == constants.Serverless { - componentList := []v1beta1api.ComponentType{v1beta1api.PredictorComponent} + componentList := []v1beta1.ComponentType{v1beta1.PredictorComponent} if isvc.Spec.Transformer != nil { - componentList = append(componentList, v1beta1api.TransformerComponent) + componentList = append(componentList, v1beta1.TransformerComponent) } if isvc.Spec.Explainer != nil { - componentList = append(componentList, v1beta1api.ExplainerComponent) + componentList = append(componentList, v1beta1.ExplainerComponent) } - isvc.Status.PropagateCrossComponentStatus(componentList, v1beta1api.RoutesReady) - isvc.Status.PropagateCrossComponentStatus(componentList, v1beta1api.LatestDeploymentReady) + isvc.Status.PropagateCrossComponentStatus(componentList, v1beta1.RoutesReady) + isvc.Status.PropagateCrossComponentStatus(componentList, v1beta1.LatestDeploymentReady) } // Reconcile ingress - ingressConfig, err := v1beta1api.NewIngressConfig(r.Clientset) + ingressConfig, err := v1beta1.NewIngressConfig(isvcConfigMap) if err != nil { return reconcile.Result{}, errors.Wrapf(err, "fails to create IngressConfig") } // check raw deployment if deploymentMode == constants.RawDeployment { - reconciler, err := ingress.NewRawIngressReconciler(r.Client, r.Scheme, ingressConfig) - if err != nil { - return reconcile.Result{}, errors.Wrapf(err, "fails to reconcile ingress") - } - if err := reconciler.Reconcile(isvc); err != nil { - return reconcile.Result{}, errors.Wrapf(err, "fails to reconcile ingress") + if ingressConfig.EnableGatewayAPI { + reconciler := ingress.NewRawHTTPRouteReconciler(r.Client, r.Scheme, ingressConfig, isvcConfig) + + if err := reconciler.Reconcile(ctx, isvc); err != nil { + return reconcile.Result{}, errors.Wrapf(err, "fails to reconcile ingress") + } + } else { + reconciler, err := ingress.NewRawIngressReconciler(r.Client, r.Scheme, ingressConfig, isvcConfig) + if err != nil { + return reconcile.Result{}, errors.Wrapf(err, "fails to reconcile ingress") + } + if err := reconciler.Reconcile(ctx, isvc); err != nil { + return reconcile.Result{}, errors.Wrapf(err, "fails to reconcile ingress") + } } } else { - reconciler := ingress.NewIngressReconciler(r.Client, r.Clientset, r.Scheme, ingressConfig) + reconciler := ingress.NewIngressReconciler(r.Client, r.Clientset, r.Scheme, ingressConfig, isvcConfig) r.Log.Info("Reconciling ingress for inference service", "isvc", isvc.Name) - if err := reconciler.Reconcile(isvc); err != nil { + if err := reconciler.Reconcile(ctx, isvc); err != nil { return reconcile.Result{}, errors.Wrapf(err, "fails to reconcile ingress") } } // Reconcile modelConfig configMapReconciler := modelconfig.NewModelConfigReconciler(r.Client, r.Clientset, r.Scheme) - if err := configMapReconciler.Reconcile(isvc); err != nil { + if err := configMapReconciler.Reconcile(ctx, isvc); err != nil { return reconcile.Result{}, err } - if err = r.updateStatus(isvc, deploymentMode); err != nil { - r.Recorder.Event(isvc, v1.EventTypeWarning, "InternalError", err.Error()) + if err = r.updateStatus(ctx, isvc, deploymentMode); err != nil { + r.Recorder.Event(isvc, corev1.EventTypeWarning, "InternalError", err.Error()) return reconcile.Result{}, err } return ctrl.Result{}, nil } -func (r *InferenceServiceReconciler) updateStatus(desiredService *v1beta1api.InferenceService, deploymentMode constants.DeploymentModeType) error { - existingService := &v1beta1api.InferenceService{} +func (r *InferenceServiceReconciler) updateStatus(ctx context.Context, desiredService *v1beta1.InferenceService, + deploymentMode constants.DeploymentModeType, +) error { + existingService := &v1beta1.InferenceService{} namespacedName := types.NamespacedName{Name: desiredService.Name, Namespace: desiredService.Namespace} - if err := r.Get(context.TODO(), namespacedName, existingService); err != nil { + if err := r.Get(ctx, namespacedName, existingService); err != nil { return err } wasReady := inferenceServiceReadiness(existingService.Status) @@ -286,9 +306,9 @@ func (r *InferenceServiceReconciler) updateStatus(desiredService *v1beta1api.Inf // This is important because the copy we loaded from the informer's // cache may be stale and we don't want to overwrite a prior update // to status with this stale state. - } else if err := r.Status().Update(context.TODO(), desiredService); err != nil { + } else if err := r.Status().Update(ctx, desiredService); err != nil { r.Log.Error(err, "Failed to update InferenceService status", "InferenceService", desiredService.Name) - r.Recorder.Eventf(desiredService, v1.EventTypeWarning, "UpdateFailed", + r.Recorder.Eventf(desiredService, corev1.EventTypeWarning, "UpdateFailed", "Failed to update status for InferenceService %q: %v", desiredService.Name, err) return errors.Wrapf(err, "fails to update InferenceService status") } else { @@ -296,41 +316,41 @@ func (r *InferenceServiceReconciler) updateStatus(desiredService *v1beta1api.Inf isReady := inferenceServiceReadiness(desiredService.Status) isReadyFalse := inferenceServiceReadinessFalse(desiredService.Status) if wasReady && isReadyFalse { // Moved to NotReady State - r.Recorder.Eventf(desiredService, v1.EventTypeWarning, string(InferenceServiceNotReadyState), + r.Recorder.Eventf(desiredService, corev1.EventTypeWarning, string(InferenceServiceNotReadyState), fmt.Sprintf("InferenceService [%v] is no longer Ready because of: %v", desiredService.GetName(), r.GetFailConditions(desiredService))) } else if !wasReady && isReady { // Moved to Ready State - r.Recorder.Eventf(desiredService, v1.EventTypeNormal, string(InferenceServiceReadyState), + r.Recorder.Eventf(desiredService, corev1.EventTypeNormal, string(InferenceServiceReadyState), fmt.Sprintf("InferenceService [%v] is Ready", desiredService.GetName())) } } return nil } -func inferenceServiceReadiness(status v1beta1api.InferenceServiceStatus) bool { +func inferenceServiceReadiness(status v1beta1.InferenceServiceStatus) bool { return status.Conditions != nil && status.GetCondition(apis.ConditionReady) != nil && - status.GetCondition(apis.ConditionReady).Status == v1.ConditionTrue + status.GetCondition(apis.ConditionReady).Status == corev1.ConditionTrue } -func inferenceServiceReadinessFalse(status v1beta1api.InferenceServiceStatus) bool { +func inferenceServiceReadinessFalse(status v1beta1.InferenceServiceStatus) bool { readyCondition := status.GetCondition(apis.ConditionReady) - return readyCondition != nil && readyCondition.Status == v1.ConditionFalse + return readyCondition != nil && readyCondition.Status == corev1.ConditionFalse } -func inferenceServiceStatusEqual(s1, s2 v1beta1api.InferenceServiceStatus, deploymentMode constants.DeploymentModeType) bool { +func inferenceServiceStatusEqual(s1, s2 v1beta1.InferenceServiceStatus, deploymentMode constants.DeploymentModeType) bool { if deploymentMode == constants.ModelMeshDeployment { // If the deployment mode is ModelMesh, reduce the status scope to compare. // Exclude Predictor and ModelStatus which are mananged by ModelMesh controllers return equality.Semantic.DeepEqual(s1.Address, s2.Address) && equality.Semantic.DeepEqual(s1.URL, s2.URL) && equality.Semantic.DeepEqual(s1.Status, s2.Status) && - equality.Semantic.DeepEqual(s1.Components[v1beta1api.TransformerComponent], s2.Components[v1beta1api.TransformerComponent]) && - equality.Semantic.DeepEqual(s1.Components[v1beta1api.ExplainerComponent], s2.Components[v1beta1api.ExplainerComponent]) + equality.Semantic.DeepEqual(s1.Components[v1beta1.TransformerComponent], s2.Components[v1beta1.TransformerComponent]) && + equality.Semantic.DeepEqual(s1.Components[v1beta1.ExplainerComponent], s2.Components[v1beta1.ExplainerComponent]) } return equality.Semantic.DeepEqual(s1, s2) } -func (r *InferenceServiceReconciler) SetupWithManager(mgr ctrl.Manager, deployConfig *v1beta1api.DeployConfig, ingressConfig *v1beta1api.IngressConfig) error { +func (r *InferenceServiceReconciler) SetupWithManager(mgr ctrl.Manager, deployConfig *v1beta1.DeployConfig, ingressConfig *v1beta1.IngressConfig) error { r.ClientConfig = mgr.GetConfig() ksvcFound, err := utils.IsCrdAvailable(r.ClientConfig, knservingv1.SchemeGroupVersion.String(), constants.KnativeServiceKind) @@ -344,7 +364,7 @@ func (r *InferenceServiceReconciler) SetupWithManager(mgr ctrl.Manager, deployCo } ctrlBuilder := ctrl.NewControllerManagedBy(mgr). - For(&v1beta1api.InferenceService{}). + For(&v1beta1.InferenceService{}). Owns(&appsv1.Deployment{}) if ksvcFound { @@ -359,14 +379,30 @@ func (r *InferenceServiceReconciler) SetupWithManager(mgr ctrl.Manager, deployCo r.Log.Info("The InferenceService controller won't watch networking.istio.io/v1beta1/VirtualService resources because the CRD is not available.") } + if ingressConfig.EnableGatewayAPI { + gatewayapiFound, err := utils.IsCrdAvailable(r.ClientConfig, gatewayapiv1.GroupVersion.String(), constants.HTTPRouteKind) + if err != nil { + return err + } + + if gatewayapiFound { + ctrlBuilder = ctrlBuilder.Owns(&gatewayapiv1.HTTPRoute{}) + } else { + r.Log.Info("The InferenceService controller won't watch gateway.networking.k8s.io/v1/HTTPRoute resources because the CRD is not available.") + panic("Gateway API CRD not available") + } + } else { + ctrlBuilder = ctrlBuilder.Owns(&netv1.Ingress{}) + } + return ctrlBuilder.Complete(r) } -func (r *InferenceServiceReconciler) deleteExternalResources(isvc *v1beta1api.InferenceService) error { +func (r *InferenceServiceReconciler) deleteExternalResources(ctx context.Context, isvc *v1beta1.InferenceService) error { // Delete all the TrainedModel that uses this InferenceService as parent r.Log.Info("Deleting external resources", "InferenceService", isvc.Name) - var trainedModels v1alpha1api.TrainedModelList - if err := r.List(context.TODO(), + var trainedModels v1alpha1.TrainedModelList + if err := r.List(ctx, &trainedModels, client.MatchingLabels{constants.ParentInferenceServiceLabel: isvc.Name}, client.InNamespace(isvc.Namespace), @@ -377,14 +413,14 @@ func (r *InferenceServiceReconciler) deleteExternalResources(isvc *v1beta1api.In // #nosec G601 for i, v := range trainedModels.Items { - if err := r.Delete(context.TODO(), &trainedModels.Items[i], client.PropagationPolicy(metav1.DeletePropagationBackground)); client.IgnoreNotFound(err) != nil { + if err := r.Delete(ctx, &trainedModels.Items[i], client.PropagationPolicy(metav1.DeletePropagationBackground)); client.IgnoreNotFound(err) != nil { r.Log.Error(err, "unable to delete trainedmodel", "trainedmodel", v) } } return nil } -func (r *InferenceServiceReconciler) GetFailConditions(isvc *v1beta1api.InferenceService) string { +func (r *InferenceServiceReconciler) GetFailConditions(isvc *v1beta1.InferenceService) string { msg := "" for _, cond := range isvc.Status.Conditions { if string(cond.Status) == "False" { diff --git a/pkg/controller/v1beta1/inferenceservice/controller_test.go b/pkg/controller/v1beta1/inferenceservice/controller_test.go index caa0ac73c4b..d1432e182bb 100644 --- a/pkg/controller/v1beta1/inferenceservice/controller_test.go +++ b/pkg/controller/v1beta1/inferenceservice/controller_test.go @@ -26,27 +26,29 @@ import ( "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" - "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" - "github.com/kserve/kserve/pkg/apis/serving/v1beta1" - "github.com/kserve/kserve/pkg/constants" - "github.com/kserve/kserve/pkg/utils" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "google.golang.org/protobuf/proto" istiov1beta1 "istio.io/api/networking/v1beta1" istioclientv1beta1 "istio.io/client-go/pkg/apis/networking/v1beta1" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" apierr "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/util/retry" + "k8s.io/utils/ptr" "knative.dev/pkg/apis" duckv1 "knative.dev/pkg/apis/duck/v1" "knative.dev/pkg/network" knservingv1 "knative.dev/serving/pkg/apis/serving/v1" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/reconcile" + + "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" + "github.com/kserve/kserve/pkg/apis/serving/v1beta1" + "github.com/kserve/kserve/pkg/constants" + "github.com/kserve/kserve/pkg/utils" ) var _ = Describe("v1beta1 inference service controller", func() { @@ -57,14 +59,14 @@ var _ = Describe("v1beta1 inference service controller", func() { domain = "example.com" ) var ( - defaultResource = v1.ResourceRequirements{ - Limits: v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("1"), - v1.ResourceMemory: resource.MustParse("2Gi"), + defaultResource = corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("2Gi"), }, - Requests: v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("1"), - v1.ResourceMemory: resource.MustParse("2Gi"), + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("2Gi"), }, } configs = map[string]string{ @@ -75,6 +77,7 @@ var _ = Describe("v1beta1 inference service controller", func() { } }`, "ingress": `{ + "kserveIngressGateway": "kserve/kserve-ingress-gateway", "ingressGateway": "knative-serving/knative-ingress-gateway", "localGateway": "knative-serving/knative-local-gateway", "localGatewayService": "knative-local-gateway.istio-system.svc.cluster.local" @@ -95,7 +98,7 @@ var _ = Describe("v1beta1 inference service controller", func() { It("Should have knative service created", func() { By("By creating a new InferenceService") // Create configmap - var configMap = &v1.ConfigMap{ + configMap := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: constants.InferenceServiceConfigMapName, Namespace: constants.KServeNamespace, @@ -129,7 +132,7 @@ var _ = Describe("v1beta1 inference service controller", func() { "key2": "val2FromSR", "key3": "val3FromSR", }, - Containers: []v1.Container{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, Image: "tensorflow/serving:1.14.0", @@ -143,7 +146,7 @@ var _ = Describe("v1beta1 inference service controller", func() { Resources: defaultResource, }, }, - ImagePullSecrets: []v1.LocalObjectReference{ + ImagePullSecrets: []corev1.LocalObjectReference{ {Name: "sr-image-pull-secret"}, }, }, @@ -154,9 +157,9 @@ var _ = Describe("v1beta1 inference service controller", func() { defer k8sClient.Delete(context.TODO(), servingRuntime) // Create InferenceService serviceName := "foo" - var expectedRequest = reconcile.Request{NamespacedName: types.NamespacedName{Name: serviceName, Namespace: "default"}} - var serviceKey = expectedRequest.NamespacedName - var storageUri = "s3://test/mnist/export" + expectedRequest := reconcile.Request{NamespacedName: types.NamespacedName{Name: serviceName, Namespace: "default"}} + serviceKey := expectedRequest.NamespacedName + storageUri := "s3://test/mnist/export" ctx := context.Background() isvc := &v1beta1.InferenceService{ ObjectMeta: metav1.ObjectMeta{ @@ -175,7 +178,7 @@ var _ = Describe("v1beta1 inference service controller", func() { Spec: v1beta1.InferenceServiceSpec{ Predictor: v1beta1.PredictorSpec{ ComponentExtensionSpec: v1beta1.ComponentExtensionSpec{ - MinReplicas: v1beta1.GetIntReference(1), + MinReplicas: ptr.To(int32(1)), MaxReplicas: 3, Labels: map[string]string{ "key3": "val3FromPredictor", @@ -188,7 +191,7 @@ var _ = Describe("v1beta1 inference service controller", func() { PredictorExtensionSpec: v1beta1.PredictorExtensionSpec{ StorageURI: &storageUri, RuntimeVersion: proto.String("1.14.0"), - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, Resources: defaultResource, }, @@ -203,15 +206,14 @@ var _ = Describe("v1beta1 inference service controller", func() { inferenceService := &v1beta1.InferenceService{} Eventually(func() bool { err := k8sClient.Get(ctx, serviceKey, inferenceService) - if err != nil { - return false - } - return true + return err == nil }, timeout, interval).Should(BeTrue()) actualService := &knservingv1.Service{} - predictorServiceKey := types.NamespacedName{Name: constants.PredictorServiceName(serviceKey.Name), - Namespace: serviceKey.Namespace} + predictorServiceKey := types.NamespacedName{ + Name: constants.PredictorServiceName(serviceKey.Name), + Namespace: serviceKey.Namespace, + } Eventually(func() error { return k8sClient.Get(context.TODO(), predictorServiceKey, actualService) }, timeout). Should(Succeed()) @@ -245,11 +247,11 @@ var _ = Describe("v1beta1 inference service controller", func() { Spec: knservingv1.RevisionSpec{ ContainerConcurrency: isvc.Spec.Predictor.ContainerConcurrency, TimeoutSeconds: isvc.Spec.Predictor.TimeoutSeconds, - PodSpec: v1.PodSpec{ - ImagePullSecrets: []v1.LocalObjectReference{ + PodSpec: corev1.PodSpec{ + ImagePullSecrets: []corev1.LocalObjectReference{ {Name: "sr-image-pull-secret"}, }, - Containers: []v1.Container{ + Containers: []corev1.Container{ { Image: "tensorflow/serving:" + *isvc.Spec.Predictor.Model.RuntimeVersion, @@ -280,7 +282,7 @@ var _ = Describe("v1beta1 inference service controller", func() { // Do a dry-run update. This will populate our local knative service object with any default values // that are present on the remote version. err := k8sClient.Update(context.TODO(), expectedService, client.DryRunAll) - Expect(err).Should(BeNil()) + Expect(err).ShouldNot(HaveOccurred()) Expect(kmp.SafeDiff(actualService.Spec, expectedService.Spec)).To(Equal("")) predictorUrl, _ := apis.ParseURL("http://" + constants.InferenceServiceHostName(constants.DefaultPredictorServiceName(serviceKey.Name), serviceKey.Namespace, domain)) // update predictor @@ -305,11 +307,13 @@ var _ = Describe("v1beta1 inference service controller", func() { } Expect(k8sClient.Status().Update(context.TODO(), updatedService)).NotTo(HaveOccurred()) } - //assert ingress + // assert ingress virtualService := &istioclientv1beta1.VirtualService{} Eventually(func() error { - return k8sClient.Get(context.TODO(), types.NamespacedName{Name: serviceKey.Name, - Namespace: serviceKey.Namespace}, virtualService) + return k8sClient.Get(context.TODO(), types.NamespacedName{ + Name: serviceKey.Name, + Namespace: serviceKey.Namespace, + }, virtualService) }, timeout). Should(Succeed()) expectedVirtualService := &istioclientv1beta1.VirtualService{ @@ -367,17 +371,14 @@ var _ = Describe("v1beta1 inference service controller", func() { } Expect(virtualService.Spec.DeepCopy()).To(Equal(expectedVirtualService.Spec.DeepCopy())) - //get inference service + // get inference service time.Sleep(10 * time.Second) actualIsvc := &v1beta1.InferenceService{} Eventually(func() bool { err := k8sClient.Get(ctx, expectedRequest.NamespacedName, actualIsvc) - if err != nil { - return false - } - return true + return err == nil }, timeout, interval).Should(BeTrue()) - //update inference service with annotations and labels + // update inference service with annotations and labels annotations := map[string]string{"testAnnotation": "test"} labels := map[string]string{"testLabel": "test"} updatedIsvc := actualIsvc.DeepCopy() @@ -388,8 +389,10 @@ var _ = Describe("v1beta1 inference service controller", func() { time.Sleep(10 * time.Second) updatedVirtualService := &istioclientv1beta1.VirtualService{} Eventually(func() error { - return k8sClient.Get(ctx, types.NamespacedName{Name: serviceKey.Name, - Namespace: serviceKey.Namespace}, updatedVirtualService) + return k8sClient.Get(ctx, types.NamespacedName{ + Name: serviceKey.Name, + Namespace: serviceKey.Namespace, + }, updatedVirtualService) }, timeout, interval).Should(Succeed()) Expect(updatedVirtualService.Spec.DeepCopy()).To(Equal(expectedVirtualService.Spec.DeepCopy())) @@ -399,12 +402,12 @@ var _ = Describe("v1beta1 inference service controller", func() { It("Should fail if Knative Serving is not installed", func() { // Simulate Knative Serving is absent by setting to false the relevant item in utils.gvResourcesCache variable servingResources, getServingResourcesErr := utils.GetAvailableResourcesForApi(cfg, knservingv1.SchemeGroupVersion.String()) - Expect(getServingResourcesErr).To(BeNil()) + Expect(getServingResourcesErr).ToNot(HaveOccurred()) defer utils.SetAvailableResourcesForApi(knservingv1.SchemeGroupVersion.String(), servingResources) utils.SetAvailableResourcesForApi(knservingv1.SchemeGroupVersion.String(), nil) // Create configmap - var configMap = &v1.ConfigMap{ + configMap := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: constants.InferenceServiceConfigMapName, Namespace: constants.KServeNamespace, @@ -416,9 +419,9 @@ var _ = Describe("v1beta1 inference service controller", func() { // Create InferenceService serviceName := "serverless-isvc" - var expectedRequest = reconcile.Request{NamespacedName: types.NamespacedName{Name: serviceName, Namespace: "default"}} - var serviceKey = expectedRequest.NamespacedName - var storageUri = "s3://test/mnist/export" + expectedRequest := reconcile.Request{NamespacedName: types.NamespacedName{Name: serviceName, Namespace: "default"}} + serviceKey := expectedRequest.NamespacedName + storageUri := "s3://test/mnist/export" isvc := &v1beta1.InferenceService{ ObjectMeta: metav1.ObjectMeta{ Name: serviceKey.Name, @@ -430,14 +433,14 @@ var _ = Describe("v1beta1 inference service controller", func() { Spec: v1beta1.InferenceServiceSpec{ Predictor: v1beta1.PredictorSpec{ ComponentExtensionSpec: v1beta1.ComponentExtensionSpec{ - MinReplicas: v1beta1.GetIntReference(1), + MinReplicas: ptr.To(int32(1)), MaxReplicas: 3, }, Tensorflow: &v1beta1.TFServingSpec{ PredictorExtensionSpec: v1beta1.PredictorExtensionSpec{ StorageURI: &storageUri, RuntimeVersion: proto.String("1.14.0"), - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, Resources: defaultResource, }, @@ -453,14 +456,11 @@ var _ = Describe("v1beta1 inference service controller", func() { defer k8sClient.Delete(ctx, isvc) Eventually(func() bool { - events := &v1.EventList{} + events := &corev1.EventList{} err := k8sClient.List(ctx, events, client.InNamespace(serviceKey.Namespace)) if err != nil { return false } - if events == nil { - return false - } for _, event := range events.Items { if event.InvolvedObject.Kind == "InferenceService" && @@ -479,14 +479,18 @@ var _ = Describe("v1beta1 inference service controller", func() { It("Should create successfully", func() { serviceName := "svc-with-transformer" namespace := "default" - var expectedRequest = reconcile.Request{NamespacedName: types.NamespacedName{Name: serviceName, Namespace: namespace}} - var serviceKey = expectedRequest.NamespacedName - - var predictorServiceKey = types.NamespacedName{Name: constants.PredictorServiceName(serviceName), - Namespace: namespace} - var transformerServiceKey = types.NamespacedName{Name: constants.TransformerServiceName(serviceName), - Namespace: namespace} - var transformer = &v1beta1.InferenceService{ + expectedRequest := reconcile.Request{NamespacedName: types.NamespacedName{Name: serviceName, Namespace: namespace}} + serviceKey := expectedRequest.NamespacedName + + predictorServiceKey := types.NamespacedName{ + Name: constants.PredictorServiceName(serviceName), + Namespace: namespace, + } + transformerServiceKey := types.NamespacedName{ + Name: constants.TransformerServiceName(serviceName), + Namespace: namespace, + } + transformer := &v1beta1.InferenceService{ ObjectMeta: metav1.ObjectMeta{ Name: serviceName, Namespace: namespace, @@ -503,7 +507,7 @@ var _ = Describe("v1beta1 inference service controller", func() { Spec: v1beta1.InferenceServiceSpec{ Predictor: v1beta1.PredictorSpec{ ComponentExtensionSpec: v1beta1.ComponentExtensionSpec{ - MinReplicas: v1beta1.GetIntReference(1), + MinReplicas: ptr.To(int32(1)), MaxReplicas: 3, Labels: map[string]string{ "key2": "val2FromPredictor", @@ -521,7 +525,7 @@ var _ = Describe("v1beta1 inference service controller", func() { }, Transformer: &v1beta1.TransformerSpec{ ComponentExtensionSpec: v1beta1.ComponentExtensionSpec{ - MinReplicas: v1beta1.GetIntReference(1), + MinReplicas: ptr.To(int32(1)), MaxReplicas: 3, Labels: map[string]string{ "key2": "val2FromTransformer", @@ -531,7 +535,7 @@ var _ = Describe("v1beta1 inference service controller", func() { }, }, PodSpec: v1beta1.PodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Image: "transformer:v1", Resources: defaultResource, @@ -550,7 +554,7 @@ var _ = Describe("v1beta1 inference service controller", func() { } // Create configmap - var configMap = &v1.ConfigMap{ + configMap := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: constants.InferenceServiceConfigMapName, Namespace: constants.KServeNamespace, @@ -574,7 +578,7 @@ var _ = Describe("v1beta1 inference service controller", func() { }, }, ServingRuntimePodSpec: v1alpha1.ServingRuntimePodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, Image: "tensorflow/serving:1.14.0", @@ -616,10 +620,11 @@ var _ = Describe("v1beta1 inference service controller", func() { ConfigurationSpec: knservingv1.ConfigurationSpec{ Template: knservingv1.RevisionTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{"serving.kserve.io/inferenceservice": serviceName, - constants.KServiceComponentLabel: constants.Transformer.String(), - "key1": "val1FromISVC", - "key2": "val2FromTransformer", + Labels: map[string]string{ + "serving.kserve.io/inferenceservice": serviceName, + constants.KServiceComponentLabel: constants.Transformer.String(), + "key1": "val1FromISVC", + "key2": "val2FromTransformer", }, Annotations: map[string]string{ "serving.kserve.io/deploymentMode": "Serverless", @@ -633,8 +638,8 @@ var _ = Describe("v1beta1 inference service controller", func() { Spec: knservingv1.RevisionSpec{ ContainerConcurrency: nil, TimeoutSeconds: nil, - PodSpec: v1.PodSpec{ - Containers: []v1.Container{ + PodSpec: corev1.PodSpec{ + Containers: []corev1.Container{ { Image: "transformer:v1", Args: []string{ @@ -665,7 +670,7 @@ var _ = Describe("v1beta1 inference service controller", func() { // Do a dry-run update. This will populate our local knative service object with any default values // that are present on the remote version. err := k8sClient.Update(context.TODO(), expectedTransformerService, client.DryRunAll) - Expect(err).Should(BeNil()) + Expect(err).ShouldNot(HaveOccurred()) Expect(cmp.Diff(transformerService.Spec, expectedTransformerService.Spec)).To(Equal("")) // mock update knative service status since knative serving controller is not running in test @@ -808,7 +813,7 @@ var _ = Describe("v1beta1 inference service controller", func() { It("Should have traffic split between two revisions", func() { By("By moving canary traffic percent to the latest revision") // Create configmap - var configMap = &v1.ConfigMap{ + configMap := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: constants.InferenceServiceConfigMapName, Namespace: constants.KServeNamespace, @@ -832,7 +837,7 @@ var _ = Describe("v1beta1 inference service controller", func() { }, }, ServingRuntimePodSpec: v1alpha1.ServingRuntimePodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, Image: "tensorflow/serving:1.14.0", @@ -854,10 +859,10 @@ var _ = Describe("v1beta1 inference service controller", func() { defer k8sClient.Delete(context.TODO(), servingRuntime) // Create Canary InferenceService serviceName := "foo-canary" - var expectedRequest = reconcile.Request{NamespacedName: types.NamespacedName{Name: serviceName, Namespace: "default"}} - var serviceKey = expectedRequest.NamespacedName - var storageUri = "s3://test/mnist/export" - var storageUri2 = "s3://test/mnist/export/v2" + expectedRequest := reconcile.Request{NamespacedName: types.NamespacedName{Name: serviceName, Namespace: "default"}} + serviceKey := expectedRequest.NamespacedName + storageUri := "s3://test/mnist/export" + storageUri2 := "s3://test/mnist/export/v2" ctx := context.Background() isvc := &v1beta1.InferenceService{ ObjectMeta: metav1.ObjectMeta{ @@ -867,14 +872,14 @@ var _ = Describe("v1beta1 inference service controller", func() { Spec: v1beta1.InferenceServiceSpec{ Predictor: v1beta1.PredictorSpec{ ComponentExtensionSpec: v1beta1.ComponentExtensionSpec{ - MinReplicas: v1beta1.GetIntReference(1), + MinReplicas: ptr.To(int32(1)), MaxReplicas: 3, }, Tensorflow: &v1beta1.TFServingSpec{ PredictorExtensionSpec: v1beta1.PredictorExtensionSpec{ StorageURI: &storageUri, RuntimeVersion: proto.String("1.14.0"), - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, Resources: defaultResource, }, @@ -889,15 +894,14 @@ var _ = Describe("v1beta1 inference service controller", func() { Eventually(func() bool { err := k8sClient.Get(ctx, serviceKey, inferenceService) - if err != nil { - return false - } - return true + return err == nil }, timeout, interval).Should(BeTrue()) updatedService := &knservingv1.Service{} - predictorServiceKey := types.NamespacedName{Name: constants.PredictorServiceName(serviceKey.Name), - Namespace: serviceKey.Namespace} + predictorServiceKey := types.NamespacedName{ + Name: constants.PredictorServiceName(serviceKey.Name), + Namespace: serviceKey.Namespace, + } Eventually(func() error { return k8sClient.Get(context.TODO(), predictorServiceKey, updatedService) }, timeout). Should(Succeed()) @@ -917,7 +921,8 @@ var _ = Describe("v1beta1 inference service controller", func() { LatestRevision: proto.Bool(true), RevisionName: "revision-v1", Percent: proto.Int64(100), - }} + }, + } Expect(retry.RetryOnConflict(retry.DefaultBackoff, func() error { return k8sClient.Status().Update(context.TODO(), updatedService) })).NotTo(HaveOccurred()) @@ -1025,7 +1030,8 @@ var _ = Describe("v1beta1 inference service controller", func() { LatestRevision: proto.Bool(true), RevisionName: "revision-v2", Percent: proto.Int64(100), - }} + }, + } Expect(k8sClient.Status().Update(context.TODO(), serviceRevision2)).NotTo(HaveOccurred()) // assert latest rolled out revision expectedIsvc := &v1beta1.InferenceService{} @@ -1049,7 +1055,7 @@ var _ = Describe("v1beta1 inference service controller", func() { Context("When creating and deleting inference service without storageUri (multi-model inferenceservice)", func() { // Create configmap - var configMap = &v1.ConfigMap{ + configMap := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: constants.InferenceServiceConfigMapName, Namespace: constants.KServeNamespace, @@ -1058,10 +1064,12 @@ var _ = Describe("v1beta1 inference service controller", func() { } serviceName := "bar" - var expectedRequest = reconcile.Request{NamespacedName: types.NamespacedName{Name: serviceName, Namespace: "default"}} - var serviceKey = expectedRequest.NamespacedName - var modelConfigMapKey = types.NamespacedName{Name: constants.ModelConfigName(serviceName, 0), - Namespace: serviceKey.Namespace} + expectedRequest := reconcile.Request{NamespacedName: types.NamespacedName{Name: serviceName, Namespace: "default"}} + serviceKey := expectedRequest.NamespacedName + modelConfigMapKey := types.NamespacedName{ + Name: constants.ModelConfigName(serviceName, 0), + Namespace: serviceKey.Namespace, + } ctx := context.Background() instance := &v1beta1.InferenceService{ @@ -1072,7 +1080,7 @@ var _ = Describe("v1beta1 inference service controller", func() { Spec: v1beta1.InferenceServiceSpec{ Predictor: v1beta1.PredictorSpec{ ComponentExtensionSpec: v1beta1.ComponentExtensionSpec{ - MinReplicas: v1beta1.GetIntReference(1), + MinReplicas: ptr.To(int32(1)), MaxReplicas: 3, }, SKLearn: &v1beta1.SKLearnSpec{ @@ -1092,23 +1100,20 @@ var _ = Describe("v1beta1 inference service controller", func() { inferenceService := &v1beta1.InferenceService{} Eventually(func() bool { - //Check if InferenceService is created + // Check if InferenceService is created err := k8sClient.Get(ctx, serviceKey, inferenceService) - if err != nil { - return false - } - return true + return err == nil }, timeout, interval).Should(BeTrue()) - modelConfigMap := &v1.ConfigMap{} + modelConfigMap := &corev1.ConfigMap{} Eventually(func() bool { - //Check if modelconfig is created + // Check if modelconfig is created err := k8sClient.Get(ctx, modelConfigMapKey, modelConfigMap) if err != nil { return false } - //Verify that this configmap's ownerreference is it's parent InferenceService + // Verify that this configmap's ownerreference is it's parent InferenceService Expect(modelConfigMap.OwnerReferences[0].Name).To(Equal(serviceKey.Name)) return true @@ -1121,8 +1126,10 @@ var _ = Describe("v1beta1 inference service controller", func() { serviceName := "svc-with-servingruntime" namespace := "default" - var predictorServiceKey = types.NamespacedName{Name: constants.PredictorServiceName(serviceName), - Namespace: namespace} + predictorServiceKey := types.NamespacedName{ + Name: constants.PredictorServiceName(serviceName), + Namespace: namespace, + } servingRuntime := &v1alpha1.ServingRuntime{ ObjectMeta: metav1.ObjectMeta{ Name: "tf-serving", @@ -1145,7 +1152,7 @@ var _ = Describe("v1beta1 inference service controller", func() { "key1": "val1FromSR", "key2": "val2FromSR", }, - Containers: []v1.Container{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, Image: "tensorflow/serving:1.14.0", @@ -1159,7 +1166,7 @@ var _ = Describe("v1beta1 inference service controller", func() { Resources: defaultResource, }, }, - ImagePullSecrets: []v1.LocalObjectReference{ + ImagePullSecrets: []corev1.LocalObjectReference{ {Name: "sr-image-pull-secret"}, }, }, @@ -1169,7 +1176,7 @@ var _ = Describe("v1beta1 inference service controller", func() { Expect(k8sClient.Create(context.TODO(), servingRuntime)).NotTo(HaveOccurred()) defer k8sClient.Delete(context.TODO(), servingRuntime) - var isvc = &v1beta1.InferenceService{ + isvc := &v1beta1.InferenceService{ ObjectMeta: metav1.ObjectMeta{ Name: serviceName, Namespace: namespace, @@ -1183,7 +1190,7 @@ var _ = Describe("v1beta1 inference service controller", func() { Spec: v1beta1.InferenceServiceSpec{ Predictor: v1beta1.PredictorSpec{ ComponentExtensionSpec: v1beta1.ComponentExtensionSpec{ - MinReplicas: v1beta1.GetIntReference(1), + MinReplicas: ptr.To(int32(1)), MaxReplicas: 3, }, Model: &v1beta1.ModelSpec{ @@ -1195,7 +1202,7 @@ var _ = Describe("v1beta1 inference service controller", func() { }, }, PodSpec: v1beta1.PodSpec{ - ImagePullSecrets: []v1.LocalObjectReference{ + ImagePullSecrets: []corev1.LocalObjectReference{ {Name: "isvc-image-pull-secret"}, }, }, @@ -1211,7 +1218,7 @@ var _ = Describe("v1beta1 inference service controller", func() { } // Create configmap - var configMap = &v1.ConfigMap{ + configMap := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: constants.InferenceServiceConfigMapName, Namespace: constants.KServeNamespace, @@ -1239,10 +1246,11 @@ var _ = Describe("v1beta1 inference service controller", func() { ConfigurationSpec: knservingv1.ConfigurationSpec{ Template: knservingv1.RevisionTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{"serving.kserve.io/inferenceservice": serviceName, - constants.KServiceComponentLabel: constants.Predictor.String(), - "key1": "val1FromSR", - "key2": "val2FromISVC", + Labels: map[string]string{ + "serving.kserve.io/inferenceservice": serviceName, + constants.KServiceComponentLabel: constants.Predictor.String(), + "key1": "val1FromSR", + "key2": "val2FromISVC", }, Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "s3://test/mnist/export", @@ -1256,8 +1264,8 @@ var _ = Describe("v1beta1 inference service controller", func() { Spec: knservingv1.RevisionSpec{ ContainerConcurrency: nil, TimeoutSeconds: nil, - PodSpec: v1.PodSpec{ - Containers: []v1.Container{ + PodSpec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, Image: "tensorflow/serving:1.14.0", @@ -1271,7 +1279,7 @@ var _ = Describe("v1beta1 inference service controller", func() { Resources: defaultResource, }, }, - ImagePullSecrets: []v1.LocalObjectReference{ + ImagePullSecrets: []corev1.LocalObjectReference{ {Name: "isvc-image-pull-secret"}, {Name: "sr-image-pull-secret"}, }, @@ -1291,21 +1299,22 @@ var _ = Describe("v1beta1 inference service controller", func() { // Do a dry-run update. This will populate our local knative service object with any default values // that are present on the remote version. err := k8sClient.Update(context.TODO(), predictorService, client.DryRunAll) - Expect(err).Should(BeNil()) + Expect(err).ShouldNot(HaveOccurred()) Expect(cmp.Diff(predictorService.Spec, expectedPredictorService.Spec)).To(Equal("")) - }) }) Context("When creating an inference service with a ServingRuntime which does not exists", func() { It("Should fail with reason RuntimeNotRecognized", func() { + ctx, cancel := context.WithCancel(context.Background()) + DeferCleanup(cancel) serviceName := "svc-with-unknown-servingruntime" servingRuntimeName := "tf-serving-unknown" namespace := "default" - var predictorServiceKey = types.NamespacedName{Name: serviceName, Namespace: namespace} + predictorServiceKey := types.NamespacedName{Name: serviceName, Namespace: namespace} - var isvc = &v1beta1.InferenceService{ + isvc := &v1beta1.InferenceService{ ObjectMeta: metav1.ObjectMeta{ Name: serviceName, Namespace: namespace, @@ -1313,7 +1322,7 @@ var _ = Describe("v1beta1 inference service controller", func() { Spec: v1beta1.InferenceServiceSpec{ Predictor: v1beta1.PredictorSpec{ ComponentExtensionSpec: v1beta1.ComponentExtensionSpec{ - MinReplicas: v1beta1.GetIntReference(1), + MinReplicas: ptr.To(int32(1)), MaxReplicas: 3, }, Model: &v1beta1.ModelSpec{ @@ -1330,7 +1339,7 @@ var _ = Describe("v1beta1 inference service controller", func() { } // Create configmap - var configMap = &v1.ConfigMap{ + configMap := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: constants.InferenceServiceConfigMapName, Namespace: constants.KServeNamespace, @@ -1356,7 +1365,7 @@ var _ = Describe("v1beta1 inference service controller", func() { return true }, timeout, interval).Should(BeTrue()) - var failureInfo = v1beta1.FailureInfo{ + failureInfo := v1beta1.FailureInfo{ Reason: v1beta1.RuntimeNotRecognized, Message: "Waiting for runtime to become available", } @@ -1368,13 +1377,15 @@ var _ = Describe("v1beta1 inference service controller", func() { Context("When creating an inference service with a ServingRuntime which is disabled", func() { It("Should fail with reason RuntimeDisabled", func() { + ctx, cancel := context.WithCancel(context.Background()) + DeferCleanup(cancel) serviceName := "svc-with-disabled-servingruntime" servingRuntimeName := "tf-serving-disabled" namespace := "default" - var predictorServiceKey = types.NamespacedName{Name: serviceName, Namespace: namespace} + predictorServiceKey := types.NamespacedName{Name: serviceName, Namespace: namespace} - var servingRuntime = &v1alpha1.ServingRuntime{ + servingRuntime := &v1alpha1.ServingRuntime{ ObjectMeta: metav1.ObjectMeta{ Name: servingRuntimeName, Namespace: namespace, @@ -1388,7 +1399,7 @@ var _ = Describe("v1beta1 inference service controller", func() { }, }, ServingRuntimePodSpec: v1alpha1.ServingRuntimePodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, Image: "tensorflow/serving:1.14.0", @@ -1410,7 +1421,7 @@ var _ = Describe("v1beta1 inference service controller", func() { Expect(k8sClient.Create(context.TODO(), servingRuntime)).NotTo(HaveOccurred()) defer k8sClient.Delete(context.TODO(), servingRuntime) - var isvc = &v1beta1.InferenceService{ + isvc := &v1beta1.InferenceService{ ObjectMeta: metav1.ObjectMeta{ Name: serviceName, Namespace: namespace, @@ -1418,7 +1429,7 @@ var _ = Describe("v1beta1 inference service controller", func() { Spec: v1beta1.InferenceServiceSpec{ Predictor: v1beta1.PredictorSpec{ ComponentExtensionSpec: v1beta1.ComponentExtensionSpec{ - MinReplicas: v1beta1.GetIntReference(1), + MinReplicas: ptr.To(int32(1)), MaxReplicas: 3, }, Model: &v1beta1.ModelSpec{ @@ -1435,7 +1446,7 @@ var _ = Describe("v1beta1 inference service controller", func() { } // Create configmap - var configMap = &v1.ConfigMap{ + configMap := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: constants.InferenceServiceConfigMapName, Namespace: constants.KServeNamespace, @@ -1461,7 +1472,7 @@ var _ = Describe("v1beta1 inference service controller", func() { return true }, timeout, interval).Should(BeTrue()) - var failureInfo = v1beta1.FailureInfo{ + failureInfo := v1beta1.FailureInfo{ Reason: v1beta1.RuntimeDisabled, Message: "Specified runtime is disabled", } @@ -1473,14 +1484,16 @@ var _ = Describe("v1beta1 inference service controller", func() { Context("When creating an inference service with a ServingRuntime which does not support specified model format", func() { It("Should fail with reason NoSupportingRuntime", func() { + ctx, cancel := context.WithCancel(context.Background()) + DeferCleanup(cancel) serviceName := "svc-with-unsupported-servingruntime" servingRuntimeName := "tf-serving-unsupported" namespace := "default" - var predictorServiceKey = types.NamespacedName{Name: serviceName, Namespace: namespace} + predictorServiceKey := types.NamespacedName{Name: serviceName, Namespace: namespace} // Create configmap - var configMap = &v1.ConfigMap{ + configMap := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: constants.InferenceServiceConfigMapName, Namespace: constants.KServeNamespace, @@ -1490,7 +1503,7 @@ var _ = Describe("v1beta1 inference service controller", func() { Expect(k8sClient.Create(context.TODO(), configMap)).NotTo(HaveOccurred()) defer k8sClient.Delete(context.TODO(), configMap) - var servingRuntime = &v1alpha1.ServingRuntime{ + servingRuntime := &v1alpha1.ServingRuntime{ ObjectMeta: metav1.ObjectMeta{ Name: servingRuntimeName, Namespace: namespace, @@ -1504,7 +1517,7 @@ var _ = Describe("v1beta1 inference service controller", func() { }, }, ServingRuntimePodSpec: v1alpha1.ServingRuntimePodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, Image: "tensorflow/serving:1.14.0", @@ -1526,7 +1539,7 @@ var _ = Describe("v1beta1 inference service controller", func() { Expect(k8sClient.Create(context.TODO(), servingRuntime)).NotTo(HaveOccurred()) defer k8sClient.Delete(context.TODO(), servingRuntime) - var isvc = &v1beta1.InferenceService{ + isvc := &v1beta1.InferenceService{ ObjectMeta: metav1.ObjectMeta{ Name: serviceName, Namespace: namespace, @@ -1534,7 +1547,7 @@ var _ = Describe("v1beta1 inference service controller", func() { Spec: v1beta1.InferenceServiceSpec{ Predictor: v1beta1.PredictorSpec{ ComponentExtensionSpec: v1beta1.ComponentExtensionSpec{ - MinReplicas: v1beta1.GetIntReference(1), + MinReplicas: ptr.To(int32(1)), MaxReplicas: 3, }, Model: &v1beta1.ModelSpec{ @@ -1566,7 +1579,7 @@ var _ = Describe("v1beta1 inference service controller", func() { return true }, timeout, interval).Should(BeTrue()) - var failureInfo = v1beta1.FailureInfo{ + failureInfo := v1beta1.FailureInfo{ Reason: v1beta1.NoSupportingRuntime, Message: "Specified runtime does not support specified framework/version", } @@ -1580,17 +1593,17 @@ var _ = Describe("v1beta1 inference service controller", func() { defaultIsvc := func(namespace string, name string, storageUri string) *v1beta1.InferenceService { predictor := v1beta1.PredictorSpec{ ComponentExtensionSpec: v1beta1.ComponentExtensionSpec{ - MinReplicas: v1beta1.GetIntReference(1), + MinReplicas: ptr.To(int32(1)), MaxReplicas: 3, }, Tensorflow: &v1beta1.TFServingSpec{ PredictorExtensionSpec: v1beta1.PredictorExtensionSpec{ StorageURI: &storageUri, RuntimeVersion: proto.String("1.14.0"), - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, Resources: defaultResource, - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ {Name: "predictor-volume"}, }, }, @@ -1626,7 +1639,7 @@ var _ = Describe("v1beta1 inference service controller", func() { }, }, ServingRuntimePodSpec: v1alpha1.ServingRuntimePodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, Image: "tensorflow/serving:1.14.0", @@ -1640,19 +1653,19 @@ var _ = Describe("v1beta1 inference service controller", func() { Resources: defaultResource, }, }, - ImagePullSecrets: []v1.LocalObjectReference{ + ImagePullSecrets: []corev1.LocalObjectReference{ {Name: "sr-image-pull-secret"}, }, }, Disabled: proto.Bool(false), }, } - Expect(k8sClient.Create(ctx, servingRuntime)).NotTo(HaveOccurred()) + Expect(k8sClient.Create(context.TODO(), servingRuntime)).NotTo(HaveOccurred()) return servingRuntime } - createInferenceServiceConfigMap := func() *v1.ConfigMap { - configMap := &v1.ConfigMap{ + createInferenceServiceConfigMap := func() *corev1.ConfigMap { + configMap := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: constants.InferenceServiceConfigMapName, Namespace: constants.KServeNamespace, @@ -1665,14 +1678,16 @@ var _ = Describe("v1beta1 inference service controller", func() { } It("should have the readonly annotation set to true in the knative serving pod spec", func() { + ctx, cancel := context.WithCancel(context.Background()) + DeferCleanup(cancel) configMap := createInferenceServiceConfigMap() defer k8sClient.Delete(ctx, configMap) serviceName := "readonly-true-isvc" serviceNamespace := "default" - var expectedRequest = reconcile.Request{NamespacedName: types.NamespacedName{Name: serviceName, Namespace: serviceNamespace}} - var serviceKey = expectedRequest.NamespacedName - var storageUri = "s3://test/mnist/export" + expectedRequest := reconcile.Request{NamespacedName: types.NamespacedName{Name: serviceName, Namespace: serviceNamespace}} + serviceKey := expectedRequest.NamespacedName + storageUri := "s3://test/mnist/export" servingRuntime := createServingRuntime(serviceKey.Namespace, "tf-serving") defer k8sClient.Delete(ctx, servingRuntime) @@ -1686,8 +1701,10 @@ var _ = Describe("v1beta1 inference service controller", func() { // Knative service actualService := &knservingv1.Service{} - predictorServiceKey := types.NamespacedName{Name: constants.PredictorServiceName(serviceKey.Name), - Namespace: serviceKey.Namespace} + predictorServiceKey := types.NamespacedName{ + Name: constants.PredictorServiceName(serviceKey.Name), + Namespace: serviceKey.Namespace, + } Eventually(func() error { return k8sClient.Get(context.TODO(), predictorServiceKey, actualService) }, timeout). Should(Succeed()) @@ -1697,14 +1714,16 @@ var _ = Describe("v1beta1 inference service controller", func() { }) It("should have the readonly annotation set to false in the knative serving pod spec", func() { + ctx, cancel := context.WithCancel(context.Background()) + DeferCleanup(cancel) configMap := createInferenceServiceConfigMap() defer k8sClient.Delete(ctx, configMap) serviceName := "readonly-false-isvc" serviceNamespace := "default" - var expectedRequest = reconcile.Request{NamespacedName: types.NamespacedName{Name: serviceName, Namespace: serviceNamespace}} - var serviceKey = expectedRequest.NamespacedName - var storageUri = "s3://test/mnist/export" + expectedRequest := reconcile.Request{NamespacedName: types.NamespacedName{Name: serviceName, Namespace: serviceNamespace}} + serviceKey := expectedRequest.NamespacedName + storageUri := "s3://test/mnist/export" servingRuntime := createServingRuntime(serviceKey.Namespace, "tf-serving") defer k8sClient.Delete(ctx, servingRuntime) @@ -1718,8 +1737,10 @@ var _ = Describe("v1beta1 inference service controller", func() { // Knative service actualService := &knservingv1.Service{} - predictorServiceKey := types.NamespacedName{Name: constants.PredictorServiceName(serviceKey.Name), - Namespace: serviceKey.Namespace} + predictorServiceKey := types.NamespacedName{ + Name: constants.PredictorServiceName(serviceKey.Name), + Namespace: serviceKey.Namespace, + } Eventually(func() error { return k8sClient.Get(context.TODO(), predictorServiceKey, actualService) }, timeout). Should(Succeed()) @@ -1731,13 +1752,15 @@ var _ = Describe("v1beta1 inference service controller", func() { Context("When creating an inference service with invalid Storage URI", func() { It("Should fail with reason ModelLoadFailed", func() { + ctx, cancel := context.WithCancel(context.Background()) + DeferCleanup(cancel) serviceName := "servingruntime-test" servingRuntimeName := "tf-serving" namespace := "default" - var inferenceServiceKey = types.NamespacedName{Name: serviceName, Namespace: namespace} + inferenceServiceKey := types.NamespacedName{Name: serviceName, Namespace: namespace} // Create configmap - var configMap = &v1.ConfigMap{ + configMap := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: constants.InferenceServiceConfigMapName, Namespace: constants.KServeNamespace, @@ -1747,7 +1770,7 @@ var _ = Describe("v1beta1 inference service controller", func() { Expect(k8sClient.Create(context.TODO(), configMap)).NotTo(HaveOccurred()) defer k8sClient.Delete(context.TODO(), configMap) - var servingRuntime = &v1alpha1.ServingRuntime{ + servingRuntime := &v1alpha1.ServingRuntime{ ObjectMeta: metav1.ObjectMeta{ Name: servingRuntimeName, Namespace: namespace, @@ -1761,7 +1784,7 @@ var _ = Describe("v1beta1 inference service controller", func() { }, }, ServingRuntimePodSpec: v1alpha1.ServingRuntimePodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, Image: "tensorflow/serving:1.14.0", @@ -1783,7 +1806,7 @@ var _ = Describe("v1beta1 inference service controller", func() { Expect(k8sClient.Create(context.TODO(), servingRuntime)).NotTo(HaveOccurred()) defer k8sClient.Delete(context.TODO(), servingRuntime) - var isvc = &v1beta1.InferenceService{ + isvc := &v1beta1.InferenceService{ ObjectMeta: metav1.ObjectMeta{ Name: serviceName, Namespace: namespace, @@ -1791,7 +1814,7 @@ var _ = Describe("v1beta1 inference service controller", func() { Spec: v1beta1.InferenceServiceSpec{ Predictor: v1beta1.PredictorSpec{ ComponentExtensionSpec: v1beta1.ComponentExtensionSpec{ - MinReplicas: v1beta1.GetIntReference(1), + MinReplicas: ptr.To(int32(1)), MaxReplicas: 3, }, Model: &v1beta1.ModelSpec{ @@ -1811,14 +1834,14 @@ var _ = Describe("v1beta1 inference service controller", func() { Expect(k8sClient.Create(context.TODO(), isvc)).NotTo(HaveOccurred()) defer k8sClient.Delete(context.TODO(), isvc) - pod := &v1.Pod{ + pod := &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: serviceName + "-predictor-" + namespace + "-00001-deployment-76464ds2zpv", Namespace: namespace, Labels: map[string]string{"serving.knative.dev/revision": serviceName + "-predictor-" + namespace + "-00001"}, }, - Spec: v1.PodSpec{ - InitContainers: []v1.Container{ + Spec: corev1.PodSpec{ + InitContainers: []corev1.Container{ { Name: "storage-initializer", Image: "kserve/storage-initializer:latest", @@ -1829,7 +1852,7 @@ var _ = Describe("v1beta1 inference service controller", func() { Resources: defaultResource, }, }, - Containers: []v1.Container{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, Image: "tensorflow/serving:1.14.0", @@ -1840,7 +1863,7 @@ var _ = Describe("v1beta1 inference service controller", func() { "--model_base_path=/mnt/models", "--rest_api_timeout_in_ms=60000", }, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "PORT", Value: "8080", @@ -1865,27 +1888,21 @@ var _ = Describe("v1beta1 inference service controller", func() { } Eventually(func() bool { err := k8sClient.Create(context.TODO(), pod) - if err != nil { - fmt.Printf("Error #%v\n", err) - return false - } - return true + return err == nil }, timeout).Should(BeTrue()) defer k8sClient.Delete(context.TODO(), pod) podStatusPatch := []byte(`{"status":{"containerStatuses":[{"image":"tensorflow/serving:1.14.0","name":"kserve-container","lastState":{},"state":{"waiting":{"reason":"PodInitializing"}}}],"initContainerStatuses":[{"image":"kserve/storage-initializer:latest","name":"storage-initializer","lastState":{"terminated":{"exitCode":1,"message":"Invalid Storage URI provided","reason":"Error"}},"state":{"waiting":{"reason":"CrashLoopBackOff"}}}]}}`) Eventually(func() bool { err := k8sClient.Status().Patch(context.TODO(), pod, client.RawPatch(types.StrategicMergePatchType, podStatusPatch)) - if err != nil { - fmt.Printf("Error #%v\n", err) - return false - } - return true + return err == nil }, timeout).Should(BeTrue()) actualService := &knservingv1.Service{} - predictorServiceKey := types.NamespacedName{Name: constants.PredictorServiceName(serviceName), - Namespace: namespace} + predictorServiceKey := types.NamespacedName{ + Name: constants.PredictorServiceName(serviceName), + Namespace: namespace, + } Eventually(func() error { return k8sClient.Get(context.TODO(), predictorServiceKey, actualService) }, timeout). Should(Succeed()) @@ -1908,7 +1925,6 @@ var _ = Describe("v1beta1 inference service controller", func() { Eventually(func() bool { err := k8sClient.Get(ctx, inferenceServiceKey, inferenceService) if err != nil { - fmt.Printf("Error %#v\n", err) return false } if inferenceService.Status.ModelStatus.LastFailureInfo == nil { @@ -1932,6 +1948,7 @@ var _ = Describe("v1beta1 inference service controller", func() { for key, value := range configs { if key == "ingress" { copiedConfigs[key] = `{ + "kserveIngressGateway": "kserve/kserve-ingress-gateway", "disableIstioVirtualHost": true, "ingressGateway": "knative-serving/knative-ingress-gateway", "localGateway": "knative-serving/knative-local-gateway", @@ -1941,7 +1958,7 @@ var _ = Describe("v1beta1 inference service controller", func() { copiedConfigs[key] = value } } - var configMap = &v1.ConfigMap{ + configMap := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: constants.InferenceServiceConfigMapName, Namespace: constants.KServeNamespace, @@ -1951,9 +1968,9 @@ var _ = Describe("v1beta1 inference service controller", func() { Expect(k8sClient.Create(context.TODO(), configMap)).NotTo(HaveOccurred()) defer k8sClient.Delete(context.TODO(), configMap) serviceName := "foo-disable-istio" - var expectedRequest = reconcile.Request{NamespacedName: types.NamespacedName{Name: serviceName, Namespace: "default"}} - var serviceKey = expectedRequest.NamespacedName - var storageUri = "s3://test/mnist/export" + expectedRequest := reconcile.Request{NamespacedName: types.NamespacedName{Name: serviceName, Namespace: "default"}} + serviceKey := expectedRequest.NamespacedName + storageUri := "s3://test/mnist/export" ctx := context.Background() isvc := &v1beta1.InferenceService{ ObjectMeta: metav1.ObjectMeta{ @@ -1963,14 +1980,14 @@ var _ = Describe("v1beta1 inference service controller", func() { Spec: v1beta1.InferenceServiceSpec{ Predictor: v1beta1.PredictorSpec{ ComponentExtensionSpec: v1beta1.ComponentExtensionSpec{ - MinReplicas: v1beta1.GetIntReference(1), + MinReplicas: ptr.To(int32(1)), MaxReplicas: 3, }, Tensorflow: &v1beta1.TFServingSpec{ PredictorExtensionSpec: v1beta1.PredictorExtensionSpec{ StorageURI: &storageUri, RuntimeVersion: proto.String("1.14.0"), - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, Resources: defaultResource, }, @@ -1985,15 +2002,14 @@ var _ = Describe("v1beta1 inference service controller", func() { Eventually(func() bool { err := k8sClient.Get(ctx, serviceKey, inferenceService) - if err != nil { - return false - } - return true + return err == nil }, timeout, interval).Should(BeTrue()) actualService := &knservingv1.Service{} - predictorServiceKey := types.NamespacedName{Name: constants.PredictorServiceName(serviceKey.Name), - Namespace: serviceKey.Namespace} + predictorServiceKey := types.NamespacedName{ + Name: constants.PredictorServiceName(serviceKey.Name), + Namespace: serviceKey.Namespace, + } Eventually(func() error { return k8sClient.Get(context.TODO(), predictorServiceKey, actualService) }, timeout).Should(Succeed()) @@ -2015,10 +2031,7 @@ var _ = Describe("v1beta1 inference service controller", func() { actualIsvc := &v1beta1.InferenceService{} Eventually(func() bool { err := k8sClient.Get(ctx, expectedRequest.NamespacedName, actualIsvc) - if err != nil { - return false - } - return true + return err == nil }, timeout, interval).Should(BeTrue()) Expect(actualIsvc.Status.URL).To(Equal(&apis.URL{ @@ -2034,7 +2047,7 @@ var _ = Describe("v1beta1 inference service controller", func() { Context("Set CaBundle ConfigMap in inferenceservice-config confimap", func() { It("Should not create a global cabundle configMap in a user namespace when CaBundleConfigMapName set ''", func() { // Create configmap - var configMap = &v1.ConfigMap{ + configMap := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: constants.InferenceServiceConfigMapName, Namespace: constants.KServeNamespace, @@ -2047,9 +2060,9 @@ var _ = Describe("v1beta1 inference service controller", func() { By("By creating a new InferenceService") serviceName := "sample-isvc" - var expectedRequest = reconcile.Request{NamespacedName: types.NamespacedName{Name: serviceName, Namespace: "default"}} - var serviceKey = expectedRequest.NamespacedName - var storageUri = "s3://test/mnist/export" + expectedRequest := reconcile.Request{NamespacedName: types.NamespacedName{Name: serviceName, Namespace: "default"}} + serviceKey := expectedRequest.NamespacedName + storageUri := "s3://test/mnist/export" ctx := context.Background() isvc := &v1beta1.InferenceService{ ObjectMeta: metav1.ObjectMeta{ @@ -2059,14 +2072,14 @@ var _ = Describe("v1beta1 inference service controller", func() { Spec: v1beta1.InferenceServiceSpec{ Predictor: v1beta1.PredictorSpec{ ComponentExtensionSpec: v1beta1.ComponentExtensionSpec{ - MinReplicas: v1beta1.GetIntReference(1), + MinReplicas: ptr.To(int32(1)), MaxReplicas: 3, }, Tensorflow: &v1beta1.TFServingSpec{ PredictorExtensionSpec: v1beta1.PredictorExtensionSpec{ StorageURI: &storageUri, RuntimeVersion: proto.String("1.14.0"), - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, Resources: defaultResource, }, @@ -2077,7 +2090,7 @@ var _ = Describe("v1beta1 inference service controller", func() { } Expect(k8sClient.Create(ctx, isvc)).Should(Succeed()) - caBundleConfigMap := &v1.ConfigMap{} + caBundleConfigMap := &corev1.ConfigMap{} Consistently(func() bool { err := k8sClient.Get(ctx, types.NamespacedName{Name: constants.DefaultGlobalCaBundleConfigMapName, Namespace: "default"}, caBundleConfigMap) if err != nil { @@ -2087,7 +2100,6 @@ var _ = Describe("v1beta1 inference service controller", func() { } return false }, timeout, interval).Should(BeTrue()) - }) It("Should not create a global cabundle configmap in a user namespace when the target cabundle configmap in the 'inferenceservice-config' configmap does not exist", func() { // Create configmap @@ -2109,7 +2121,7 @@ var _ = Describe("v1beta1 inference service controller", func() { } } - var configMap = &v1.ConfigMap{ + configMap := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: constants.InferenceServiceConfigMapName, Namespace: constants.KServeNamespace, @@ -2122,9 +2134,9 @@ var _ = Describe("v1beta1 inference service controller", func() { By("By creating a new InferenceService") serviceName := "sample-isvc-2" - var expectedRequest = reconcile.Request{NamespacedName: types.NamespacedName{Name: serviceName, Namespace: "default"}} - var serviceKey = expectedRequest.NamespacedName - var storageUri = "s3://test/mnist/export" + expectedRequest := reconcile.Request{NamespacedName: types.NamespacedName{Name: serviceName, Namespace: "default"}} + serviceKey := expectedRequest.NamespacedName + storageUri := "s3://test/mnist/export" ctx := context.Background() isvc := &v1beta1.InferenceService{ ObjectMeta: metav1.ObjectMeta{ @@ -2134,14 +2146,14 @@ var _ = Describe("v1beta1 inference service controller", func() { Spec: v1beta1.InferenceServiceSpec{ Predictor: v1beta1.PredictorSpec{ ComponentExtensionSpec: v1beta1.ComponentExtensionSpec{ - MinReplicas: v1beta1.GetIntReference(1), + MinReplicas: ptr.To(int32(1)), MaxReplicas: 3, }, Tensorflow: &v1beta1.TFServingSpec{ PredictorExtensionSpec: v1beta1.PredictorExtensionSpec{ StorageURI: &storageUri, RuntimeVersion: proto.String("1.14.0"), - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, Resources: defaultResource, }, @@ -2153,7 +2165,7 @@ var _ = Describe("v1beta1 inference service controller", func() { Expect(k8sClient.Create(ctx, isvc)).Should(Succeed()) defer k8sClient.Delete(ctx, isvc) - caBundleConfigMap := &v1.ConfigMap{} + caBundleConfigMap := &corev1.ConfigMap{} Consistently(func() bool { err := k8sClient.Get(ctx, types.NamespacedName{Name: constants.DefaultGlobalCaBundleConfigMapName, Namespace: "default"}, caBundleConfigMap) if err != nil { @@ -2163,7 +2175,6 @@ var _ = Describe("v1beta1 inference service controller", func() { } return false }, timeout, interval).Should(BeTrue()) - }) It("Should not create a global cabundle configmap in a user namespace when the cabundle.crt file data does not exist in the target cabundle configmap in the 'inferenceservice-config' configmap", func() { // Create configmap @@ -2184,7 +2195,7 @@ var _ = Describe("v1beta1 inference service controller", func() { copiedConfigs[key] = value } } - var configMap = &v1.ConfigMap{ + configMap := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: constants.InferenceServiceConfigMapName, Namespace: constants.KServeNamespace, @@ -2198,7 +2209,7 @@ var _ = Describe("v1beta1 inference service controller", func() { // Create original cabundle configmap with wrong file name cabundleConfigMapData := make(map[string]string) cabundleConfigMapData["wrong-cabundle-name.crt"] = "SAMPLE_CA_BUNDLE" - var originalCabundleConfigMap = &v1.ConfigMap{ + originalCabundleConfigMap := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: "test-cabundle-with-wrong-file-name", Namespace: constants.KServeNamespace, @@ -2211,9 +2222,9 @@ var _ = Describe("v1beta1 inference service controller", func() { By("By creating a new InferenceService") serviceName := "sample-isvc-3" - var expectedRequest = reconcile.Request{NamespacedName: types.NamespacedName{Name: serviceName, Namespace: "default"}} - var serviceKey = expectedRequest.NamespacedName - var storageUri = "s3://test/mnist/export" + expectedRequest := reconcile.Request{NamespacedName: types.NamespacedName{Name: serviceName, Namespace: "default"}} + serviceKey := expectedRequest.NamespacedName + storageUri := "s3://test/mnist/export" ctx := context.Background() isvc := &v1beta1.InferenceService{ ObjectMeta: metav1.ObjectMeta{ @@ -2223,14 +2234,14 @@ var _ = Describe("v1beta1 inference service controller", func() { Spec: v1beta1.InferenceServiceSpec{ Predictor: v1beta1.PredictorSpec{ ComponentExtensionSpec: v1beta1.ComponentExtensionSpec{ - MinReplicas: v1beta1.GetIntReference(1), + MinReplicas: ptr.To(int32(1)), MaxReplicas: 3, }, Tensorflow: &v1beta1.TFServingSpec{ PredictorExtensionSpec: v1beta1.PredictorExtensionSpec{ StorageURI: &storageUri, RuntimeVersion: proto.String("1.14.0"), - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, Resources: defaultResource, }, @@ -2242,7 +2253,7 @@ var _ = Describe("v1beta1 inference service controller", func() { Expect(k8sClient.Create(ctx, isvc)).Should(Succeed()) defer k8sClient.Delete(ctx, isvc) - caBundleConfigMap := &v1.ConfigMap{} + caBundleConfigMap := &corev1.ConfigMap{} Consistently(func() bool { err := k8sClient.Get(ctx, types.NamespacedName{Name: constants.DefaultGlobalCaBundleConfigMapName, Namespace: "default"}, caBundleConfigMap) if err != nil { @@ -2273,7 +2284,7 @@ var _ = Describe("v1beta1 inference service controller", func() { copiedConfigs[key] = value } } - var configMap = &v1.ConfigMap{ + configMap := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: constants.InferenceServiceConfigMapName, Namespace: constants.KServeNamespace, @@ -2284,11 +2295,11 @@ var _ = Describe("v1beta1 inference service controller", func() { Expect(k8sClient.Create(context.TODO(), configMap)).NotTo(HaveOccurred()) defer k8sClient.Delete(context.TODO(), configMap) - //Create original cabundle configmap with right file name + // Create original cabundle configmap with right file name cabundleConfigMapData := make(map[string]string) // cabundle data cabundleConfigMapData["cabundle.crt"] = "SAMPLE_CA_BUNDLE" - var originalCabundleConfigMap = &v1.ConfigMap{ + originalCabundleConfigMap := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: "test-cabundle-with-right-file-name", Namespace: constants.KServeNamespace, @@ -2301,9 +2312,9 @@ var _ = Describe("v1beta1 inference service controller", func() { By("By creating a new InferenceService") serviceName := "sample-isvc-4" - var expectedRequest = reconcile.Request{NamespacedName: types.NamespacedName{Name: serviceName, Namespace: "default"}} - var serviceKey = expectedRequest.NamespacedName - var storageUri = "s3://test/mnist/export" + expectedRequest := reconcile.Request{NamespacedName: types.NamespacedName{Name: serviceName, Namespace: "default"}} + serviceKey := expectedRequest.NamespacedName + storageUri := "s3://test/mnist/export" ctx := context.Background() isvc := &v1beta1.InferenceService{ ObjectMeta: metav1.ObjectMeta{ @@ -2313,14 +2324,14 @@ var _ = Describe("v1beta1 inference service controller", func() { Spec: v1beta1.InferenceServiceSpec{ Predictor: v1beta1.PredictorSpec{ ComponentExtensionSpec: v1beta1.ComponentExtensionSpec{ - MinReplicas: v1beta1.GetIntReference(1), + MinReplicas: ptr.To(int32(1)), MaxReplicas: 3, }, Tensorflow: &v1beta1.TFServingSpec{ PredictorExtensionSpec: v1beta1.PredictorExtensionSpec{ StorageURI: &storageUri, RuntimeVersion: proto.String("1.14.0"), - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, Resources: defaultResource, }, @@ -2332,7 +2343,7 @@ var _ = Describe("v1beta1 inference service controller", func() { Expect(k8sClient.Create(ctx, isvc)).Should(Succeed()) defer k8sClient.Delete(ctx, isvc) - caBundleConfigMap := &v1.ConfigMap{} + caBundleConfigMap := &corev1.ConfigMap{} Eventually(func() bool { err := k8sClient.Get(ctx, types.NamespacedName{Name: constants.DefaultGlobalCaBundleConfigMapName, Namespace: "default"}, caBundleConfigMap) if err != nil { @@ -2344,10 +2355,10 @@ var _ = Describe("v1beta1 inference service controller", func() { }, timeout, interval).Should(BeTrue()) }) }) - Context("If the InferenceService occured any error", func() { + Context("If the InferenceService occurred any error", func() { It("InferenceService should generate event message about non-ready conditions", func() { // Create configmap - var configMap = &v1.ConfigMap{ + configMap := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: constants.InferenceServiceConfigMapName, Namespace: constants.KServeNamespace, @@ -2371,7 +2382,7 @@ var _ = Describe("v1beta1 inference service controller", func() { }, }, ServingRuntimePodSpec: v1alpha1.ServingRuntimePodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, Image: "tensorflow/serving:1.14.0", @@ -2392,9 +2403,9 @@ var _ = Describe("v1beta1 inference service controller", func() { k8sClient.Create(context.TODO(), servingRuntime) defer k8sClient.Delete(context.TODO(), servingRuntime) serviceName := "test-err-msg" - var expectedRequest = reconcile.Request{NamespacedName: types.NamespacedName{Name: serviceName, Namespace: "default"}} - var serviceKey = expectedRequest.NamespacedName - var storageUri = "s3://test/mnist/export" + expectedRequest := reconcile.Request{NamespacedName: types.NamespacedName{Name: serviceName, Namespace: "default"}} + serviceKey := expectedRequest.NamespacedName + storageUri := "s3://test/mnist/export" ctx := context.Background() isvc := &v1beta1.InferenceService{ ObjectMeta: metav1.ObjectMeta{ @@ -2404,14 +2415,14 @@ var _ = Describe("v1beta1 inference service controller", func() { Spec: v1beta1.InferenceServiceSpec{ Predictor: v1beta1.PredictorSpec{ ComponentExtensionSpec: v1beta1.ComponentExtensionSpec{ - MinReplicas: v1beta1.GetIntReference(1), + MinReplicas: ptr.To(int32(1)), MaxReplicas: 3, }, Tensorflow: &v1beta1.TFServingSpec{ PredictorExtensionSpec: v1beta1.PredictorExtensionSpec{ StorageURI: &storageUri, RuntimeVersion: proto.String("1.14.0"), - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, Resources: defaultResource, }, @@ -2425,8 +2436,10 @@ var _ = Describe("v1beta1 inference service controller", func() { inferenceService := &v1beta1.InferenceService{} updatedService := &knservingv1.Service{} - predictorServiceKey := types.NamespacedName{Name: constants.PredictorServiceName(serviceKey.Name), - Namespace: serviceKey.Namespace} + predictorServiceKey := types.NamespacedName{ + Name: constants.PredictorServiceName(serviceKey.Name), + Namespace: serviceKey.Namespace, + } Eventually(func() error { return k8sClient.Get(context.TODO(), predictorServiceKey, updatedService) }, timeout). Should(Succeed()) @@ -2447,10 +2460,7 @@ var _ = Describe("v1beta1 inference service controller", func() { // assert inference service predictor status Eventually(func() bool { err := k8sClient.Get(ctx, serviceKey, inferenceService) - if err != nil { - return false - } - return true + return err == nil }, timeout, interval).Should(BeTrue()) // should turn to fail @@ -2470,7 +2480,7 @@ var _ = Describe("v1beta1 inference service controller", func() { } Eventually(func() bool { - events := &v1.EventList{} + events := &corev1.EventList{} err := k8sClient.List(ctx, events, client.InNamespace(serviceKey.Namespace)) if err != nil { return false @@ -2497,7 +2507,7 @@ var _ = Describe("v1beta1 inference service controller", func() { for _, expectedCondition := range expectedConditions { found := false for _, cond := range inferenceService.Status.Conditions { - if string(cond.Type) == expectedCondition && cond.Status == v1.ConditionFalse { + if string(cond.Type) == expectedCondition && cond.Status == corev1.ConditionFalse { found = true break } diff --git a/pkg/controller/v1beta1/inferenceservice/rawkube_controller_test.go b/pkg/controller/v1beta1/inferenceservice/rawkube_controller_test.go index 9b22c39ddc2..53d3bebede5 100644 --- a/pkg/controller/v1beta1/inferenceservice/rawkube_controller_test.go +++ b/pkg/controller/v1beta1/inferenceservice/rawkube_controller_test.go @@ -21,51 +21,50 @@ import ( "fmt" "time" - "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" - "github.com/kserve/kserve/pkg/utils" - apierr "k8s.io/apimachinery/pkg/api/errors" - "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" - "github.com/kserve/kserve/pkg/apis/serving/v1beta1" - "github.com/kserve/kserve/pkg/constants" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "google.golang.org/protobuf/proto" appsv1 "k8s.io/api/apps/v1" - autoscalingv2 "k8s.io/api/autoscaling/v2" - v1 "k8s.io/api/core/v1" - + corev1 "k8s.io/api/core/v1" netv1 "k8s.io/api/networking/v1" + apierr "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/utils/ptr" "knative.dev/pkg/apis" duckv1 "knative.dev/pkg/apis/duck/v1" "sigs.k8s.io/controller-runtime/pkg/reconcile" + gatewayapiv1 "sigs.k8s.io/gateway-api/apis/v1" + + "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" + "github.com/kserve/kserve/pkg/apis/serving/v1beta1" + "github.com/kserve/kserve/pkg/constants" + "github.com/kserve/kserve/pkg/utils" ) var _ = Describe("v1beta1 inference service controller", func() { // Define utility constants for object names and testing timeouts/durations and intervals. const ( - timeout = time.Second * 10 + timeout = time.Second * 60 interval = time.Millisecond * 250 domain = "example.com" ) - var ( - defaultResource = v1.ResourceRequirements{ - Limits: v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("1"), - v1.ResourceMemory: resource.MustParse("2Gi"), - }, - Requests: v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("1"), - v1.ResourceMemory: resource.MustParse("2Gi"), - }, - } - ) + defaultResource := corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("2Gi"), + }, + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("2Gi"), + }, + } + kserveGateway := types.NamespacedName{Name: "kserve-ingress-gateway", Namespace: "kserve"} Context("When creating inference service with raw kube predictor", func() { configs := map[string]string{ @@ -76,9 +75,12 @@ var _ = Describe("v1beta1 inference service controller", func() { } }`, "ingress": `{ + "enableGatewayApi": true, + "kserveIngressGateway": "kserve/kserve-ingress-gateway", "ingressGateway": "knative-serving/knative-ingress-gateway", "localGateway": "knative-serving/knative-local-gateway", - "localGatewayService": "knative-local-gateway.istio-system.svc.cluster.local" + "localGatewayService": "knative-local-gateway.istio-system.svc.cluster.local", + "additionalIngressDomains": ["additional.example.com"] }`, "storageInitializer": `{ "image" : "kserve/storage-initializer:latest", @@ -92,10 +94,10 @@ var _ = Describe("v1beta1 inference service controller", func() { }`, } - It("Should have ingress/service/deployment/hpa created", func() { + It("Should have httproute/service/deployment/httproute created", func() { By("By creating a new InferenceService") // Create configmap - var configMap = &v1.ConfigMap{ + configMap := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: constants.InferenceServiceConfigMapName, Namespace: constants.KServeNamespace, @@ -119,7 +121,7 @@ var _ = Describe("v1beta1 inference service controller", func() { }, }, ServingRuntimePodSpec: v1alpha1.ServingRuntimePodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Name: "kserve-container", Image: "tensorflow/serving:1.14.0", @@ -140,9 +142,9 @@ var _ = Describe("v1beta1 inference service controller", func() { k8sClient.Create(context.TODO(), servingRuntime) defer k8sClient.Delete(context.TODO(), servingRuntime) serviceName := "raw-foo" - var expectedRequest = reconcile.Request{NamespacedName: types.NamespacedName{Name: serviceName, Namespace: "default"}} - var serviceKey = expectedRequest.NamespacedName - var storageUri = "s3://test/mnist/export" + expectedRequest := reconcile.Request{NamespacedName: types.NamespacedName{Name: serviceName, Namespace: "default"}} + serviceKey := expectedRequest.NamespacedName + storageUri := "s3://test/mnist/export" ctx := context.Background() isvc := &v1beta1.InferenceService{ ObjectMeta: metav1.ObjectMeta{ @@ -158,14 +160,15 @@ var _ = Describe("v1beta1 inference service controller", func() { Spec: v1beta1.InferenceServiceSpec{ Predictor: v1beta1.PredictorSpec{ ComponentExtensionSpec: v1beta1.ComponentExtensionSpec{ - MinReplicas: v1beta1.GetIntReference(1), - MaxReplicas: 3, + MinReplicas: ptr.To(int32(1)), + MaxReplicas: 3, + TimeoutSeconds: ptr.To(int64(30)), }, Tensorflow: &v1beta1.TFServingSpec{ PredictorExtensionSpec: v1beta1.PredictorExtensionSpec{ StorageURI: &storageUri, RuntimeVersion: proto.String("1.14.0"), - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, Resources: defaultResource, }, @@ -176,20 +179,20 @@ var _ = Describe("v1beta1 inference service controller", func() { } isvc.DefaultInferenceService(nil, nil, &v1beta1.SecurityConfig{AutoMountServiceAccountToken: false}, nil) Expect(k8sClient.Create(ctx, isvc)).Should(Succeed()) + defer k8sClient.Delete(ctx, isvc) inferenceService := &v1beta1.InferenceService{} Eventually(func() bool { err := k8sClient.Get(ctx, serviceKey, inferenceService) - if err != nil { - return false - } - return true + return err == nil }, timeout, interval).Should(BeTrue()) actualDeployment := &appsv1.Deployment{} - predictorDeploymentKey := types.NamespacedName{Name: constants.PredictorServiceName(serviceKey.Name), - Namespace: serviceKey.Namespace} + predictorDeploymentKey := types.NamespacedName{ + Name: constants.PredictorServiceName(serviceKey.Name), + Namespace: serviceKey.Namespace, + } Eventually(func() error { return k8sClient.Get(context.TODO(), predictorDeploymentKey, actualDeployment) }, timeout). Should(Succeed()) var replicas int32 = 1 @@ -208,7 +211,7 @@ var _ = Describe("v1beta1 inference service controller", func() { "app": "isvc." + predictorDeploymentKey.Name, }, }, - Template: v1.PodTemplateSpec{ + Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Name: predictorDeploymentKey.Name, Namespace: "default", @@ -225,8 +228,8 @@ var _ = Describe("v1beta1 inference service controller", func() { "serving.kserve.io/targetUtilizationPercentage": "75", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Image: "tensorflow/serving:" + *isvc.Spec.Predictor.Model.RuntimeVersion, @@ -239,9 +242,9 @@ var _ = Describe("v1beta1 inference service controller", func() { "--rest_api_timeout_in_ms=60000", }, Resources: defaultResource, - ReadinessProbe: &v1.Probe{ - ProbeHandler: v1.ProbeHandler{ - TCPSocket: &v1.TCPSocketAction{ + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + TCPSocket: &corev1.TCPSocketAction{ Port: intstr.IntOrString{ IntVal: 8080, }, @@ -262,7 +265,7 @@ var _ = Describe("v1beta1 inference service controller", func() { RestartPolicy: "Always", TerminationGracePeriodSeconds: &gracePeriod, DNSPolicy: "ClusterFirst", - SecurityContext: &v1.PodSecurityContext{ + SecurityContext: &corev1.PodSecurityContext{ SELinuxOptions: nil, WindowsOptions: nil, RunAsUser: nil, @@ -288,22 +291,24 @@ var _ = Describe("v1beta1 inference service controller", func() { ProgressDeadlineSeconds: &progressDeadlineSeconds, }, } - Expect(actualDeployment.Spec).To(Equal(expectedDeployment.Spec)) + Expect(actualDeployment.Spec).To(BeComparableTo(expectedDeployment.Spec)) - //check service - actualService := &v1.Service{} - predictorServiceKey := types.NamespacedName{Name: constants.PredictorServiceName(serviceKey.Name), - Namespace: serviceKey.Namespace} + // check service + actualService := &corev1.Service{} + predictorServiceKey := types.NamespacedName{ + Name: constants.PredictorServiceName(serviceKey.Name), + Namespace: serviceKey.Namespace, + } Eventually(func() error { return k8sClient.Get(context.TODO(), predictorServiceKey, actualService) }, timeout). Should(Succeed()) - expectedService := &v1.Service{ + expectedService := &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: predictorServiceKey.Name, Namespace: predictorServiceKey.Namespace, }, - Spec: v1.ServiceSpec{ - Ports: []v1.ServicePort{ + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ { Name: constants.PredictorServiceName(serviceName), Protocol: "TCP", @@ -314,7 +319,7 @@ var _ = Describe("v1beta1 inference service controller", func() { Type: "ClusterIP", SessionAffinity: "None", Selector: map[string]string{ - "app": fmt.Sprintf("isvc.%s", constants.PredictorServiceName(serviceName)), + "app": "isvc." + constants.PredictorServiceName(serviceName), }, }, } @@ -323,74 +328,191 @@ var _ = Describe("v1beta1 inference service controller", func() { actualService.Spec.IPFamilies = nil actualService.Spec.IPFamilyPolicy = nil actualService.Spec.InternalTrafficPolicy = nil - Expect(actualService.Spec).To(Equal(expectedService.Spec)) + Expect(actualService.Spec).To(BeComparableTo(expectedService.Spec)) - //check isvc status + // check isvc status updatedDeployment := actualDeployment.DeepCopy() updatedDeployment.Status.Conditions = []appsv1.DeploymentCondition{ { Type: appsv1.DeploymentAvailable, - Status: v1.ConditionTrue, + Status: corev1.ConditionTrue, }, } Expect(k8sClient.Status().Update(context.TODO(), updatedDeployment)).NotTo(HaveOccurred()) - //check ingress - pathType := netv1.PathTypePrefix - actualIngress := &netv1.Ingress{} - predictorIngressKey := types.NamespacedName{Name: serviceKey.Name, - Namespace: serviceKey.Namespace} - Eventually(func() error { return k8sClient.Get(context.TODO(), predictorIngressKey, actualIngress) }, timeout). - Should(Succeed()) - expectedIngress := netv1.Ingress{ - Spec: netv1.IngressSpec{ - Rules: []netv1.IngressRule{ + // check http route + actualTopLevelHttpRoute := &gatewayapiv1.HTTPRoute{} + Eventually(func() error { + return k8sClient.Get(context.TODO(), types.NamespacedName{ + Name: serviceKey.Name, + Namespace: serviceKey.Namespace, + }, actualTopLevelHttpRoute) + }, timeout).Should(Succeed()) + topLevelHost := fmt.Sprintf("%s-%s.%s", serviceKey.Name, serviceKey.Namespace, "example.com") + expectedTopLevelHttpRoute := gatewayapiv1.HTTPRoute{ + Spec: gatewayapiv1.HTTPRouteSpec{ + Hostnames: []gatewayapiv1.Hostname{gatewayapiv1.Hostname(topLevelHost), "additional.example.com"}, + Rules: []gatewayapiv1.HTTPRouteRule{ { - Host: "raw-foo-default.example.com", - IngressRuleValue: netv1.IngressRuleValue{ - HTTP: &netv1.HTTPIngressRuleValue{ - Paths: []netv1.HTTPIngressPath{ - { - Path: "/", - PathType: &pathType, - Backend: netv1.IngressBackend{ - Service: &netv1.IngressServiceBackend{ - Name: "raw-foo-predictor", - Port: netv1.ServiceBackendPort{ - Number: 80, - }, - }, + Matches: []gatewayapiv1.HTTPRouteMatch{ + { + Path: &gatewayapiv1.HTTPPathMatch{ + Type: ptr.To(gatewayapiv1.PathMatchRegularExpression), + Value: ptr.To(constants.FallbackPrefix()), + }, + }, + }, + Filters: []gatewayapiv1.HTTPRouteFilter{ + { + Type: gatewayapiv1.HTTPRouteFilterRequestHeaderModifier, + RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ + Set: []gatewayapiv1.HTTPHeader{ + { + Name: constants.IsvcNameHeader, + Value: serviceKey.Name, }, + { + Name: constants.IsvcNamespaceHeader, + Value: serviceKey.Namespace, + }, + }, + }, + }, + }, + BackendRefs: []gatewayapiv1.HTTPBackendRef{ + { + BackendRef: gatewayapiv1.BackendRef{ + BackendObjectReference: gatewayapiv1.BackendObjectReference{ + Group: (*gatewayapiv1.Group)(ptr.To("")), + Kind: ptr.To(gatewayapiv1.Kind(constants.ServiceKind)), + Name: gatewayapiv1.ObjectName(predictorServiceKey.Name), + Namespace: (*gatewayapiv1.Namespace)(ptr.To(serviceKey.Namespace)), + Port: (*gatewayapiv1.PortNumber)(ptr.To(int32(constants.CommonDefaultHttpPort))), }, + Weight: ptr.To(int32(1)), }, }, }, + Timeouts: &gatewayapiv1.HTTPRouteTimeouts{ + Request: ptr.To(gatewayapiv1.Duration("30s")), + }, + }, + }, + CommonRouteSpec: gatewayapiv1.CommonRouteSpec{ + ParentRefs: []gatewayapiv1.ParentReference{ + { + Name: gatewayapiv1.ObjectName(kserveGateway.Name), + Kind: ptr.To(gatewayapiv1.Kind(constants.GatewayKind)), + Group: (*gatewayapiv1.Group)(&gatewayapiv1.GroupVersion.Group), + Namespace: ptr.To(gatewayapiv1.Namespace(kserveGateway.Namespace)), + }, }, + }, + }, + } + Expect(actualTopLevelHttpRoute.Spec).To(BeComparableTo(expectedTopLevelHttpRoute.Spec)) + + actualPredictorHttpRoute := &gatewayapiv1.HTTPRoute{} + Eventually(func() error { + return k8sClient.Get(context.TODO(), types.NamespacedName{ + Name: predictorServiceKey.Name, + Namespace: serviceKey.Namespace, + }, actualPredictorHttpRoute) + }, timeout).Should(Succeed()) + predictorHost := fmt.Sprintf("%s-%s.%s", predictorServiceKey.Name, serviceKey.Namespace, "example.com") + expectedPredictorHttpRoute := gatewayapiv1.HTTPRoute{ + Spec: gatewayapiv1.HTTPRouteSpec{ + Hostnames: []gatewayapiv1.Hostname{gatewayapiv1.Hostname(predictorHost)}, + Rules: []gatewayapiv1.HTTPRouteRule{ { - Host: "raw-foo-predictor-default.example.com", - IngressRuleValue: netv1.IngressRuleValue{ - HTTP: &netv1.HTTPIngressRuleValue{ - Paths: []netv1.HTTPIngressPath{ - { - Path: "/", - PathType: &pathType, - Backend: netv1.IngressBackend{ - Service: &netv1.IngressServiceBackend{ - Name: "raw-foo-predictor", - Port: netv1.ServiceBackendPort{ - Number: 80, - }, - }, + Matches: []gatewayapiv1.HTTPRouteMatch{ + { + Path: &gatewayapiv1.HTTPPathMatch{ + Type: ptr.To(gatewayapiv1.PathMatchRegularExpression), + Value: ptr.To(constants.FallbackPrefix()), + }, + }, + }, + Filters: []gatewayapiv1.HTTPRouteFilter{ + { + Type: gatewayapiv1.HTTPRouteFilterRequestHeaderModifier, + RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ + Set: []gatewayapiv1.HTTPHeader{ + { + Name: constants.IsvcNameHeader, + Value: serviceKey.Name, + }, + { + Name: constants.IsvcNamespaceHeader, + Value: serviceKey.Namespace, }, }, }, }, }, + BackendRefs: []gatewayapiv1.HTTPBackendRef{ + { + BackendRef: gatewayapiv1.BackendRef{ + BackendObjectReference: gatewayapiv1.BackendObjectReference{ + Group: (*gatewayapiv1.Group)(ptr.To("")), + Kind: ptr.To(gatewayapiv1.Kind(constants.ServiceKind)), + Name: gatewayapiv1.ObjectName(predictorServiceKey.Name), + Namespace: (*gatewayapiv1.Namespace)(ptr.To(serviceKey.Namespace)), + Port: (*gatewayapiv1.PortNumber)(ptr.To(int32(constants.CommonDefaultHttpPort))), + }, + Weight: ptr.To(int32(1)), + }, + }, + }, + Timeouts: &gatewayapiv1.HTTPRouteTimeouts{ + Request: ptr.To(gatewayapiv1.Duration("30s")), + }, + }, + }, + CommonRouteSpec: gatewayapiv1.CommonRouteSpec{ + ParentRefs: []gatewayapiv1.ParentReference{ + { + Name: gatewayapiv1.ObjectName(kserveGateway.Name), + Kind: ptr.To(gatewayapiv1.Kind(constants.GatewayKind)), + Group: (*gatewayapiv1.Group)(&gatewayapiv1.GroupVersion.Group), + Namespace: ptr.To(gatewayapiv1.Namespace(kserveGateway.Namespace)), + }, }, }, }, } - Expect(actualIngress.Spec).To(Equal(expectedIngress.Spec)) + Expect(actualPredictorHttpRoute.Spec).To(BeComparableTo(expectedPredictorHttpRoute.Spec)) + + // Mark the Ingress as accepted to make isvc ready + httpRouteStatus := gatewayapiv1.HTTPRouteStatus{ + RouteStatus: gatewayapiv1.RouteStatus{ + Parents: []gatewayapiv1.RouteParentStatus{ + { + ParentRef: gatewayapiv1.ParentReference{ + Name: gatewayapiv1.ObjectName(kserveGateway.Name), + Kind: ptr.To(gatewayapiv1.Kind(constants.GatewayKind)), + Group: (*gatewayapiv1.Group)(&gatewayapiv1.GroupVersion.Group), + Namespace: ptr.To(gatewayapiv1.Namespace(kserveGateway.Namespace)), + }, + ControllerName: "istio.io/gateway-controller", + Conditions: []metav1.Condition{ + { + Type: string(gatewayapiv1.ListenerConditionAccepted), + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "Route was valid", + LastTransitionTime: metav1.Now(), + }, + }, + }, + }, + }, + } + actualPredictorHttpRoute.Status = httpRouteStatus + Expect(k8sClient.Status().Update(context.Background(), actualPredictorHttpRoute)).NotTo(HaveOccurred()) + actualTopLevelHttpRoute.Status = httpRouteStatus + Expect(k8sClient.Status().Update(context.Background(), actualTopLevelHttpRoute)).NotTo(HaveOccurred()) + // verify if InferenceService status is updated expectedIsvcStatus := v1beta1.InferenceServiceStatus{ Status: duckv1.Status{ @@ -441,15 +563,17 @@ var _ = Describe("v1beta1 inference service controller", func() { return cmp.Diff(&expectedIsvcStatus, &isvc.Status, cmpopts.IgnoreTypes(apis.VolatileTime{})) }, timeout).Should(BeEmpty()) - //check HPA + // check HPA var minReplicas int32 = 1 var maxReplicas int32 = 3 var cpuUtilization int32 = 75 var stabilizationWindowSeconds int32 = 0 selectPolicy := autoscalingv2.MaxChangePolicySelect actualHPA := &autoscalingv2.HorizontalPodAutoscaler{} - predictorHPAKey := types.NamespacedName{Name: constants.PredictorServiceName(serviceKey.Name), - Namespace: serviceKey.Namespace} + predictorHPAKey := types.NamespacedName{ + Name: predictorServiceKey.Name, + Namespace: serviceKey.Namespace, + } Eventually(func() error { return k8sClient.Get(context.TODO(), predictorHPAKey, actualHPA) }, timeout). Should(Succeed()) expectedHPA := &autoscalingv2.HorizontalPodAutoscaler{ @@ -457,7 +581,7 @@ var _ = Describe("v1beta1 inference service controller", func() { ScaleTargetRef: autoscalingv2.CrossVersionObjectReference{ APIVersion: "apps/v1", Kind: "Deployment", - Name: constants.PredictorServiceName(serviceKey.Name), + Name: predictorServiceKey.Name, }, MinReplicas: &minReplicas, MaxReplicas: maxReplicas, @@ -465,7 +589,7 @@ var _ = Describe("v1beta1 inference service controller", func() { { Type: autoscalingv2.ResourceMetricSourceType, Resource: &autoscalingv2.ResourceMetricSource{ - Name: v1.ResourceCPU, + Name: corev1.ResourceCPU, Target: autoscalingv2.MetricTarget{ Type: "Utilization", AverageUtilization: &cpuUtilization, @@ -504,12 +628,12 @@ var _ = Describe("v1beta1 inference service controller", func() { }, }, } - Expect(actualHPA.Spec).To(Equal(expectedHPA.Spec)) + Expect(actualHPA.Spec).To(BeComparableTo(expectedHPA.Spec)) }) - It("Should have ingress/service/deployment/hpa created with DeploymentStrategy", func() { + It("Should have httproute/service/deployment/hpa created with DeploymentStrategy", func() { By("By creating a new InferenceService with DeploymentStrategy in PredictorSpec") // Create configmap - var configMap = &v1.ConfigMap{ + configMap := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: constants.InferenceServiceConfigMapName, Namespace: constants.KServeNamespace, @@ -533,7 +657,7 @@ var _ = Describe("v1beta1 inference service controller", func() { }, }, ServingRuntimePodSpec: v1alpha1.ServingRuntimePodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Name: "kserve-container", Image: "tensorflow/serving:1.14.0", @@ -554,11 +678,13 @@ var _ = Describe("v1beta1 inference service controller", func() { k8sClient.Create(context.TODO(), servingRuntime) defer k8sClient.Delete(context.TODO(), servingRuntime) serviceName := "raw-foo-customized" - var expectedRequest = reconcile.Request{NamespacedName: types.NamespacedName{Name: serviceName, Namespace: "default"}} - var serviceKey = expectedRequest.NamespacedName - var storageUri = "s3://test/mnist/export" - predictorDeploymentKey := types.NamespacedName{Name: constants.PredictorServiceName(serviceKey.Name), - Namespace: serviceKey.Namespace} + expectedRequest := reconcile.Request{NamespacedName: types.NamespacedName{Name: serviceName, Namespace: "default"}} + serviceKey := expectedRequest.NamespacedName + storageUri := "s3://test/mnist/export" + predictorDeploymentKey := types.NamespacedName{ + Name: constants.PredictorServiceName(serviceKey.Name), + Namespace: serviceKey.Namespace, + } var replicas int32 = 1 var revisionHistory int32 = 10 var progressDeadlineSeconds int32 = 600 @@ -578,16 +704,17 @@ var _ = Describe("v1beta1 inference service controller", func() { Spec: v1beta1.InferenceServiceSpec{ Predictor: v1beta1.PredictorSpec{ ComponentExtensionSpec: v1beta1.ComponentExtensionSpec{ - MinReplicas: v1beta1.GetIntReference(1), + MinReplicas: ptr.To(int32(1)), MaxReplicas: 3, DeploymentStrategy: &appsv1.DeploymentStrategy{ Type: appsv1.RecreateDeploymentStrategyType, - }}, + }, + }, Tensorflow: &v1beta1.TFServingSpec{ PredictorExtensionSpec: v1beta1.PredictorExtensionSpec{ StorageURI: &storageUri, RuntimeVersion: proto.String("1.14.0"), - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, Resources: defaultResource, }, @@ -598,15 +725,13 @@ var _ = Describe("v1beta1 inference service controller", func() { } isvc.DefaultInferenceService(nil, nil, &v1beta1.SecurityConfig{AutoMountServiceAccountToken: false}, nil) Expect(k8sClient.Create(ctx, isvc)).Should(Succeed()) + defer k8sClient.Delete(ctx, isvc) inferenceService := &v1beta1.InferenceService{} Eventually(func() bool { err := k8sClient.Get(ctx, serviceKey, inferenceService) - if err != nil { - return false - } - return true + return err == nil }, timeout, interval).Should(BeTrue()) actualDeployment := &appsv1.Deployment{} @@ -626,7 +751,7 @@ var _ = Describe("v1beta1 inference service controller", func() { "app": "isvc." + predictorDeploymentKey.Name, }, }, - Template: v1.PodTemplateSpec{ + Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Name: predictorDeploymentKey.Name, Namespace: "default", @@ -643,8 +768,8 @@ var _ = Describe("v1beta1 inference service controller", func() { "serving.kserve.io/targetUtilizationPercentage": "75", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Image: "tensorflow/serving:" + *isvc.Spec.Predictor.Model.RuntimeVersion, @@ -657,9 +782,9 @@ var _ = Describe("v1beta1 inference service controller", func() { "--rest_api_timeout_in_ms=60000", }, Resources: defaultResource, - ReadinessProbe: &v1.Probe{ - ProbeHandler: v1.ProbeHandler{ - TCPSocket: &v1.TCPSocketAction{ + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + TCPSocket: &corev1.TCPSocketAction{ Port: intstr.IntOrString{ IntVal: 8080, }, @@ -680,7 +805,7 @@ var _ = Describe("v1beta1 inference service controller", func() { RestartPolicy: "Always", TerminationGracePeriodSeconds: &gracePeriod, DNSPolicy: "ClusterFirst", - SecurityContext: &v1.PodSecurityContext{ + SecurityContext: &corev1.PodSecurityContext{ SELinuxOptions: nil, WindowsOptions: nil, RunAsUser: nil, @@ -703,22 +828,24 @@ var _ = Describe("v1beta1 inference service controller", func() { ProgressDeadlineSeconds: &progressDeadlineSeconds, }, } - Expect(actualDeployment.Spec).To(Equal(expectedDeployment.Spec)) + Expect(actualDeployment.Spec).To(BeComparableTo(expectedDeployment.Spec)) - //check service - actualService := &v1.Service{} - predictorServiceKey := types.NamespacedName{Name: constants.PredictorServiceName(serviceKey.Name), - Namespace: serviceKey.Namespace} + // check service + actualService := &corev1.Service{} + predictorServiceKey := types.NamespacedName{ + Name: constants.PredictorServiceName(serviceKey.Name), + Namespace: serviceKey.Namespace, + } Eventually(func() error { return k8sClient.Get(context.TODO(), predictorServiceKey, actualService) }, timeout). Should(Succeed()) - expectedService := &v1.Service{ + expectedService := &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: predictorServiceKey.Name, Namespace: predictorServiceKey.Namespace, }, - Spec: v1.ServiceSpec{ - Ports: []v1.ServicePort{ + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ { Name: constants.PredictorServiceName(serviceName), Protocol: "TCP", @@ -729,7 +856,7 @@ var _ = Describe("v1beta1 inference service controller", func() { Type: "ClusterIP", SessionAffinity: "None", Selector: map[string]string{ - "app": fmt.Sprintf("isvc.%s", constants.PredictorServiceName(serviceName)), + "app": "isvc." + constants.PredictorServiceName(serviceName), }, }, } @@ -738,74 +865,193 @@ var _ = Describe("v1beta1 inference service controller", func() { actualService.Spec.IPFamilies = nil actualService.Spec.IPFamilyPolicy = nil actualService.Spec.InternalTrafficPolicy = nil - Expect(actualService.Spec).To(Equal(expectedService.Spec)) + Expect(actualService.Spec).To(BeComparableTo(expectedService.Spec)) - //check isvc status + // check isvc status updatedDeployment := actualDeployment.DeepCopy() updatedDeployment.Status.Conditions = []appsv1.DeploymentCondition{ { Type: appsv1.DeploymentAvailable, - Status: v1.ConditionTrue, + Status: corev1.ConditionTrue, }, } Expect(k8sClient.Status().Update(context.TODO(), updatedDeployment)).NotTo(HaveOccurred()) - //check ingress - pathType := netv1.PathTypePrefix - actualIngress := &netv1.Ingress{} - predictorIngressKey := types.NamespacedName{Name: serviceKey.Name, - Namespace: serviceKey.Namespace} - Eventually(func() error { return k8sClient.Get(context.TODO(), predictorIngressKey, actualIngress) }, timeout). + // check http route + actualToplevelHttpRoute := &gatewayapiv1.HTTPRoute{} + Eventually(func() error { + return k8sClient.Get(context.TODO(), types.NamespacedName{ + Name: serviceKey.Name, + Namespace: serviceKey.Namespace, + }, actualToplevelHttpRoute) + }, timeout). Should(Succeed()) - expectedIngress := netv1.Ingress{ - Spec: netv1.IngressSpec{ - Rules: []netv1.IngressRule{ + topLevelHost := fmt.Sprintf("%s-%s.%s", serviceKey.Name, serviceKey.Namespace, "example.com") + expectedToplevelHttpRoute := gatewayapiv1.HTTPRoute{ + Spec: gatewayapiv1.HTTPRouteSpec{ + Hostnames: []gatewayapiv1.Hostname{gatewayapiv1.Hostname(topLevelHost), "additional.example.com"}, + Rules: []gatewayapiv1.HTTPRouteRule{ { - Host: "raw-foo-customized-default.example.com", - IngressRuleValue: netv1.IngressRuleValue{ - HTTP: &netv1.HTTPIngressRuleValue{ - Paths: []netv1.HTTPIngressPath{ - { - Path: "/", - PathType: &pathType, - Backend: netv1.IngressBackend{ - Service: &netv1.IngressServiceBackend{ - Name: "raw-foo-customized-predictor", - Port: netv1.ServiceBackendPort{ - Number: 80, - }, - }, + Matches: []gatewayapiv1.HTTPRouteMatch{ + { + Path: &gatewayapiv1.HTTPPathMatch{ + Type: ptr.To(gatewayapiv1.PathMatchRegularExpression), + Value: ptr.To(constants.FallbackPrefix()), + }, + }, + }, + Filters: []gatewayapiv1.HTTPRouteFilter{ + { + Type: gatewayapiv1.HTTPRouteFilterRequestHeaderModifier, + RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ + Set: []gatewayapiv1.HTTPHeader{ + { + Name: constants.IsvcNameHeader, + Value: serviceKey.Name, }, + { + Name: constants.IsvcNamespaceHeader, + Value: serviceKey.Namespace, + }, + }, + }, + }, + }, + BackendRefs: []gatewayapiv1.HTTPBackendRef{ + { + BackendRef: gatewayapiv1.BackendRef{ + BackendObjectReference: gatewayapiv1.BackendObjectReference{ + Group: (*gatewayapiv1.Group)(ptr.To("")), + Kind: ptr.To(gatewayapiv1.Kind(constants.ServiceKind)), + Name: gatewayapiv1.ObjectName(predictorServiceKey.Name), + Namespace: (*gatewayapiv1.Namespace)(ptr.To(serviceKey.Namespace)), + Port: (*gatewayapiv1.PortNumber)(ptr.To(int32(constants.CommonDefaultHttpPort))), }, + Weight: ptr.To(int32(1)), }, }, }, + Timeouts: &gatewayapiv1.HTTPRouteTimeouts{ + Request: ptr.To(gatewayapiv1.Duration("60s")), + }, + }, + }, + CommonRouteSpec: gatewayapiv1.CommonRouteSpec{ + ParentRefs: []gatewayapiv1.ParentReference{ + { + Name: gatewayapiv1.ObjectName(kserveGateway.Name), + Kind: ptr.To(gatewayapiv1.Kind(constants.GatewayKind)), + Group: (*gatewayapiv1.Group)(&gatewayapiv1.GroupVersion.Group), + Namespace: ptr.To(gatewayapiv1.Namespace(kserveGateway.Namespace)), + }, }, + }, + }, + } + Expect(actualToplevelHttpRoute.Spec).To(BeComparableTo(expectedToplevelHttpRoute.Spec)) + + actualPredictorHttpRoute := &gatewayapiv1.HTTPRoute{} + Eventually(func() error { + return k8sClient.Get(context.TODO(), types.NamespacedName{ + Name: predictorServiceKey.Name, + Namespace: serviceKey.Namespace, + }, actualPredictorHttpRoute) + }, timeout). + Should(Succeed()) + predictorHost := fmt.Sprintf("%s-%s.%s", predictorServiceKey.Name, serviceKey.Namespace, "example.com") + expectedPredictorHttpRoute := gatewayapiv1.HTTPRoute{ + Spec: gatewayapiv1.HTTPRouteSpec{ + Hostnames: []gatewayapiv1.Hostname{gatewayapiv1.Hostname(predictorHost)}, + Rules: []gatewayapiv1.HTTPRouteRule{ { - Host: "raw-foo-customized-predictor-default.example.com", - IngressRuleValue: netv1.IngressRuleValue{ - HTTP: &netv1.HTTPIngressRuleValue{ - Paths: []netv1.HTTPIngressPath{ - { - Path: "/", - PathType: &pathType, - Backend: netv1.IngressBackend{ - Service: &netv1.IngressServiceBackend{ - Name: "raw-foo-customized-predictor", - Port: netv1.ServiceBackendPort{ - Number: 80, - }, - }, + Matches: []gatewayapiv1.HTTPRouteMatch{ + { + Path: &gatewayapiv1.HTTPPathMatch{ + Type: ptr.To(gatewayapiv1.PathMatchRegularExpression), + Value: ptr.To(constants.FallbackPrefix()), + }, + }, + }, + Filters: []gatewayapiv1.HTTPRouteFilter{ + { + Type: gatewayapiv1.HTTPRouteFilterRequestHeaderModifier, + RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ + Set: []gatewayapiv1.HTTPHeader{ + { + Name: constants.IsvcNameHeader, + Value: serviceKey.Name, }, + { + Name: constants.IsvcNamespaceHeader, + Value: serviceKey.Namespace, + }, + }, + }, + }, + }, + BackendRefs: []gatewayapiv1.HTTPBackendRef{ + { + BackendRef: gatewayapiv1.BackendRef{ + BackendObjectReference: gatewayapiv1.BackendObjectReference{ + Group: (*gatewayapiv1.Group)(ptr.To("")), + Kind: ptr.To(gatewayapiv1.Kind(constants.ServiceKind)), + Name: gatewayapiv1.ObjectName(predictorServiceKey.Name), + Namespace: (*gatewayapiv1.Namespace)(ptr.To(serviceKey.Namespace)), + Port: (*gatewayapiv1.PortNumber)(ptr.To(int32(constants.CommonDefaultHttpPort))), }, + Weight: ptr.To(int32(1)), }, }, }, + Timeouts: &gatewayapiv1.HTTPRouteTimeouts{ + Request: ptr.To(gatewayapiv1.Duration("60s")), + }, + }, + }, + CommonRouteSpec: gatewayapiv1.CommonRouteSpec{ + ParentRefs: []gatewayapiv1.ParentReference{ + { + Name: gatewayapiv1.ObjectName(kserveGateway.Name), + Kind: ptr.To(gatewayapiv1.Kind(constants.GatewayKind)), + Group: (*gatewayapiv1.Group)(&gatewayapiv1.GroupVersion.Group), + Namespace: ptr.To(gatewayapiv1.Namespace(kserveGateway.Namespace)), + }, }, }, }, } - Expect(actualIngress.Spec).To(Equal(expectedIngress.Spec)) + Expect(actualPredictorHttpRoute.Spec).To(BeComparableTo(expectedPredictorHttpRoute.Spec)) + + // Mark the Ingress as accepted to make isvc ready + httpRouteStatus := gatewayapiv1.HTTPRouteStatus{ + RouteStatus: gatewayapiv1.RouteStatus{ + Parents: []gatewayapiv1.RouteParentStatus{ + { + ParentRef: gatewayapiv1.ParentReference{ + Name: gatewayapiv1.ObjectName(kserveGateway.Name), + Kind: ptr.To(gatewayapiv1.Kind(constants.GatewayKind)), + Group: (*gatewayapiv1.Group)(&gatewayapiv1.GroupVersion.Group), + Namespace: ptr.To(gatewayapiv1.Namespace(kserveGateway.Namespace)), + }, + ControllerName: "istio.io/gateway-controller", + Conditions: []metav1.Condition{ + { + Type: string(gatewayapiv1.ListenerConditionAccepted), + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "Route was valid", + LastTransitionTime: metav1.Now(), + }, + }, + }, + }, + }, + } + actualPredictorHttpRoute.Status = httpRouteStatus + Expect(k8sClient.Status().Update(context.Background(), actualPredictorHttpRoute)).NotTo(HaveOccurred()) + actualToplevelHttpRoute.Status = httpRouteStatus + Expect(k8sClient.Status().Update(context.Background(), actualToplevelHttpRoute)).NotTo(HaveOccurred()) + // verify if InferenceService status is updated expectedIsvcStatus := v1beta1.InferenceServiceStatus{ Status: duckv1.Status{ @@ -856,15 +1102,17 @@ var _ = Describe("v1beta1 inference service controller", func() { return cmp.Diff(&expectedIsvcStatus, &isvc.Status, cmpopts.IgnoreTypes(apis.VolatileTime{})) }, timeout).Should(BeEmpty()) - //check HPA + // check HPA var minReplicas int32 = 1 var maxReplicas int32 = 3 var cpuUtilization int32 = 75 var stabilizationWindowSeconds int32 = 0 selectPolicy := autoscalingv2.MaxChangePolicySelect actualHPA := &autoscalingv2.HorizontalPodAutoscaler{} - predictorHPAKey := types.NamespacedName{Name: constants.PredictorServiceName(serviceKey.Name), - Namespace: serviceKey.Namespace} + predictorHPAKey := types.NamespacedName{ + Name: predictorServiceKey.Name, + Namespace: serviceKey.Namespace, + } Eventually(func() error { return k8sClient.Get(context.TODO(), predictorHPAKey, actualHPA) }, timeout). Should(Succeed()) expectedHPA := &autoscalingv2.HorizontalPodAutoscaler{ @@ -872,7 +1120,7 @@ var _ = Describe("v1beta1 inference service controller", func() { ScaleTargetRef: autoscalingv2.CrossVersionObjectReference{ APIVersion: "apps/v1", Kind: "Deployment", - Name: constants.PredictorServiceName(serviceKey.Name), + Name: predictorServiceKey.Name, }, MinReplicas: &minReplicas, MaxReplicas: maxReplicas, @@ -880,7 +1128,7 @@ var _ = Describe("v1beta1 inference service controller", func() { { Type: autoscalingv2.ResourceMetricSourceType, Resource: &autoscalingv2.ResourceMetricSource{ - Name: v1.ResourceCPU, + Name: corev1.ResourceCPU, Target: autoscalingv2.MetricTarget{ Type: "Utilization", AverageUtilization: &cpuUtilization, @@ -919,12 +1167,12 @@ var _ = Describe("v1beta1 inference service controller", func() { }, }, } - Expect(actualHPA.Spec).To(Equal(expectedHPA.Spec)) + Expect(actualHPA.Spec).To(BeComparableTo(expectedHPA.Spec)) }) - It("Should have ingress/service/deployment created", func() { + It("Should have httproute/service/deployment created", func() { By("By creating a new InferenceService with AutoscalerClassExternal") // Create configmap - var configMap = &v1.ConfigMap{ + configMap := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: constants.InferenceServiceConfigMapName, Namespace: constants.KServeNamespace, @@ -948,7 +1196,7 @@ var _ = Describe("v1beta1 inference service controller", func() { }, }, ServingRuntimePodSpec: v1alpha1.ServingRuntimePodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Name: "kserve-container", Image: "tensorflow/serving:1.14.0", @@ -969,9 +1217,9 @@ var _ = Describe("v1beta1 inference service controller", func() { k8sClient.Create(context.TODO(), servingRuntime) defer k8sClient.Delete(context.TODO(), servingRuntime) serviceName := "raw-foo-2" - var expectedRequest = reconcile.Request{NamespacedName: types.NamespacedName{Name: serviceName, Namespace: "default"}} - var serviceKey = expectedRequest.NamespacedName - var storageUri = "s3://test/mnist/export" + expectedRequest := reconcile.Request{NamespacedName: types.NamespacedName{Name: serviceName, Namespace: "default"}} + serviceKey := expectedRequest.NamespacedName + storageUri := "s3://test/mnist/export" ctx := context.Background() isvc := &v1beta1.InferenceService{ ObjectMeta: metav1.ObjectMeta{ @@ -988,7 +1236,7 @@ var _ = Describe("v1beta1 inference service controller", func() { PredictorExtensionSpec: v1beta1.PredictorExtensionSpec{ StorageURI: &storageUri, RuntimeVersion: proto.String("1.14.0"), - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, Resources: defaultResource, }, @@ -999,20 +1247,20 @@ var _ = Describe("v1beta1 inference service controller", func() { } isvc.DefaultInferenceService(nil, nil, &v1beta1.SecurityConfig{AutoMountServiceAccountToken: false}, nil) Expect(k8sClient.Create(ctx, isvc)).Should(Succeed()) + defer k8sClient.Delete(ctx, isvc) inferenceService := &v1beta1.InferenceService{} Eventually(func() bool { err := k8sClient.Get(ctx, serviceKey, inferenceService) - if err != nil { - return false - } - return true + return err == nil }, timeout, interval).Should(BeTrue()) actualDeployment := &appsv1.Deployment{} - predictorDeploymentKey := types.NamespacedName{Name: constants.PredictorServiceName(serviceKey.Name), - Namespace: serviceKey.Namespace} + predictorDeploymentKey := types.NamespacedName{ + Name: constants.PredictorServiceName(serviceKey.Name), + Namespace: serviceKey.Namespace, + } Eventually(func() error { return k8sClient.Get(context.TODO(), predictorDeploymentKey, actualDeployment) }, timeout). Should(Succeed()) var replicas int32 = 1 @@ -1031,7 +1279,7 @@ var _ = Describe("v1beta1 inference service controller", func() { "app": "isvc." + predictorDeploymentKey.Name, }, }, - Template: v1.PodTemplateSpec{ + Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Name: predictorDeploymentKey.Name, Namespace: "default", @@ -1046,8 +1294,8 @@ var _ = Describe("v1beta1 inference service controller", func() { "serving.kserve.io/autoscalerClass": "external", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Image: "tensorflow/serving:" + *isvc.Spec.Predictor.Model.RuntimeVersion, @@ -1060,9 +1308,9 @@ var _ = Describe("v1beta1 inference service controller", func() { "--rest_api_timeout_in_ms=60000", }, Resources: defaultResource, - ReadinessProbe: &v1.Probe{ - ProbeHandler: v1.ProbeHandler{ - TCPSocket: &v1.TCPSocketAction{ + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + TCPSocket: &corev1.TCPSocketAction{ Port: intstr.IntOrString{ IntVal: 8080, }, @@ -1083,7 +1331,7 @@ var _ = Describe("v1beta1 inference service controller", func() { RestartPolicy: "Always", TerminationGracePeriodSeconds: &gracePeriod, DNSPolicy: "ClusterFirst", - SecurityContext: &v1.PodSecurityContext{ + SecurityContext: &corev1.PodSecurityContext{ SELinuxOptions: nil, WindowsOptions: nil, RunAsUser: nil, @@ -1109,22 +1357,24 @@ var _ = Describe("v1beta1 inference service controller", func() { ProgressDeadlineSeconds: &progressDeadlineSeconds, }, } - Expect(actualDeployment.Spec).To(Equal(expectedDeployment.Spec)) + Expect(actualDeployment.Spec).To(BeComparableTo(expectedDeployment.Spec)) - //check service - actualService := &v1.Service{} - predictorServiceKey := types.NamespacedName{Name: constants.PredictorServiceName(serviceKey.Name), - Namespace: serviceKey.Namespace} + // check service + actualService := &corev1.Service{} + predictorServiceKey := types.NamespacedName{ + Name: constants.PredictorServiceName(serviceKey.Name), + Namespace: serviceKey.Namespace, + } Eventually(func() error { return k8sClient.Get(context.TODO(), predictorServiceKey, actualService) }, timeout). Should(Succeed()) - expectedService := &v1.Service{ + expectedService := &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: predictorServiceKey.Name, Namespace: predictorServiceKey.Namespace, }, - Spec: v1.ServiceSpec{ - Ports: []v1.ServicePort{ + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ { Name: "raw-foo-2-predictor", Protocol: "TCP", @@ -1144,74 +1394,193 @@ var _ = Describe("v1beta1 inference service controller", func() { actualService.Spec.IPFamilies = nil actualService.Spec.IPFamilyPolicy = nil actualService.Spec.InternalTrafficPolicy = nil - Expect(actualService.Spec).To(Equal(expectedService.Spec)) + Expect(actualService.Spec).To(BeComparableTo(expectedService.Spec)) - //check isvc status + // check isvc status updatedDeployment := actualDeployment.DeepCopy() updatedDeployment.Status.Conditions = []appsv1.DeploymentCondition{ { Type: appsv1.DeploymentAvailable, - Status: v1.ConditionTrue, + Status: corev1.ConditionTrue, }, } Expect(k8sClient.Status().Update(context.TODO(), updatedDeployment)).NotTo(HaveOccurred()) - //check ingress - pathType := netv1.PathTypePrefix - actualIngress := &netv1.Ingress{} - predictorIngressKey := types.NamespacedName{Name: serviceKey.Name, - Namespace: serviceKey.Namespace} - Eventually(func() error { return k8sClient.Get(context.TODO(), predictorIngressKey, actualIngress) }, timeout). + // check http Route + actualToplevelHttpRoute := &gatewayapiv1.HTTPRoute{} + Eventually(func() error { + return k8sClient.Get(context.TODO(), types.NamespacedName{ + Name: serviceKey.Name, + Namespace: serviceKey.Namespace, + }, actualToplevelHttpRoute) + }, timeout). Should(Succeed()) - expectedIngress := netv1.Ingress{ - Spec: netv1.IngressSpec{ - Rules: []netv1.IngressRule{ + topLevelHost := fmt.Sprintf("%s-%s.%s", serviceKey.Name, serviceKey.Namespace, "example.com") + expectedToplevelHttpRoute := gatewayapiv1.HTTPRoute{ + Spec: gatewayapiv1.HTTPRouteSpec{ + Hostnames: []gatewayapiv1.Hostname{gatewayapiv1.Hostname(topLevelHost), "additional.example.com"}, + Rules: []gatewayapiv1.HTTPRouteRule{ { - Host: "raw-foo-2-default.example.com", - IngressRuleValue: netv1.IngressRuleValue{ - HTTP: &netv1.HTTPIngressRuleValue{ - Paths: []netv1.HTTPIngressPath{ - { - Path: "/", - PathType: &pathType, - Backend: netv1.IngressBackend{ - Service: &netv1.IngressServiceBackend{ - Name: "raw-foo-2-predictor", - Port: netv1.ServiceBackendPort{ - Number: 80, - }, - }, + Matches: []gatewayapiv1.HTTPRouteMatch{ + { + Path: &gatewayapiv1.HTTPPathMatch{ + Type: ptr.To(gatewayapiv1.PathMatchRegularExpression), + Value: ptr.To(constants.FallbackPrefix()), + }, + }, + }, + Filters: []gatewayapiv1.HTTPRouteFilter{ + { + Type: gatewayapiv1.HTTPRouteFilterRequestHeaderModifier, + RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ + Set: []gatewayapiv1.HTTPHeader{ + { + Name: constants.IsvcNameHeader, + Value: serviceKey.Name, + }, + { + Name: constants.IsvcNamespaceHeader, + Value: serviceKey.Namespace, }, }, }, }, }, + BackendRefs: []gatewayapiv1.HTTPBackendRef{ + { + BackendRef: gatewayapiv1.BackendRef{ + BackendObjectReference: gatewayapiv1.BackendObjectReference{ + Group: (*gatewayapiv1.Group)(ptr.To("")), + Kind: ptr.To(gatewayapiv1.Kind(constants.ServiceKind)), + Name: gatewayapiv1.ObjectName(predictorServiceKey.Name), + Namespace: (*gatewayapiv1.Namespace)(ptr.To(serviceKey.Namespace)), + Port: (*gatewayapiv1.PortNumber)(ptr.To(int32(constants.CommonDefaultHttpPort))), + }, + Weight: ptr.To(int32(1)), + }, + }, + }, + Timeouts: &gatewayapiv1.HTTPRouteTimeouts{ + Request: ptr.To(gatewayapiv1.Duration("60s")), + }, }, + }, + CommonRouteSpec: gatewayapiv1.CommonRouteSpec{ + ParentRefs: []gatewayapiv1.ParentReference{ + { + Name: gatewayapiv1.ObjectName(kserveGateway.Name), + Kind: ptr.To(gatewayapiv1.Kind(constants.GatewayKind)), + Group: (*gatewayapiv1.Group)(&gatewayapiv1.GroupVersion.Group), + Namespace: ptr.To(gatewayapiv1.Namespace(kserveGateway.Namespace)), + }, + }, + }, + }, + } + Expect(actualToplevelHttpRoute.Spec).To(BeComparableTo(expectedToplevelHttpRoute.Spec)) + + actualPredictorHttpRoute := &gatewayapiv1.HTTPRoute{} + Eventually(func() error { + return k8sClient.Get(context.TODO(), types.NamespacedName{ + Name: predictorServiceKey.Name, + Namespace: serviceKey.Namespace, + }, actualPredictorHttpRoute) + }, timeout). + Should(Succeed()) + predictorHost := fmt.Sprintf("%s-%s.%s", predictorServiceKey.Name, serviceKey.Namespace, "example.com") + expectedPredictorHttpRoute := gatewayapiv1.HTTPRoute{ + Spec: gatewayapiv1.HTTPRouteSpec{ + Hostnames: []gatewayapiv1.Hostname{gatewayapiv1.Hostname(predictorHost)}, + Rules: []gatewayapiv1.HTTPRouteRule{ { - Host: "raw-foo-2-predictor-default.example.com", - IngressRuleValue: netv1.IngressRuleValue{ - HTTP: &netv1.HTTPIngressRuleValue{ - Paths: []netv1.HTTPIngressPath{ - { - Path: "/", - PathType: &pathType, - Backend: netv1.IngressBackend{ - Service: &netv1.IngressServiceBackend{ - Name: "raw-foo-2-predictor", - Port: netv1.ServiceBackendPort{ - Number: 80, - }, - }, + Matches: []gatewayapiv1.HTTPRouteMatch{ + { + Path: &gatewayapiv1.HTTPPathMatch{ + Type: ptr.To(gatewayapiv1.PathMatchRegularExpression), + Value: ptr.To(constants.FallbackPrefix()), + }, + }, + }, + Filters: []gatewayapiv1.HTTPRouteFilter{ + { + Type: gatewayapiv1.HTTPRouteFilterRequestHeaderModifier, + RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ + Set: []gatewayapiv1.HTTPHeader{ + { + Name: constants.IsvcNameHeader, + Value: serviceKey.Name, + }, + { + Name: constants.IsvcNamespaceHeader, + Value: serviceKey.Namespace, }, }, }, }, }, + BackendRefs: []gatewayapiv1.HTTPBackendRef{ + { + BackendRef: gatewayapiv1.BackendRef{ + BackendObjectReference: gatewayapiv1.BackendObjectReference{ + Group: (*gatewayapiv1.Group)(ptr.To("")), + Kind: ptr.To(gatewayapiv1.Kind(constants.ServiceKind)), + Name: gatewayapiv1.ObjectName(predictorServiceKey.Name), + Namespace: (*gatewayapiv1.Namespace)(ptr.To(serviceKey.Namespace)), + Port: (*gatewayapiv1.PortNumber)(ptr.To(int32(constants.CommonDefaultHttpPort))), + }, + Weight: ptr.To(int32(1)), + }, + }, + }, + Timeouts: &gatewayapiv1.HTTPRouteTimeouts{ + Request: ptr.To(gatewayapiv1.Duration("60s")), + }, + }, + }, + CommonRouteSpec: gatewayapiv1.CommonRouteSpec{ + ParentRefs: []gatewayapiv1.ParentReference{ + { + Name: gatewayapiv1.ObjectName(kserveGateway.Name), + Kind: ptr.To(gatewayapiv1.Kind(constants.GatewayKind)), + Group: (*gatewayapiv1.Group)(&gatewayapiv1.GroupVersion.Group), + Namespace: ptr.To(gatewayapiv1.Namespace(kserveGateway.Namespace)), + }, }, }, }, } - Expect(actualIngress.Spec).To(Equal(expectedIngress.Spec)) + Expect(actualPredictorHttpRoute.Spec).To(BeComparableTo(expectedPredictorHttpRoute.Spec)) + + // Mark the Ingress as accepted to make isvc ready + httpRouteStatus := gatewayapiv1.HTTPRouteStatus{ + RouteStatus: gatewayapiv1.RouteStatus{ + Parents: []gatewayapiv1.RouteParentStatus{ + { + ParentRef: gatewayapiv1.ParentReference{ + Name: gatewayapiv1.ObjectName(kserveGateway.Name), + Kind: ptr.To(gatewayapiv1.Kind(constants.GatewayKind)), + Group: (*gatewayapiv1.Group)(&gatewayapiv1.GroupVersion.Group), + Namespace: ptr.To(gatewayapiv1.Namespace(kserveGateway.Namespace)), + }, + ControllerName: "istio.io/gateway-controller", + Conditions: []metav1.Condition{ + { + Type: string(gatewayapiv1.ListenerConditionAccepted), + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "Route was valid", + LastTransitionTime: metav1.Now(), + }, + }, + }, + }, + }, + } + actualPredictorHttpRoute.Status = httpRouteStatus + Expect(k8sClient.Status().Update(context.Background(), actualPredictorHttpRoute)).NotTo(HaveOccurred()) + actualToplevelHttpRoute.Status = httpRouteStatus + Expect(k8sClient.Status().Update(context.Background(), actualToplevelHttpRoute)).NotTo(HaveOccurred()) + // verify if InferenceService status is updated expectedIsvcStatus := v1beta1.InferenceServiceStatus{ Status: duckv1.Status{ @@ -1262,15 +1631,40 @@ var _ = Describe("v1beta1 inference service controller", func() { return cmp.Diff(&expectedIsvcStatus, &isvc.Status, cmpopts.IgnoreTypes(apis.VolatileTime{})) }, timeout).Should(BeEmpty()) - //check HPA is not created + // check HPA is not created actualHPA := &autoscalingv2.HorizontalPodAutoscaler{} - predictorHPAKey := types.NamespacedName{Name: constants.DefaultPredictorServiceName(serviceKey.Name), - Namespace: serviceKey.Namespace} + predictorHPAKey := types.NamespacedName{ + Name: constants.DefaultPredictorServiceName(serviceKey.Name), + Namespace: serviceKey.Namespace, + } Eventually(func() error { return k8sClient.Get(context.TODO(), predictorHPAKey, actualHPA) }, timeout). Should(HaveOccurred()) + + // Replica should not be nil and it should be set to minReplicas if it was set. + updated_isvc := &v1beta1.InferenceService{} + + Eventually(func() error { + return k8sClient.Get(ctx, serviceKey, updated_isvc) + }, timeout, interval).Should(Succeed()) + if updated_isvc.Labels == nil { + updated_isvc.Labels = make(map[string]string) + } + updated_isvc.Spec.Predictor.ComponentExtensionSpec = v1beta1.ComponentExtensionSpec{ + MinReplicas: ptr.To(int32(2)), + } + Expect(k8sClient.Update(context.TODO(), updated_isvc)).NotTo(HaveOccurred()) + + updatedDeployment_isvc_updated := &appsv1.Deployment{} + Eventually(func() bool { + if err := k8sClient.Get(context.TODO(), predictorDeploymentKey, updatedDeployment_isvc_updated); err == nil { + return updatedDeployment_isvc_updated.Spec.Replicas != nil && *updatedDeployment_isvc_updated.Spec.Replicas == 2 + } else { + return false + } + }, timeout, interval).Should(BeTrue()) }) }) - Context("When creating inference service with raw kube predictor and empty ingressClassName", func() { + Context("When creating inference service with raw kube predictor and ingress creation disabled", func() { configs := map[string]string{ "explainers": `{ "alibi": { @@ -1279,17 +1673,20 @@ var _ = Describe("v1beta1 inference service controller", func() { } }`, "ingress": `{ - "ingressGateway": "knative-serving/knative-ingress-gateway", - "localGateway": "knative-serving/knative-local-gateway", - "localGatewayService": "knative-local-gateway.istio-system.svc.cluster.local", - "ingressDomain": "example.com" - }`, + "kserveIngressGateway": "kserve/kserve-ingress-gateway", + "ingressGateway": "knative-serving/knative-ingress-gateway", + "localGateway": "knative-serving/knative-local-gateway", + "localGatewayService": "knative-local-gateway.istio-system.svc.cluster.local", + "ingressDomain": "example.com", + "additionalIngressDomains": ["additional.example.com"], + "disableIngressCreation": true + }`, } - It("Should have ingress/service/deployment/hpa created", func() { + It("Should have service/deployment/hpa created and http route should not be created", func() { By("By creating a new InferenceService") // Create configmap - var configMap = &v1.ConfigMap{ + configMap := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: constants.InferenceServiceConfigMapName, Namespace: constants.KServeNamespace, @@ -1313,7 +1710,7 @@ var _ = Describe("v1beta1 inference service controller", func() { }, }, ServingRuntimePodSpec: v1alpha1.ServingRuntimePodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Name: "kserve-container", Image: "tensorflow/serving:1.14.0", @@ -1335,9 +1732,9 @@ var _ = Describe("v1beta1 inference service controller", func() { defer k8sClient.Delete(context.TODO(), servingRuntime) // Create InferenceService serviceName := "raw-foo-no-ingress-class" - var expectedRequest = reconcile.Request{NamespacedName: types.NamespacedName{Name: serviceName, Namespace: "default"}} - var serviceKey = expectedRequest.NamespacedName - var storageUri = "s3://test/mnist/export" + expectedRequest := reconcile.Request{NamespacedName: types.NamespacedName{Name: serviceName, Namespace: "default"}} + serviceKey := expectedRequest.NamespacedName + storageUri := "s3://test/mnist/export" ctx := context.Background() isvc := &v1beta1.InferenceService{ ObjectMeta: metav1.ObjectMeta{ @@ -1353,14 +1750,14 @@ var _ = Describe("v1beta1 inference service controller", func() { Spec: v1beta1.InferenceServiceSpec{ Predictor: v1beta1.PredictorSpec{ ComponentExtensionSpec: v1beta1.ComponentExtensionSpec{ - MinReplicas: v1beta1.GetIntReference(1), + MinReplicas: ptr.To(int32(1)), MaxReplicas: 3, }, Tensorflow: &v1beta1.TFServingSpec{ PredictorExtensionSpec: v1beta1.PredictorExtensionSpec{ StorageURI: &storageUri, RuntimeVersion: proto.String("1.14.0"), - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, Resources: defaultResource, }, @@ -1371,20 +1768,20 @@ var _ = Describe("v1beta1 inference service controller", func() { } isvc.DefaultInferenceService(nil, nil, &v1beta1.SecurityConfig{AutoMountServiceAccountToken: false}, nil) Expect(k8sClient.Create(ctx, isvc)).Should(Succeed()) + defer k8sClient.Delete(ctx, isvc) inferenceService := &v1beta1.InferenceService{} Eventually(func() bool { err := k8sClient.Get(ctx, serviceKey, inferenceService) - if err != nil { - return false - } - return true + return err == nil }, timeout, interval).Should(BeTrue()) actualDeployment := &appsv1.Deployment{} - predictorDeploymentKey := types.NamespacedName{Name: constants.PredictorServiceName(serviceKey.Name), - Namespace: serviceKey.Namespace} + predictorDeploymentKey := types.NamespacedName{ + Name: constants.PredictorServiceName(serviceKey.Name), + Namespace: serviceKey.Namespace, + } Eventually(func() error { return k8sClient.Get(context.TODO(), predictorDeploymentKey, actualDeployment) }, timeout). Should(Succeed()) var replicas int32 = 1 @@ -1403,7 +1800,7 @@ var _ = Describe("v1beta1 inference service controller", func() { "app": "isvc." + predictorDeploymentKey.Name, }, }, - Template: v1.PodTemplateSpec{ + Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Name: predictorDeploymentKey.Name, Namespace: "default", @@ -1420,8 +1817,8 @@ var _ = Describe("v1beta1 inference service controller", func() { "serving.kserve.io/targetUtilizationPercentage": "75", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Image: "tensorflow/serving:" + *isvc.Spec.Predictor.Model.RuntimeVersion, @@ -1434,9 +1831,9 @@ var _ = Describe("v1beta1 inference service controller", func() { "--rest_api_timeout_in_ms=60000", }, Resources: defaultResource, - ReadinessProbe: &v1.Probe{ - ProbeHandler: v1.ProbeHandler{ - TCPSocket: &v1.TCPSocketAction{ + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + TCPSocket: &corev1.TCPSocketAction{ Port: intstr.IntOrString{ IntVal: 8080, }, @@ -1457,7 +1854,7 @@ var _ = Describe("v1beta1 inference service controller", func() { RestartPolicy: "Always", TerminationGracePeriodSeconds: &gracePeriod, DNSPolicy: "ClusterFirst", - SecurityContext: &v1.PodSecurityContext{ + SecurityContext: &corev1.PodSecurityContext{ SELinuxOptions: nil, WindowsOptions: nil, RunAsUser: nil, @@ -1483,22 +1880,24 @@ var _ = Describe("v1beta1 inference service controller", func() { ProgressDeadlineSeconds: &progressDeadlineSeconds, }, } - Expect(actualDeployment.Spec).To(Equal(expectedDeployment.Spec)) + Expect(actualDeployment.Spec).To(BeComparableTo(expectedDeployment.Spec)) - //check service - actualService := &v1.Service{} - predictorServiceKey := types.NamespacedName{Name: constants.PredictorServiceName(serviceKey.Name), - Namespace: serviceKey.Namespace} + // check service + actualService := &corev1.Service{} + predictorServiceKey := types.NamespacedName{ + Name: constants.PredictorServiceName(serviceKey.Name), + Namespace: serviceKey.Namespace, + } Eventually(func() error { return k8sClient.Get(context.TODO(), predictorServiceKey, actualService) }, timeout). Should(Succeed()) - expectedService := &v1.Service{ + expectedService := &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: predictorServiceKey.Name, Namespace: predictorServiceKey.Namespace, }, - Spec: v1.ServiceSpec{ - Ports: []v1.ServicePort{ + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ { Name: constants.PredictorServiceName(serviceName), Protocol: "TCP", @@ -1509,7 +1908,7 @@ var _ = Describe("v1beta1 inference service controller", func() { Type: "ClusterIP", SessionAffinity: "None", Selector: map[string]string{ - "app": fmt.Sprintf("isvc.%s", constants.PredictorServiceName(serviceName)), + "app": "isvc." + constants.PredictorServiceName(serviceName), }, }, } @@ -1518,78 +1917,4935 @@ var _ = Describe("v1beta1 inference service controller", func() { actualService.Spec.IPFamilies = nil actualService.Spec.IPFamilyPolicy = nil actualService.Spec.InternalTrafficPolicy = nil - Expect(actualService.Spec).To(Equal(expectedService.Spec)) + Expect(actualService.Spec).To(BeComparableTo(expectedService.Spec)) - //check isvc status + // check isvc status updatedDeployment := actualDeployment.DeepCopy() updatedDeployment.Status.Conditions = []appsv1.DeploymentCondition{ { Type: appsv1.DeploymentAvailable, - Status: v1.ConditionTrue, + Status: corev1.ConditionTrue, + }, + } + Expect(k8sClient.Status().Update(context.TODO(), updatedDeployment)).NotTo(HaveOccurred()) + + // check ingress not created + actualToplevelHttpRoute := &gatewayapiv1.HTTPRoute{} + Consistently(func() error { + return k8sClient.Get(context.TODO(), types.NamespacedName{ + Name: serviceKey.Name, + Namespace: serviceKey.Namespace, + }, actualToplevelHttpRoute) + }, timeout). + Should(Not(Succeed())) + actualPredictorHttpRoute := &gatewayapiv1.HTTPRoute{} + Consistently(func() error { + return k8sClient.Get(context.TODO(), types.NamespacedName{ + Name: predictorServiceKey.Name, + Namespace: serviceKey.Namespace, + }, actualPredictorHttpRoute) + }, timeout). + Should(Not(Succeed())) + + // verify if InferenceService status is updated + expectedIsvcStatus := v1beta1.InferenceServiceStatus{ + Status: duckv1.Status{ + Conditions: duckv1.Conditions{ + { + Type: v1beta1.IngressReady, + Status: "True", + }, + { + Type: v1beta1.PredictorReady, + Status: "True", + }, + { + Type: apis.ConditionReady, + Status: "True", + }, + }, + }, + URL: &apis.URL{ + Scheme: "http", + Host: serviceName + "-default.example.com", + }, + Address: &duckv1.Addressable{ + URL: &apis.URL{ + Scheme: "http", + Host: fmt.Sprintf("%s-predictor.%s.svc.cluster.local", serviceKey.Name, serviceKey.Namespace), + }, + }, + Components: map[v1beta1.ComponentType]v1beta1.ComponentStatusSpec{ + v1beta1.PredictorComponent: { + LatestCreatedRevision: "", + URL: &apis.URL{ + Scheme: "http", + Host: serviceName + "-predictor-default.example.com", + }, + }, + }, + ModelStatus: v1beta1.ModelStatus{ + TransitionStatus: "InProgress", + ModelRevisionStates: &v1beta1.ModelRevisionStates{TargetModelState: "Pending"}, + }, + } + Eventually(func() string { + isvc := &v1beta1.InferenceService{} + if err := k8sClient.Get(context.TODO(), serviceKey, isvc); err != nil { + return err.Error() + } + return cmp.Diff(&expectedIsvcStatus, &isvc.Status, cmpopts.IgnoreTypes(apis.VolatileTime{})) + }, timeout).Should(BeEmpty()) + + // check HPA + var minReplicas int32 = 1 + var maxReplicas int32 = 3 + var cpuUtilization int32 = 75 + var stabilizationWindowSeconds int32 = 0 + selectPolicy := autoscalingv2.MaxChangePolicySelect + actualHPA := &autoscalingv2.HorizontalPodAutoscaler{} + predictorHPAKey := types.NamespacedName{ + Name: predictorServiceKey.Name, + Namespace: serviceKey.Namespace, + } + Eventually(func() error { return k8sClient.Get(context.TODO(), predictorHPAKey, actualHPA) }, timeout). + Should(Succeed()) + expectedHPA := &autoscalingv2.HorizontalPodAutoscaler{ + Spec: autoscalingv2.HorizontalPodAutoscalerSpec{ + ScaleTargetRef: autoscalingv2.CrossVersionObjectReference{ + APIVersion: "apps/v1", + Kind: "Deployment", + Name: predictorServiceKey.Name, + }, + MinReplicas: &minReplicas, + MaxReplicas: maxReplicas, + Metrics: []autoscalingv2.MetricSpec{ + { + Type: autoscalingv2.ResourceMetricSourceType, + Resource: &autoscalingv2.ResourceMetricSource{ + Name: corev1.ResourceCPU, + Target: autoscalingv2.MetricTarget{ + Type: "Utilization", + AverageUtilization: &cpuUtilization, + }, + }, + }, + }, + Behavior: &autoscalingv2.HorizontalPodAutoscalerBehavior{ + ScaleUp: &autoscalingv2.HPAScalingRules{ + StabilizationWindowSeconds: &stabilizationWindowSeconds, + SelectPolicy: &selectPolicy, + Policies: []autoscalingv2.HPAScalingPolicy{ + { + Type: "Pods", + Value: 4, + PeriodSeconds: 15, + }, + { + Type: "Percent", + Value: 100, + PeriodSeconds: 15, + }, + }, + }, + ScaleDown: &autoscalingv2.HPAScalingRules{ + StabilizationWindowSeconds: nil, + SelectPolicy: &selectPolicy, + Policies: []autoscalingv2.HPAScalingPolicy{ + { + Type: "Percent", + Value: 100, + PeriodSeconds: 15, + }, + }, + }, + }, + }, + } + Expect(actualHPA.Spec).To(BeComparableTo(expectedHPA.Spec)) + }) + }) + Context("When creating inference service with raw kube predictor with domain template", func() { + configs := map[string]string{ + "explainers": `{ + "alibi": { + "image": "kfserving/alibi-explainer", + "defaultImageVersion": "latest" + } + }`, + "ingress": `{ + "enableGatewayApi": true, + "kserveIngressGateway": "kserve/kserve-ingress-gateway", + "ingressGateway": "knative-serving/knative-ingress-gateway", + "localGateway": "knative-serving/knative-local-gateway", + "localGatewayService": "knative-local-gateway.istio-system.svc.cluster.local", + "ingressDomain": "example.com", + "domainTemplate": "{{ .Name }}.{{ .Namespace }}.{{ .IngressDomain }}", + "additionalIngressDomains": ["additional.example.com"] + }`, + } + + It("Should have httproute/service/deployment/hpa created", func() { + By("By creating a new InferenceService") + // Create configmap + configMap := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: constants.InferenceServiceConfigMapName, + Namespace: constants.KServeNamespace, + }, + Data: configs, + } + Expect(k8sClient.Create(context.TODO(), configMap)).NotTo(HaveOccurred()) + defer k8sClient.Delete(context.TODO(), configMap) + // Create ServingRuntime + servingRuntime := &v1alpha1.ServingRuntime{ + ObjectMeta: metav1.ObjectMeta{ + Name: "tf-serving-raw", + Namespace: "default", + }, + Spec: v1alpha1.ServingRuntimeSpec{ + SupportedModelFormats: []v1alpha1.SupportedModelFormat{ + { + Name: "tensorflow", + Version: proto.String("1"), + AutoSelect: proto.Bool(true), + }, + }, + ServingRuntimePodSpec: v1alpha1.ServingRuntimePodSpec{ + Containers: []corev1.Container{ + { + Name: "kserve-container", + Image: "tensorflow/serving:1.14.0", + Command: []string{"/usr/bin/tensorflow_model_server"}, + Args: []string{ + "--port=9000", + "--rest_api_port=8080", + "--model_base_path=/mnt/models", + "--rest_api_timeout_in_ms=60000", + }, + Resources: defaultResource, + }, + }, + }, + Disabled: proto.Bool(false), + }, + } + k8sClient.Create(context.TODO(), servingRuntime) + defer k8sClient.Delete(context.TODO(), servingRuntime) + // Create InferenceService + serviceName := "model" + expectedRequest := reconcile.Request{NamespacedName: types.NamespacedName{Name: serviceName, Namespace: "default"}} + serviceKey := expectedRequest.NamespacedName + storageUri := "s3://test/mnist/export" + ctx := context.Background() + isvc := &v1beta1.InferenceService{ + ObjectMeta: metav1.ObjectMeta{ + Name: serviceKey.Name, + Namespace: serviceKey.Namespace, + Annotations: map[string]string{ + "serving.kserve.io/deploymentMode": "RawDeployment", + "serving.kserve.io/autoscalerClass": "hpa", + "serving.kserve.io/metrics": "cpu", + "serving.kserve.io/targetUtilizationPercentage": "75", + }, + }, + Spec: v1beta1.InferenceServiceSpec{ + Predictor: v1beta1.PredictorSpec{ + ComponentExtensionSpec: v1beta1.ComponentExtensionSpec{ + MinReplicas: ptr.To(int32(1)), + MaxReplicas: 3, + }, + Tensorflow: &v1beta1.TFServingSpec{ + PredictorExtensionSpec: v1beta1.PredictorExtensionSpec{ + StorageURI: &storageUri, + RuntimeVersion: proto.String("1.14.0"), + Container: corev1.Container{ + Name: constants.InferenceServiceContainerName, + Resources: defaultResource, + }, + }, + }, + }, + }, + } + isvc.DefaultInferenceService(nil, nil, &v1beta1.SecurityConfig{AutoMountServiceAccountToken: false}, nil) + Expect(k8sClient.Create(ctx, isvc)).Should(Succeed()) + defer k8sClient.Delete(ctx, isvc) + + inferenceService := &v1beta1.InferenceService{} + + Eventually(func() bool { + err := k8sClient.Get(ctx, serviceKey, inferenceService) + return err == nil + }, timeout, interval).Should(BeTrue()) + + actualDeployment := &appsv1.Deployment{} + predictorDeploymentKey := types.NamespacedName{ + Name: constants.PredictorServiceName(serviceKey.Name), + Namespace: serviceKey.Namespace, + } + Eventually(func() error { return k8sClient.Get(context.TODO(), predictorDeploymentKey, actualDeployment) }, timeout). + Should(Succeed()) + var replicas int32 = 1 + var revisionHistory int32 = 10 + var progressDeadlineSeconds int32 = 600 + var gracePeriod int64 = 30 + expectedDeployment := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: predictorDeploymentKey.Name, + Namespace: predictorDeploymentKey.Namespace, + }, + Spec: appsv1.DeploymentSpec{ + Replicas: &replicas, + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "isvc." + predictorDeploymentKey.Name, + }, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Name: predictorDeploymentKey.Name, + Namespace: "default", + Labels: map[string]string{ + "app": "isvc." + predictorDeploymentKey.Name, + constants.KServiceComponentLabel: constants.Predictor.String(), + constants.InferenceServicePodLabelKey: serviceName, + }, + Annotations: map[string]string{ + constants.StorageInitializerSourceUriInternalAnnotationKey: *isvc.Spec.Predictor.Model.StorageURI, + "serving.kserve.io/deploymentMode": "RawDeployment", + "serving.kserve.io/autoscalerClass": "hpa", + "serving.kserve.io/metrics": "cpu", + "serving.kserve.io/targetUtilizationPercentage": "75", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Image: "tensorflow/serving:" + + *isvc.Spec.Predictor.Model.RuntimeVersion, + Name: constants.InferenceServiceContainerName, + Command: []string{v1beta1.TensorflowEntrypointCommand}, + Args: []string{ + "--port=" + v1beta1.TensorflowServingGRPCPort, + "--rest_api_port=" + v1beta1.TensorflowServingRestPort, + "--model_base_path=" + constants.DefaultModelLocalMountPath, + "--rest_api_timeout_in_ms=60000", + }, + Resources: defaultResource, + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + TCPSocket: &corev1.TCPSocketAction{ + Port: intstr.IntOrString{ + IntVal: 8080, + }, + }, + }, + InitialDelaySeconds: 0, + TimeoutSeconds: 1, + PeriodSeconds: 10, + SuccessThreshold: 1, + FailureThreshold: 3, + }, + TerminationMessagePath: "/dev/termination-log", + TerminationMessagePolicy: "File", + ImagePullPolicy: "IfNotPresent", + }, + }, + SchedulerName: "default-scheduler", + RestartPolicy: "Always", + TerminationGracePeriodSeconds: &gracePeriod, + DNSPolicy: "ClusterFirst", + SecurityContext: &corev1.PodSecurityContext{ + SELinuxOptions: nil, + WindowsOptions: nil, + RunAsUser: nil, + RunAsGroup: nil, + RunAsNonRoot: nil, + SupplementalGroups: nil, + FSGroup: nil, + Sysctls: nil, + FSGroupChangePolicy: nil, + SeccompProfile: nil, + }, + AutomountServiceAccountToken: proto.Bool(false), + }, + }, + Strategy: appsv1.DeploymentStrategy{ + Type: "RollingUpdate", + RollingUpdate: &appsv1.RollingUpdateDeployment{ + MaxUnavailable: &intstr.IntOrString{Type: 1, IntVal: 0, StrVal: "25%"}, + MaxSurge: &intstr.IntOrString{Type: 1, IntVal: 0, StrVal: "25%"}, + }, + }, + RevisionHistoryLimit: &revisionHistory, + ProgressDeadlineSeconds: &progressDeadlineSeconds, + }, + } + Expect(actualDeployment.Spec).To(BeComparableTo(expectedDeployment.Spec)) + + // check service + actualService := &corev1.Service{} + predictorServiceKey := types.NamespacedName{ + Name: constants.PredictorServiceName(serviceKey.Name), + Namespace: serviceKey.Namespace, + } + Eventually(func() error { return k8sClient.Get(context.TODO(), predictorServiceKey, actualService) }, timeout). + Should(Succeed()) + + expectedService := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: predictorServiceKey.Name, + Namespace: predictorServiceKey.Namespace, + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Name: constants.PredictorServiceName(serviceName), + Protocol: "TCP", + Port: 80, + TargetPort: intstr.IntOrString{Type: 0, IntVal: 8080, StrVal: ""}, + }, + }, + Type: "ClusterIP", + SessionAffinity: "None", + Selector: map[string]string{ + "app": "isvc." + constants.PredictorServiceName(serviceName), + }, + }, + } + actualService.Spec.ClusterIP = "" + actualService.Spec.ClusterIPs = nil + actualService.Spec.IPFamilies = nil + actualService.Spec.IPFamilyPolicy = nil + actualService.Spec.InternalTrafficPolicy = nil + Expect(actualService.Spec).To(BeComparableTo(expectedService.Spec)) + + // check isvc status + updatedDeployment := actualDeployment.DeepCopy() + updatedDeployment.Status.Conditions = []appsv1.DeploymentCondition{ + { + Type: appsv1.DeploymentAvailable, + Status: corev1.ConditionTrue, + }, + } + Expect(k8sClient.Status().Update(context.TODO(), updatedDeployment)).NotTo(HaveOccurred()) + + // check http route + actualToplevelHttpRoute := &gatewayapiv1.HTTPRoute{} + Eventually(func() error { + return k8sClient.Get(context.TODO(), types.NamespacedName{ + Name: serviceKey.Name, + Namespace: serviceKey.Namespace, + }, actualToplevelHttpRoute) + }, timeout). + Should(Succeed()) + topLevelHost := fmt.Sprintf("%s.%s.%s", serviceKey.Name, serviceKey.Namespace, "example.com") + expectedToplevelHttpRoute := gatewayapiv1.HTTPRoute{ + Spec: gatewayapiv1.HTTPRouteSpec{ + Hostnames: []gatewayapiv1.Hostname{gatewayapiv1.Hostname(topLevelHost), "additional.example.com"}, + Rules: []gatewayapiv1.HTTPRouteRule{ + { + Matches: []gatewayapiv1.HTTPRouteMatch{ + { + Path: &gatewayapiv1.HTTPPathMatch{ + Type: ptr.To(gatewayapiv1.PathMatchRegularExpression), + Value: ptr.To(constants.FallbackPrefix()), + }, + }, + }, + Filters: []gatewayapiv1.HTTPRouteFilter{ + { + Type: gatewayapiv1.HTTPRouteFilterRequestHeaderModifier, + RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ + Set: []gatewayapiv1.HTTPHeader{ + { + Name: constants.IsvcNameHeader, + Value: serviceKey.Name, + }, + { + Name: constants.IsvcNamespaceHeader, + Value: serviceKey.Namespace, + }, + }, + }, + }, + }, + BackendRefs: []gatewayapiv1.HTTPBackendRef{ + { + BackendRef: gatewayapiv1.BackendRef{ + BackendObjectReference: gatewayapiv1.BackendObjectReference{ + Group: (*gatewayapiv1.Group)(ptr.To("")), + Kind: ptr.To(gatewayapiv1.Kind(constants.ServiceKind)), + Name: gatewayapiv1.ObjectName(predictorServiceKey.Name), + Namespace: (*gatewayapiv1.Namespace)(ptr.To(serviceKey.Namespace)), + Port: (*gatewayapiv1.PortNumber)(ptr.To(int32(constants.CommonDefaultHttpPort))), + }, + Weight: ptr.To(int32(1)), + }, + }, + }, + Timeouts: &gatewayapiv1.HTTPRouteTimeouts{ + Request: ptr.To(gatewayapiv1.Duration("60s")), + }, + }, + }, + CommonRouteSpec: gatewayapiv1.CommonRouteSpec{ + ParentRefs: []gatewayapiv1.ParentReference{ + { + Name: gatewayapiv1.ObjectName(kserveGateway.Name), + Kind: ptr.To(gatewayapiv1.Kind(constants.GatewayKind)), + Group: (*gatewayapiv1.Group)(&gatewayapiv1.GroupVersion.Group), + Namespace: ptr.To(gatewayapiv1.Namespace(kserveGateway.Namespace)), + }, + }, + }, + }, + } + Expect(actualToplevelHttpRoute.Spec).To(BeComparableTo(expectedToplevelHttpRoute.Spec)) + + actualPredictorHttpRoute := &gatewayapiv1.HTTPRoute{} + Eventually(func() error { + return k8sClient.Get(context.TODO(), types.NamespacedName{ + Name: predictorServiceKey.Name, + Namespace: serviceKey.Namespace, + }, actualPredictorHttpRoute) + }, timeout). + Should(Succeed()) + predictorHost := fmt.Sprintf("%s.%s.%s", predictorServiceKey.Name, serviceKey.Namespace, "example.com") + expectedPredictorHttpRoute := gatewayapiv1.HTTPRoute{ + Spec: gatewayapiv1.HTTPRouteSpec{ + Hostnames: []gatewayapiv1.Hostname{gatewayapiv1.Hostname(predictorHost)}, + Rules: []gatewayapiv1.HTTPRouteRule{ + { + Matches: []gatewayapiv1.HTTPRouteMatch{ + { + Path: &gatewayapiv1.HTTPPathMatch{ + Type: ptr.To(gatewayapiv1.PathMatchRegularExpression), + Value: ptr.To(constants.FallbackPrefix()), + }, + }, + }, + Filters: []gatewayapiv1.HTTPRouteFilter{ + { + Type: gatewayapiv1.HTTPRouteFilterRequestHeaderModifier, + RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ + Set: []gatewayapiv1.HTTPHeader{ + { + Name: constants.IsvcNameHeader, + Value: serviceKey.Name, + }, + { + Name: constants.IsvcNamespaceHeader, + Value: serviceKey.Namespace, + }, + }, + }, + }, + }, + BackendRefs: []gatewayapiv1.HTTPBackendRef{ + { + BackendRef: gatewayapiv1.BackendRef{ + BackendObjectReference: gatewayapiv1.BackendObjectReference{ + Group: (*gatewayapiv1.Group)(ptr.To("")), + Kind: ptr.To(gatewayapiv1.Kind(constants.ServiceKind)), + Name: gatewayapiv1.ObjectName(predictorServiceKey.Name), + Namespace: (*gatewayapiv1.Namespace)(ptr.To(serviceKey.Namespace)), + Port: (*gatewayapiv1.PortNumber)(ptr.To(int32(constants.CommonDefaultHttpPort))), + }, + Weight: ptr.To(int32(1)), + }, + }, + }, + Timeouts: &gatewayapiv1.HTTPRouteTimeouts{ + Request: ptr.To(gatewayapiv1.Duration("60s")), + }, + }, + }, + CommonRouteSpec: gatewayapiv1.CommonRouteSpec{ + ParentRefs: []gatewayapiv1.ParentReference{ + { + Name: gatewayapiv1.ObjectName(kserveGateway.Name), + Kind: ptr.To(gatewayapiv1.Kind(constants.GatewayKind)), + Group: (*gatewayapiv1.Group)(&gatewayapiv1.GroupVersion.Group), + Namespace: ptr.To(gatewayapiv1.Namespace(kserveGateway.Namespace)), + }, + }, + }, + }, + } + Expect(actualPredictorHttpRoute.Spec).To(BeComparableTo(expectedPredictorHttpRoute.Spec)) + + // Mark the Ingress as accepted to make isvc ready + httpRouteStatus := gatewayapiv1.HTTPRouteStatus{ + RouteStatus: gatewayapiv1.RouteStatus{ + Parents: []gatewayapiv1.RouteParentStatus{ + { + ParentRef: gatewayapiv1.ParentReference{ + Name: gatewayapiv1.ObjectName(kserveGateway.Name), + Kind: ptr.To(gatewayapiv1.Kind(constants.GatewayKind)), + Group: (*gatewayapiv1.Group)(&gatewayapiv1.GroupVersion.Group), + Namespace: ptr.To(gatewayapiv1.Namespace(kserveGateway.Namespace)), + }, + ControllerName: "istio.io/gateway-controller", + Conditions: []metav1.Condition{ + { + Type: string(gatewayapiv1.ListenerConditionAccepted), + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "Route was valid", + LastTransitionTime: metav1.Now(), + }, + }, + }, + }, + }, + } + actualPredictorHttpRoute.Status = httpRouteStatus + Expect(k8sClient.Status().Update(context.Background(), actualPredictorHttpRoute)).NotTo(HaveOccurred()) + actualToplevelHttpRoute.Status = httpRouteStatus + Expect(k8sClient.Status().Update(context.Background(), actualToplevelHttpRoute)).NotTo(HaveOccurred()) + + // verify if InferenceService status is updated + expectedIsvcStatus := v1beta1.InferenceServiceStatus{ + Status: duckv1.Status{ + Conditions: duckv1.Conditions{ + { + Type: v1beta1.IngressReady, + Status: "True", + }, + { + Type: v1beta1.PredictorReady, + Status: "True", + }, + { + Type: apis.ConditionReady, + Status: "True", + }, + }, + }, + URL: &apis.URL{ + Scheme: "http", + Host: fmt.Sprintf("%s.%s.%s", serviceName, serviceKey.Namespace, domain), + }, + Address: &duckv1.Addressable{ + URL: &apis.URL{ + Scheme: "http", + Host: fmt.Sprintf("%s-predictor.%s.svc.cluster.local", serviceKey.Name, serviceKey.Namespace), + }, + }, + Components: map[v1beta1.ComponentType]v1beta1.ComponentStatusSpec{ + v1beta1.PredictorComponent: { + LatestCreatedRevision: "", + URL: &apis.URL{ + Scheme: "http", + Host: fmt.Sprintf("%s-predictor.%s.%s", serviceName, serviceKey.Namespace, domain), + }, + }, + }, + ModelStatus: v1beta1.ModelStatus{ + TransitionStatus: "InProgress", + ModelRevisionStates: &v1beta1.ModelRevisionStates{TargetModelState: "Pending"}, + }, + } + Eventually(func() string { + isvc := &v1beta1.InferenceService{} + if err := k8sClient.Get(context.TODO(), serviceKey, isvc); err != nil { + return err.Error() + } + return cmp.Diff(&expectedIsvcStatus, &isvc.Status, cmpopts.IgnoreTypes(apis.VolatileTime{})) + }, timeout).Should(BeEmpty()) + + // check HPA + var minReplicas int32 = 1 + var maxReplicas int32 = 3 + var cpuUtilization int32 = 75 + var stabilizationWindowSeconds int32 = 0 + selectPolicy := autoscalingv2.MaxChangePolicySelect + actualHPA := &autoscalingv2.HorizontalPodAutoscaler{} + predictorHPAKey := types.NamespacedName{ + Name: predictorServiceKey.Name, + Namespace: serviceKey.Namespace, + } + Eventually(func() error { return k8sClient.Get(context.TODO(), predictorHPAKey, actualHPA) }, timeout). + Should(Succeed()) + expectedHPA := &autoscalingv2.HorizontalPodAutoscaler{ + Spec: autoscalingv2.HorizontalPodAutoscalerSpec{ + ScaleTargetRef: autoscalingv2.CrossVersionObjectReference{ + APIVersion: "apps/v1", + Kind: "Deployment", + Name: predictorServiceKey.Name, + }, + MinReplicas: &minReplicas, + MaxReplicas: maxReplicas, + Metrics: []autoscalingv2.MetricSpec{ + { + Type: autoscalingv2.ResourceMetricSourceType, + Resource: &autoscalingv2.ResourceMetricSource{ + Name: corev1.ResourceCPU, + Target: autoscalingv2.MetricTarget{ + Type: "Utilization", + AverageUtilization: &cpuUtilization, + }, + }, + }, + }, + Behavior: &autoscalingv2.HorizontalPodAutoscalerBehavior{ + ScaleUp: &autoscalingv2.HPAScalingRules{ + StabilizationWindowSeconds: &stabilizationWindowSeconds, + SelectPolicy: &selectPolicy, + Policies: []autoscalingv2.HPAScalingPolicy{ + { + Type: "Pods", + Value: 4, + PeriodSeconds: 15, + }, + { + Type: "Percent", + Value: 100, + PeriodSeconds: 15, + }, + }, + }, + ScaleDown: &autoscalingv2.HPAScalingRules{ + StabilizationWindowSeconds: nil, + SelectPolicy: &selectPolicy, + Policies: []autoscalingv2.HPAScalingPolicy{ + { + Type: "Percent", + Value: 100, + PeriodSeconds: 15, + }, + }, + }, + }, + }, + } + Expect(actualHPA.Spec).To(BeComparableTo(expectedHPA.Spec)) + }) + }) + Context("When creating inference service with raw kube predictor and transformer", func() { + configs := map[string]string{ + "explainers": `{ + "alibi": { + "image": "kserve/alibi-explainer", + "defaultImageVersion": "latest" + } + }`, + "ingress": `{ + "enableGatewayApi": true, + "kserveIngressGateway": "kserve/kserve-ingress-gateway", + "ingressGateway": "knative-serving/knative-ingress-gateway", + "localGateway": "knative-serving/knative-local-gateway", + "localGatewayService": "knative-local-gateway.istio-system.svc.cluster.local", + "additionalIngressDomains": ["additional.example.com"] + }`, + "storageInitializer": `{ + "image" : "kserve/storage-initializer:latest", + "memoryRequest": "100Mi", + "memoryLimit": "1Gi", + "cpuRequest": "100m", + "cpuLimit": "1", + "CaBundleConfigMapName": "", + "caBundleVolumeMountPath": "/etc/ssl/custom-certs", + "enableDirectPvcVolumeMount": false + }`, + } + + It("Should have httproute/service/deployment/hpa created for transformer and predictor", func() { + By("By creating a new InferenceService") + // Create configmap + configMap := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: constants.InferenceServiceConfigMapName, + Namespace: constants.KServeNamespace, + }, + Data: configs, + } + Expect(k8sClient.Create(context.TODO(), configMap)).NotTo(HaveOccurred()) + defer k8sClient.Delete(context.TODO(), configMap) + // Create ServingRuntime + servingRuntime := &v1alpha1.ServingRuntime{ + ObjectMeta: metav1.ObjectMeta{ + Name: "tf-serving-raw", + Namespace: "default", + }, + Spec: v1alpha1.ServingRuntimeSpec{ + SupportedModelFormats: []v1alpha1.SupportedModelFormat{ + { + Name: "tensorflow", + Version: proto.String("1"), + AutoSelect: proto.Bool(true), + }, + }, + ServingRuntimePodSpec: v1alpha1.ServingRuntimePodSpec{ + Containers: []corev1.Container{ + { + Name: "kserve-container", + Image: "tensorflow/serving:1.14.0", + Command: []string{"/usr/bin/tensorflow_model_server"}, + Args: []string{ + "--port=9000", + "--rest_api_port=8080", + "--model_base_path=/mnt/models", + "--rest_api_timeout_in_ms=60000", + }, + Resources: defaultResource, + }, + }, + }, + Disabled: proto.Bool(false), + }, + } + k8sClient.Create(context.TODO(), servingRuntime) + defer k8sClient.Delete(context.TODO(), servingRuntime) + serviceName := "raw-foo-trans" + namespace := "default" + expectedRequest := reconcile.Request{NamespacedName: types.NamespacedName{Name: serviceName, Namespace: namespace}} + serviceKey := expectedRequest.NamespacedName + predictorServiceKey := types.NamespacedName{ + Name: constants.PredictorServiceName(serviceName), + Namespace: namespace, + } + transformerServiceKey := types.NamespacedName{ + Name: constants.TransformerServiceName(serviceName), + Namespace: namespace, + } + storageUri := "s3://test/mnist/export" + var minReplicas int32 = 1 + var maxReplicas int32 = 3 + var transformerMinReplicas int32 = 1 + var transformerMaxReplicas int32 = 2 + var transformerCpuUtilization int32 = 80 + var transformerStabilizationWindowSeconds int32 = 0 + transformerSelectPolicy := autoscalingv2.MaxChangePolicySelect + ctx := context.Background() + isvc := &v1beta1.InferenceService{ + ObjectMeta: metav1.ObjectMeta{ + Name: serviceKey.Name, + Namespace: serviceKey.Namespace, + Annotations: map[string]string{ + "serving.kserve.io/deploymentMode": "RawDeployment", + "serving.kserve.io/autoscalerClass": "hpa", + "serving.kserve.io/metrics": "cpu", + "serving.kserve.io/targetUtilizationPercentage": "75", + }, + }, + Spec: v1beta1.InferenceServiceSpec{ + Predictor: v1beta1.PredictorSpec{ + ComponentExtensionSpec: v1beta1.ComponentExtensionSpec{ + MinReplicas: ptr.To(minReplicas), + MaxReplicas: maxReplicas, + }, + Tensorflow: &v1beta1.TFServingSpec{ + PredictorExtensionSpec: v1beta1.PredictorExtensionSpec{ + StorageURI: &storageUri, + RuntimeVersion: proto.String("1.14.0"), + Container: corev1.Container{ + Name: constants.InferenceServiceContainerName, + Resources: defaultResource, + }, + }, + }, + }, + Transformer: &v1beta1.TransformerSpec{ + ComponentExtensionSpec: v1beta1.ComponentExtensionSpec{ + MinReplicas: ptr.To(transformerMinReplicas), + MaxReplicas: transformerMaxReplicas, + ScaleTarget: ptr.To(transformerCpuUtilization), + TimeoutSeconds: ptr.To(int64(30)), + }, + PodSpec: v1beta1.PodSpec{ + Containers: []corev1.Container{ + { + Image: "transformer:v1", + Resources: defaultResource, + Args: []string{ + "--port=8080", + }, + }, + }, + }, + }, + }, + } + isvc.DefaultInferenceService(nil, nil, &v1beta1.SecurityConfig{AutoMountServiceAccountToken: false}, nil) + Expect(k8sClient.Create(ctx, isvc)).Should(Succeed()) + defer k8sClient.Delete(ctx, isvc) + + inferenceService := &v1beta1.InferenceService{} + + Eventually(func() bool { + err := k8sClient.Get(ctx, serviceKey, inferenceService) + return err == nil + }, timeout, interval).Should(BeTrue()) + + // check predictor deployment + actualPredictorDeployment := &appsv1.Deployment{} + predictorDeploymentKey := types.NamespacedName{ + Name: predictorServiceKey.Name, + Namespace: serviceKey.Namespace, + } + Eventually(func() error { return k8sClient.Get(context.TODO(), predictorDeploymentKey, actualPredictorDeployment) }, timeout). + Should(Succeed()) + var replicas int32 = 1 + var revisionHistory int32 = 10 + var progressDeadlineSeconds int32 = 600 + var gracePeriod int64 = 30 + expectedPredictorDeployment := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: predictorDeploymentKey.Name, + Namespace: predictorDeploymentKey.Namespace, + }, + Spec: appsv1.DeploymentSpec{ + Replicas: &replicas, + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "isvc." + predictorDeploymentKey.Name, + }, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Name: predictorDeploymentKey.Name, + Namespace: "default", + Labels: map[string]string{ + "app": "isvc." + predictorDeploymentKey.Name, + constants.KServiceComponentLabel: constants.Predictor.String(), + constants.InferenceServicePodLabelKey: serviceName, + }, + Annotations: map[string]string{ + constants.StorageInitializerSourceUriInternalAnnotationKey: *isvc.Spec.Predictor.Model.StorageURI, + "serving.kserve.io/deploymentMode": "RawDeployment", + "serving.kserve.io/autoscalerClass": "hpa", + "serving.kserve.io/metrics": "cpu", + "serving.kserve.io/targetUtilizationPercentage": "75", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Image: "tensorflow/serving:" + + *isvc.Spec.Predictor.Model.RuntimeVersion, + Name: constants.InferenceServiceContainerName, + Command: []string{v1beta1.TensorflowEntrypointCommand}, + Args: []string{ + "--port=" + v1beta1.TensorflowServingGRPCPort, + "--rest_api_port=" + v1beta1.TensorflowServingRestPort, + "--model_base_path=" + constants.DefaultModelLocalMountPath, + "--rest_api_timeout_in_ms=60000", + }, + Resources: defaultResource, + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + TCPSocket: &corev1.TCPSocketAction{ + Port: intstr.IntOrString{ + IntVal: 8080, + }, + }, + }, + InitialDelaySeconds: 0, + TimeoutSeconds: 1, + PeriodSeconds: 10, + SuccessThreshold: 1, + FailureThreshold: 3, + }, + TerminationMessagePath: "/dev/termination-log", + TerminationMessagePolicy: "File", + ImagePullPolicy: "IfNotPresent", + }, + }, + SchedulerName: "default-scheduler", + RestartPolicy: "Always", + TerminationGracePeriodSeconds: &gracePeriod, + DNSPolicy: "ClusterFirst", + SecurityContext: &corev1.PodSecurityContext{ + SELinuxOptions: nil, + WindowsOptions: nil, + RunAsUser: nil, + RunAsGroup: nil, + RunAsNonRoot: nil, + SupplementalGroups: nil, + FSGroup: nil, + Sysctls: nil, + FSGroupChangePolicy: nil, + SeccompProfile: nil, + }, + AutomountServiceAccountToken: proto.Bool(false), + }, + }, + Strategy: appsv1.DeploymentStrategy{ + Type: "RollingUpdate", + RollingUpdate: &appsv1.RollingUpdateDeployment{ + MaxUnavailable: &intstr.IntOrString{Type: 1, IntVal: 0, StrVal: "25%"}, + MaxSurge: &intstr.IntOrString{Type: 1, IntVal: 0, StrVal: "25%"}, + }, + }, + RevisionHistoryLimit: &revisionHistory, + ProgressDeadlineSeconds: &progressDeadlineSeconds, + }, + } + Expect(actualPredictorDeployment.Spec).To(BeComparableTo(expectedPredictorDeployment.Spec)) + + // check transformer deployment + actualTransformerDeployment := &appsv1.Deployment{} + transformerDeploymentKey := types.NamespacedName{ + Name: transformerServiceKey.Name, + Namespace: serviceKey.Namespace, + } + Eventually(func() error { + return k8sClient.Get(context.TODO(), transformerDeploymentKey, actualTransformerDeployment) + }, timeout). + Should(Succeed()) + expectedTransformerDeployment := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: transformerDeploymentKey.Name, + Namespace: transformerDeploymentKey.Namespace, + }, + Spec: appsv1.DeploymentSpec{ + Replicas: &replicas, + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "isvc." + transformerDeploymentKey.Name, + }, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Name: transformerDeploymentKey.Name, + Namespace: "default", + Labels: map[string]string{ + "app": "isvc." + transformerDeploymentKey.Name, + constants.KServiceComponentLabel: constants.Transformer.String(), + constants.InferenceServicePodLabelKey: serviceName, + }, + Annotations: map[string]string{ + "serving.kserve.io/deploymentMode": "RawDeployment", + "serving.kserve.io/autoscalerClass": "hpa", + "serving.kserve.io/metrics": "cpu", + "serving.kserve.io/targetUtilizationPercentage": "75", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Image: "transformer:v1", + Name: constants.InferenceServiceContainerName, + Args: []string{ + "--port=8080", + "--model_name", + serviceKey.Name, + "--predictor_host", + fmt.Sprintf("%s.%s", predictorServiceKey.Name, predictorServiceKey.Namespace), + "--http_port", + "8080", + }, + Resources: defaultResource, + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + TCPSocket: &corev1.TCPSocketAction{ + Port: intstr.IntOrString{ + IntVal: 8080, + }, + }, + }, + InitialDelaySeconds: 0, + TimeoutSeconds: 1, + PeriodSeconds: 10, + SuccessThreshold: 1, + FailureThreshold: 3, + }, + TerminationMessagePath: "/dev/termination-log", + TerminationMessagePolicy: "File", + ImagePullPolicy: "IfNotPresent", + }, + }, + SchedulerName: "default-scheduler", + RestartPolicy: "Always", + TerminationGracePeriodSeconds: &gracePeriod, + DNSPolicy: "ClusterFirst", + SecurityContext: &corev1.PodSecurityContext{ + SELinuxOptions: nil, + WindowsOptions: nil, + RunAsUser: nil, + RunAsGroup: nil, + RunAsNonRoot: nil, + SupplementalGroups: nil, + FSGroup: nil, + Sysctls: nil, + FSGroupChangePolicy: nil, + SeccompProfile: nil, + }, + AutomountServiceAccountToken: proto.Bool(false), + }, + }, + Strategy: appsv1.DeploymentStrategy{ + Type: "RollingUpdate", + RollingUpdate: &appsv1.RollingUpdateDeployment{ + MaxUnavailable: &intstr.IntOrString{Type: 1, IntVal: 0, StrVal: "25%"}, + MaxSurge: &intstr.IntOrString{Type: 1, IntVal: 0, StrVal: "25%"}, + }, + }, + RevisionHistoryLimit: &revisionHistory, + ProgressDeadlineSeconds: &progressDeadlineSeconds, + }, + } + Expect(actualTransformerDeployment.Spec).To(BeComparableTo(expectedTransformerDeployment.Spec)) + + // check predictor service + actualPredictorService := &corev1.Service{} + Eventually(func() error { return k8sClient.Get(context.TODO(), predictorServiceKey, actualPredictorService) }, timeout). + Should(Succeed()) + expectedPredictorService := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: predictorServiceKey.Name, + Namespace: predictorServiceKey.Namespace, + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Name: predictorServiceKey.Name, + Protocol: "TCP", + Port: 80, + TargetPort: intstr.IntOrString{Type: 0, IntVal: 8080, StrVal: ""}, + }, + }, + Type: "ClusterIP", + SessionAffinity: "None", + Selector: map[string]string{ + "app": "isvc." + predictorServiceKey.Name, + }, + }, + } + actualPredictorService.Spec.ClusterIP = "" + actualPredictorService.Spec.ClusterIPs = nil + actualPredictorService.Spec.IPFamilies = nil + actualPredictorService.Spec.IPFamilyPolicy = nil + actualPredictorService.Spec.InternalTrafficPolicy = nil + Expect(actualPredictorService.Spec).To(BeComparableTo(expectedPredictorService.Spec)) + + // check transformer service + actualTransformerService := &corev1.Service{} + Eventually(func() error { return k8sClient.Get(context.TODO(), transformerServiceKey, actualTransformerService) }, timeout). + Should(Succeed()) + expectedTransformerService := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: transformerServiceKey.Name, + Namespace: transformerServiceKey.Namespace, + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Name: transformerServiceKey.Name, + Protocol: "TCP", + Port: 80, + TargetPort: intstr.IntOrString{Type: 0, IntVal: 8080, StrVal: ""}, + }, + }, + Type: "ClusterIP", + SessionAffinity: "None", + Selector: map[string]string{ + "app": "isvc." + transformerServiceKey.Name, + }, + }, + } + actualTransformerService.Spec.ClusterIP = "" + actualTransformerService.Spec.ClusterIPs = nil + actualTransformerService.Spec.IPFamilies = nil + actualTransformerService.Spec.IPFamilyPolicy = nil + actualTransformerService.Spec.InternalTrafficPolicy = nil + Expect(actualTransformerService.Spec).To(BeComparableTo(expectedTransformerService.Spec)) + + // update deployment status to make isvc ready + updatedPredictorDeployment := actualPredictorDeployment.DeepCopy() + updatedPredictorDeployment.Status.Conditions = []appsv1.DeploymentCondition{ + { + Type: appsv1.DeploymentAvailable, + Status: corev1.ConditionTrue, + }, + } + Expect(k8sClient.Status().Update(context.TODO(), updatedPredictorDeployment)).NotTo(HaveOccurred()) + updatedTransformerDeployment := actualTransformerDeployment.DeepCopy() + updatedTransformerDeployment.Status.Conditions = []appsv1.DeploymentCondition{ + { + Type: appsv1.DeploymentAvailable, + Status: corev1.ConditionTrue, + }, + } + Expect(k8sClient.Status().Update(context.TODO(), updatedTransformerDeployment)).NotTo(HaveOccurred()) + + // check http route + actualToplevelHttpRoute := &gatewayapiv1.HTTPRoute{} + Eventually(func() error { + return k8sClient.Get(context.TODO(), types.NamespacedName{ + Name: serviceKey.Name, + Namespace: serviceKey.Namespace, + }, actualToplevelHttpRoute) + }, timeout). + Should(Succeed()) + topLevelHost := fmt.Sprintf("%s-%s.%s", serviceKey.Name, serviceKey.Namespace, "example.com") + expectedToplevelHttpRoute := gatewayapiv1.HTTPRoute{ + Spec: gatewayapiv1.HTTPRouteSpec{ + Hostnames: []gatewayapiv1.Hostname{gatewayapiv1.Hostname(topLevelHost), "additional.example.com"}, + Rules: []gatewayapiv1.HTTPRouteRule{ + { + Matches: []gatewayapiv1.HTTPRouteMatch{ + { + Path: &gatewayapiv1.HTTPPathMatch{ + Type: ptr.To(gatewayapiv1.PathMatchRegularExpression), + Value: ptr.To(constants.FallbackPrefix()), + }, + }, + }, + Filters: []gatewayapiv1.HTTPRouteFilter{ + { + Type: gatewayapiv1.HTTPRouteFilterRequestHeaderModifier, + RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ + Set: []gatewayapiv1.HTTPHeader{ + { + Name: constants.IsvcNameHeader, + Value: serviceKey.Name, + }, + { + Name: constants.IsvcNamespaceHeader, + Value: serviceKey.Namespace, + }, + }, + }, + }, + }, + BackendRefs: []gatewayapiv1.HTTPBackendRef{ + { + BackendRef: gatewayapiv1.BackendRef{ + BackendObjectReference: gatewayapiv1.BackendObjectReference{ + Group: (*gatewayapiv1.Group)(ptr.To("")), + Kind: ptr.To(gatewayapiv1.Kind(constants.ServiceKind)), + Name: gatewayapiv1.ObjectName(transformerServiceKey.Name), + Namespace: (*gatewayapiv1.Namespace)(ptr.To(serviceKey.Namespace)), + Port: (*gatewayapiv1.PortNumber)(ptr.To(int32(constants.CommonDefaultHttpPort))), + }, + Weight: ptr.To(int32(1)), + }, + }, + }, + Timeouts: &gatewayapiv1.HTTPRouteTimeouts{ + Request: ptr.To(gatewayapiv1.Duration("30s")), + }, + }, + }, + CommonRouteSpec: gatewayapiv1.CommonRouteSpec{ + ParentRefs: []gatewayapiv1.ParentReference{ + { + Name: gatewayapiv1.ObjectName(kserveGateway.Name), + Kind: ptr.To(gatewayapiv1.Kind(constants.GatewayKind)), + Group: (*gatewayapiv1.Group)(&gatewayapiv1.GroupVersion.Group), + Namespace: ptr.To(gatewayapiv1.Namespace(kserveGateway.Namespace)), + }, + }, + }, + }, + } + Expect(actualToplevelHttpRoute.Spec).To(BeComparableTo(expectedToplevelHttpRoute.Spec)) + + actualPredictorHttpRoute := &gatewayapiv1.HTTPRoute{} + Eventually(func() error { + return k8sClient.Get(context.TODO(), types.NamespacedName{ + Name: predictorServiceKey.Name, + Namespace: serviceKey.Namespace, + }, actualPredictorHttpRoute) + }, timeout). + Should(Succeed()) + predictorHost := fmt.Sprintf("%s-%s.%s", predictorServiceKey.Name, serviceKey.Namespace, "example.com") + expectedPredictorHttpRoute := gatewayapiv1.HTTPRoute{ + Spec: gatewayapiv1.HTTPRouteSpec{ + Hostnames: []gatewayapiv1.Hostname{gatewayapiv1.Hostname(predictorHost)}, + Rules: []gatewayapiv1.HTTPRouteRule{ + { + Matches: []gatewayapiv1.HTTPRouteMatch{ + { + Path: &gatewayapiv1.HTTPPathMatch{ + Type: ptr.To(gatewayapiv1.PathMatchRegularExpression), + Value: ptr.To(constants.FallbackPrefix()), + }, + }, + }, + Filters: []gatewayapiv1.HTTPRouteFilter{ + { + Type: gatewayapiv1.HTTPRouteFilterRequestHeaderModifier, + RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ + Set: []gatewayapiv1.HTTPHeader{ + { + Name: constants.IsvcNameHeader, + Value: serviceKey.Name, + }, + { + Name: constants.IsvcNamespaceHeader, + Value: serviceKey.Namespace, + }, + }, + }, + }, + }, + BackendRefs: []gatewayapiv1.HTTPBackendRef{ + { + BackendRef: gatewayapiv1.BackendRef{ + BackendObjectReference: gatewayapiv1.BackendObjectReference{ + Group: (*gatewayapiv1.Group)(ptr.To("")), + Kind: ptr.To(gatewayapiv1.Kind(constants.ServiceKind)), + Name: gatewayapiv1.ObjectName(predictorServiceKey.Name), + Namespace: (*gatewayapiv1.Namespace)(ptr.To(serviceKey.Namespace)), + Port: (*gatewayapiv1.PortNumber)(ptr.To(int32(constants.CommonDefaultHttpPort))), + }, + Weight: ptr.To(int32(1)), + }, + }, + }, + Timeouts: &gatewayapiv1.HTTPRouteTimeouts{ + Request: ptr.To(gatewayapiv1.Duration("60s")), + }, + }, + }, + CommonRouteSpec: gatewayapiv1.CommonRouteSpec{ + ParentRefs: []gatewayapiv1.ParentReference{ + { + Name: gatewayapiv1.ObjectName(kserveGateway.Name), + Kind: ptr.To(gatewayapiv1.Kind(constants.GatewayKind)), + Group: (*gatewayapiv1.Group)(&gatewayapiv1.GroupVersion.Group), + Namespace: ptr.To(gatewayapiv1.Namespace(kserveGateway.Namespace)), + }, + }, + }, + }, + } + Expect(actualPredictorHttpRoute.Spec).To(BeComparableTo(expectedPredictorHttpRoute.Spec)) + + actualTransformerHttpRoute := &gatewayapiv1.HTTPRoute{} + Eventually(func() error { + return k8sClient.Get(context.TODO(), types.NamespacedName{ + Name: transformerServiceKey.Name, + Namespace: serviceKey.Namespace, + }, actualTransformerHttpRoute) + }, timeout). + Should(Succeed()) + transformerHost := fmt.Sprintf("%s-%s.%s", transformerServiceKey.Name, serviceKey.Namespace, "example.com") + expectedTransformerHttpRoute := gatewayapiv1.HTTPRoute{ + Spec: gatewayapiv1.HTTPRouteSpec{ + Hostnames: []gatewayapiv1.Hostname{gatewayapiv1.Hostname(transformerHost)}, + Rules: []gatewayapiv1.HTTPRouteRule{ + { + Matches: []gatewayapiv1.HTTPRouteMatch{ + { + Path: &gatewayapiv1.HTTPPathMatch{ + Type: ptr.To(gatewayapiv1.PathMatchRegularExpression), + Value: ptr.To(constants.FallbackPrefix()), + }, + }, + }, + Filters: []gatewayapiv1.HTTPRouteFilter{ + { + Type: gatewayapiv1.HTTPRouteFilterRequestHeaderModifier, + RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ + Set: []gatewayapiv1.HTTPHeader{ + { + Name: constants.IsvcNameHeader, + Value: serviceKey.Name, + }, + { + Name: constants.IsvcNamespaceHeader, + Value: serviceKey.Namespace, + }, + }, + }, + }, + }, + BackendRefs: []gatewayapiv1.HTTPBackendRef{ + { + BackendRef: gatewayapiv1.BackendRef{ + BackendObjectReference: gatewayapiv1.BackendObjectReference{ + Group: (*gatewayapiv1.Group)(ptr.To("")), + Kind: ptr.To(gatewayapiv1.Kind(constants.ServiceKind)), + Name: gatewayapiv1.ObjectName(transformerServiceKey.Name), + Namespace: (*gatewayapiv1.Namespace)(ptr.To(serviceKey.Namespace)), + Port: (*gatewayapiv1.PortNumber)(ptr.To(int32(constants.CommonDefaultHttpPort))), + }, + Weight: ptr.To(int32(1)), + }, + }, + }, + Timeouts: &gatewayapiv1.HTTPRouteTimeouts{ + Request: ptr.To(gatewayapiv1.Duration("30s")), + }, + }, + }, + CommonRouteSpec: gatewayapiv1.CommonRouteSpec{ + ParentRefs: []gatewayapiv1.ParentReference{ + { + Name: gatewayapiv1.ObjectName(kserveGateway.Name), + Kind: ptr.To(gatewayapiv1.Kind(constants.GatewayKind)), + Group: (*gatewayapiv1.Group)(&gatewayapiv1.GroupVersion.Group), + Namespace: ptr.To(gatewayapiv1.Namespace(kserveGateway.Namespace)), + }, + }, + }, + }, + } + Expect(actualTransformerHttpRoute.Spec).To(BeComparableTo(expectedTransformerHttpRoute.Spec)) + + // Mark the Ingress as accepted to make isvc ready + httpRouteStatus := gatewayapiv1.HTTPRouteStatus{ + RouteStatus: gatewayapiv1.RouteStatus{ + Parents: []gatewayapiv1.RouteParentStatus{ + { + ParentRef: gatewayapiv1.ParentReference{ + Name: gatewayapiv1.ObjectName(kserveGateway.Name), + Kind: ptr.To(gatewayapiv1.Kind(constants.GatewayKind)), + Group: (*gatewayapiv1.Group)(&gatewayapiv1.GroupVersion.Group), + Namespace: ptr.To(gatewayapiv1.Namespace(kserveGateway.Namespace)), + }, + ControllerName: "istio.io/gateway-controller", + Conditions: []metav1.Condition{ + { + Type: string(gatewayapiv1.ListenerConditionAccepted), + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "Route was valid", + LastTransitionTime: metav1.Now(), + }, + }, + }, + }, + }, + } + actualPredictorHttpRoute.Status = httpRouteStatus + Expect(k8sClient.Status().Update(context.Background(), actualPredictorHttpRoute)).NotTo(HaveOccurred()) + actualTransformerHttpRoute.Status = httpRouteStatus + Expect(k8sClient.Status().Update(context.Background(), actualTransformerHttpRoute)).NotTo(HaveOccurred()) + actualToplevelHttpRoute.Status = httpRouteStatus + Expect(k8sClient.Status().Update(context.Background(), actualToplevelHttpRoute)).NotTo(HaveOccurred()) + + // verify if InferenceService status is updated + expectedIsvcStatus := v1beta1.InferenceServiceStatus{ + Status: duckv1.Status{ + Conditions: duckv1.Conditions{ + { + Type: v1beta1.IngressReady, + Status: "True", + }, + { + Type: v1beta1.PredictorReady, + Status: "True", + }, + { + Type: apis.ConditionReady, + Status: "True", + }, + { + Type: v1beta1.TransformerReady, + Status: "True", + Severity: "Info", + }, + }, + }, + URL: &apis.URL{ + Scheme: "http", + Host: fmt.Sprintf("%s-%s.example.com", serviceKey.Name, serviceKey.Namespace), + }, + Address: &duckv1.Addressable{ + URL: &apis.URL{ + Scheme: "http", + Host: fmt.Sprintf("%s.%s.svc.cluster.local", transformerServiceKey.Name, serviceKey.Namespace), + }, + }, + Components: map[v1beta1.ComponentType]v1beta1.ComponentStatusSpec{ + v1beta1.PredictorComponent: { + LatestCreatedRevision: "", + URL: &apis.URL{ + Scheme: "http", + Host: fmt.Sprintf("%s-%s.example.com", predictorServiceKey.Name, serviceKey.Namespace), + }, + }, + v1beta1.TransformerComponent: { + LatestCreatedRevision: "", + URL: &apis.URL{ + Scheme: "http", + Host: fmt.Sprintf("%s-%s.example.com", transformerServiceKey.Name, serviceKey.Namespace), + }, + }, + }, + ModelStatus: v1beta1.ModelStatus{ + TransitionStatus: "InProgress", + ModelRevisionStates: &v1beta1.ModelRevisionStates{TargetModelState: "Pending"}, + }, + } + Eventually(func() string { + isvc := &v1beta1.InferenceService{} + if err := k8sClient.Get(context.TODO(), serviceKey, isvc); err != nil { + return err.Error() + } + return cmp.Diff(&expectedIsvcStatus, &isvc.Status, cmpopts.IgnoreTypes(apis.VolatileTime{})) + }, timeout).Should(BeEmpty()) + + // check predictor HPA + var cpuUtilization int32 = 75 + var stabilizationWindowSeconds int32 = 0 + selectPolicy := autoscalingv2.MaxChangePolicySelect + actualPredictorHPA := &autoscalingv2.HorizontalPodAutoscaler{} + predictorHPAKey := types.NamespacedName{ + Name: predictorServiceKey.Name, + Namespace: serviceKey.Namespace, + } + Eventually(func() error { return k8sClient.Get(context.TODO(), predictorHPAKey, actualPredictorHPA) }, timeout). + Should(Succeed()) + expectedPredictorHPA := &autoscalingv2.HorizontalPodAutoscaler{ + Spec: autoscalingv2.HorizontalPodAutoscalerSpec{ + ScaleTargetRef: autoscalingv2.CrossVersionObjectReference{ + APIVersion: "apps/v1", + Kind: "Deployment", + Name: predictorServiceKey.Name, + }, + MinReplicas: &minReplicas, + MaxReplicas: maxReplicas, + Metrics: []autoscalingv2.MetricSpec{ + { + Type: autoscalingv2.ResourceMetricSourceType, + Resource: &autoscalingv2.ResourceMetricSource{ + Name: corev1.ResourceCPU, + Target: autoscalingv2.MetricTarget{ + Type: "Utilization", + AverageUtilization: &cpuUtilization, + }, + }, + }, + }, + Behavior: &autoscalingv2.HorizontalPodAutoscalerBehavior{ + ScaleUp: &autoscalingv2.HPAScalingRules{ + StabilizationWindowSeconds: &stabilizationWindowSeconds, + SelectPolicy: &selectPolicy, + Policies: []autoscalingv2.HPAScalingPolicy{ + { + Type: "Pods", + Value: 4, + PeriodSeconds: 15, + }, + { + Type: "Percent", + Value: 100, + PeriodSeconds: 15, + }, + }, + }, + ScaleDown: &autoscalingv2.HPAScalingRules{ + StabilizationWindowSeconds: nil, + SelectPolicy: &selectPolicy, + Policies: []autoscalingv2.HPAScalingPolicy{ + { + Type: "Percent", + Value: 100, + PeriodSeconds: 15, + }, + }, + }, + }, + }, + } + Expect(actualPredictorHPA.Spec).To(BeComparableTo(expectedPredictorHPA.Spec)) + + // check transformer HPA + actualTransformerHPA := &autoscalingv2.HorizontalPodAutoscaler{} + transformerHPAKey := types.NamespacedName{ + Name: transformerServiceKey.Name, + Namespace: serviceKey.Namespace, + } + Eventually(func() error { return k8sClient.Get(context.TODO(), transformerHPAKey, actualTransformerHPA) }, timeout). + Should(Succeed()) + expectedTransformerHPA := &autoscalingv2.HorizontalPodAutoscaler{ + Spec: autoscalingv2.HorizontalPodAutoscalerSpec{ + ScaleTargetRef: autoscalingv2.CrossVersionObjectReference{ + APIVersion: "apps/v1", + Kind: "Deployment", + Name: transformerServiceKey.Name, + }, + MinReplicas: &transformerMinReplicas, + MaxReplicas: transformerMaxReplicas, + Metrics: []autoscalingv2.MetricSpec{ + { + Type: autoscalingv2.ResourceMetricSourceType, + Resource: &autoscalingv2.ResourceMetricSource{ + Name: corev1.ResourceCPU, + Target: autoscalingv2.MetricTarget{ + Type: "Utilization", + AverageUtilization: &transformerCpuUtilization, + }, + }, + }, + }, + Behavior: &autoscalingv2.HorizontalPodAutoscalerBehavior{ + ScaleUp: &autoscalingv2.HPAScalingRules{ + StabilizationWindowSeconds: &transformerStabilizationWindowSeconds, + SelectPolicy: &transformerSelectPolicy, + Policies: []autoscalingv2.HPAScalingPolicy{ + { + Type: "Pods", + Value: 4, + PeriodSeconds: 15, + }, + { + Type: "Percent", + Value: 100, + PeriodSeconds: 15, + }, + }, + }, + ScaleDown: &autoscalingv2.HPAScalingRules{ + StabilizationWindowSeconds: nil, + SelectPolicy: &transformerSelectPolicy, + Policies: []autoscalingv2.HPAScalingPolicy{ + { + Type: "Percent", + Value: 100, + PeriodSeconds: 15, + }, + }, + }, + }, + }, + } + Expect(actualTransformerHPA.Spec).To(BeComparableTo(expectedTransformerHPA.Spec)) + }) + }) + Context("When creating inference service with raw kube predictor and explainer", func() { + configs := map[string]string{ + "explainers": `{ + "art": { + "image": "kserve/art-explainer", + "defaultImageVersion": "latest" + } + }`, + "ingress": `{ + "enableGatewayApi": true, + "kserveIngressGateway": "kserve/kserve-ingress-gateway", + "ingressGateway": "knative-serving/knative-ingress-gateway", + "localGateway": "knative-serving/knative-local-gateway", + "localGatewayService": "knative-local-gateway.istio-system.svc.cluster.local", + "additionalIngressDomains": ["additional.example.com"] + }`, + "storageInitializer": `{ + "image" : "kserve/storage-initializer:latest", + "memoryRequest": "100Mi", + "memoryLimit": "1Gi", + "cpuRequest": "100m", + "cpuLimit": "1", + "CaBundleConfigMapName": "", + "caBundleVolumeMountPath": "/etc/ssl/custom-certs", + "enableDirectPvcVolumeMount": false + }`, + } + + It("Should have httproute/service/deployment/hpa created for explainer and predictor", func() { + By("By creating a new InferenceService") + // Create configmap + configMap := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: constants.InferenceServiceConfigMapName, + Namespace: constants.KServeNamespace, + }, + Data: configs, + } + Expect(k8sClient.Create(context.TODO(), configMap)).NotTo(HaveOccurred()) + defer k8sClient.Delete(context.TODO(), configMap) + // Create ServingRuntime + servingRuntime := &v1alpha1.ServingRuntime{ + ObjectMeta: metav1.ObjectMeta{ + Name: "tf-serving-raw", + Namespace: "default", + }, + Spec: v1alpha1.ServingRuntimeSpec{ + SupportedModelFormats: []v1alpha1.SupportedModelFormat{ + { + Name: "tensorflow", + Version: proto.String("1"), + AutoSelect: proto.Bool(true), + }, + }, + ServingRuntimePodSpec: v1alpha1.ServingRuntimePodSpec{ + Containers: []corev1.Container{ + { + Name: "kserve-container", + Image: "tensorflow/serving:1.14.0", + Command: []string{"/usr/bin/tensorflow_model_server"}, + Args: []string{ + "--port=9000", + "--rest_api_port=8080", + "--model_base_path=/mnt/models", + "--rest_api_timeout_in_ms=60000", + }, + Resources: defaultResource, + }, + }, + }, + Disabled: proto.Bool(false), + }, + } + k8sClient.Create(context.TODO(), servingRuntime) + defer k8sClient.Delete(context.TODO(), servingRuntime) + serviceName := "raw-foo-exp" + namespace := "default" + expectedRequest := reconcile.Request{NamespacedName: types.NamespacedName{Name: serviceName, Namespace: namespace}} + serviceKey := expectedRequest.NamespacedName + predictorServiceKey := types.NamespacedName{ + Name: constants.PredictorServiceName(serviceName), + Namespace: namespace, + } + explainerServiceKey := types.NamespacedName{ + Name: constants.ExplainerServiceName(serviceName), + Namespace: namespace, + } + storageUri := "s3://test/mnist/export" + var minReplicas int32 = 1 + var maxReplicas int32 = 3 + var explainerMinReplicas int32 = 1 + var explainerMaxReplicas int32 = 2 + var explainerCpuUtilization int32 = 80 + var explainerStabilizationWindowSeconds int32 = 0 + ExplainerSelectPolicy := autoscalingv2.MaxChangePolicySelect + ctx := context.Background() + isvc := &v1beta1.InferenceService{ + ObjectMeta: metav1.ObjectMeta{ + Name: serviceKey.Name, + Namespace: serviceKey.Namespace, + Annotations: map[string]string{ + "serving.kserve.io/deploymentMode": "RawDeployment", + "serving.kserve.io/autoscalerClass": "hpa", + "serving.kserve.io/metrics": "cpu", + "serving.kserve.io/targetUtilizationPercentage": "75", + }, + }, + Spec: v1beta1.InferenceServiceSpec{ + Predictor: v1beta1.PredictorSpec{ + ComponentExtensionSpec: v1beta1.ComponentExtensionSpec{ + MinReplicas: ptr.To(minReplicas), + MaxReplicas: maxReplicas, + }, + Tensorflow: &v1beta1.TFServingSpec{ + PredictorExtensionSpec: v1beta1.PredictorExtensionSpec{ + StorageURI: &storageUri, + RuntimeVersion: proto.String("1.14.0"), + Container: corev1.Container{ + Name: constants.InferenceServiceContainerName, + Resources: defaultResource, + }, + }, + }, + }, + Explainer: &v1beta1.ExplainerSpec{ + ART: &v1beta1.ARTExplainerSpec{ + Type: v1beta1.ARTSquareAttackExplainer, + ExplainerExtensionSpec: v1beta1.ExplainerExtensionSpec{ + Config: map[string]string{"nb_classes": "10"}, + Container: corev1.Container{ + Name: constants.InferenceServiceContainerName, + Resources: defaultResource, + }, + }, + }, + ComponentExtensionSpec: v1beta1.ComponentExtensionSpec{ + MinReplicas: ptr.To(explainerMinReplicas), + MaxReplicas: explainerMaxReplicas, + ScaleTarget: ptr.To(explainerCpuUtilization), + TimeoutSeconds: ptr.To(int64(30)), + }, + }, + }, + } + isvcConfigMap, err := v1beta1.GetInferenceServiceConfigMap(context.Background(), clientset) + Expect(err).NotTo(HaveOccurred()) + isvcConfig, err := v1beta1.NewInferenceServicesConfig(isvcConfigMap) + Expect(err).NotTo(HaveOccurred()) + isvc.DefaultInferenceService(isvcConfig, nil, &v1beta1.SecurityConfig{AutoMountServiceAccountToken: false}, nil) + Expect(k8sClient.Create(ctx, isvc)).Should(Succeed()) + defer k8sClient.Delete(ctx, isvc) + + inferenceService := &v1beta1.InferenceService{} + + Eventually(func() bool { + err := k8sClient.Get(ctx, serviceKey, inferenceService) + return err == nil + }, timeout, interval).Should(BeTrue()) + + // check predictor deployment + actualPredictorDeployment := &appsv1.Deployment{} + predictorDeploymentKey := types.NamespacedName{ + Name: predictorServiceKey.Name, + Namespace: serviceKey.Namespace, + } + Eventually(func() error { return k8sClient.Get(context.TODO(), predictorDeploymentKey, actualPredictorDeployment) }, timeout). + Should(Succeed()) + var replicas int32 = 1 + var revisionHistory int32 = 10 + var progressDeadlineSeconds int32 = 600 + var gracePeriod int64 = 30 + expectedPredictorDeployment := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: predictorDeploymentKey.Name, + Namespace: predictorDeploymentKey.Namespace, + }, + Spec: appsv1.DeploymentSpec{ + Replicas: &replicas, + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "isvc." + predictorDeploymentKey.Name, + }, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Name: predictorDeploymentKey.Name, + Namespace: "default", + Labels: map[string]string{ + "app": "isvc." + predictorDeploymentKey.Name, + constants.KServiceComponentLabel: constants.Predictor.String(), + constants.InferenceServicePodLabelKey: serviceName, + }, + Annotations: map[string]string{ + constants.StorageInitializerSourceUriInternalAnnotationKey: *isvc.Spec.Predictor.Model.StorageURI, + "serving.kserve.io/deploymentMode": "RawDeployment", + "serving.kserve.io/autoscalerClass": "hpa", + "serving.kserve.io/metrics": "cpu", + "serving.kserve.io/targetUtilizationPercentage": "75", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Image: "tensorflow/serving:" + + *isvc.Spec.Predictor.Model.RuntimeVersion, + Name: constants.InferenceServiceContainerName, + Command: []string{v1beta1.TensorflowEntrypointCommand}, + Args: []string{ + "--port=" + v1beta1.TensorflowServingGRPCPort, + "--rest_api_port=" + v1beta1.TensorflowServingRestPort, + "--model_base_path=" + constants.DefaultModelLocalMountPath, + "--rest_api_timeout_in_ms=60000", + }, + Resources: defaultResource, + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + TCPSocket: &corev1.TCPSocketAction{ + Port: intstr.IntOrString{ + IntVal: 8080, + }, + }, + }, + InitialDelaySeconds: 0, + TimeoutSeconds: 1, + PeriodSeconds: 10, + SuccessThreshold: 1, + FailureThreshold: 3, + }, + TerminationMessagePath: "/dev/termination-log", + TerminationMessagePolicy: "File", + ImagePullPolicy: "IfNotPresent", + }, + }, + SchedulerName: "default-scheduler", + RestartPolicy: "Always", + TerminationGracePeriodSeconds: &gracePeriod, + DNSPolicy: "ClusterFirst", + SecurityContext: &corev1.PodSecurityContext{ + SELinuxOptions: nil, + WindowsOptions: nil, + RunAsUser: nil, + RunAsGroup: nil, + RunAsNonRoot: nil, + SupplementalGroups: nil, + FSGroup: nil, + Sysctls: nil, + FSGroupChangePolicy: nil, + SeccompProfile: nil, + }, + AutomountServiceAccountToken: proto.Bool(false), + }, + }, + Strategy: appsv1.DeploymentStrategy{ + Type: "RollingUpdate", + RollingUpdate: &appsv1.RollingUpdateDeployment{ + MaxUnavailable: &intstr.IntOrString{Type: 1, IntVal: 0, StrVal: "25%"}, + MaxSurge: &intstr.IntOrString{Type: 1, IntVal: 0, StrVal: "25%"}, + }, + }, + RevisionHistoryLimit: &revisionHistory, + ProgressDeadlineSeconds: &progressDeadlineSeconds, + }, + } + Expect(actualPredictorDeployment.Spec).To(BeComparableTo(expectedPredictorDeployment.Spec)) + + // check Explainer deployment + actualExplainerDeployment := &appsv1.Deployment{} + explainerDeploymentKey := types.NamespacedName{ + Name: explainerServiceKey.Name, + Namespace: serviceKey.Namespace, + } + Eventually(func() error { + return k8sClient.Get(context.TODO(), explainerDeploymentKey, actualExplainerDeployment) + }, timeout). + Should(Succeed()) + expectedExplainerDeployment := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: explainerDeploymentKey.Name, + Namespace: explainerDeploymentKey.Namespace, + }, + Spec: appsv1.DeploymentSpec{ + Replicas: &replicas, + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "isvc." + explainerDeploymentKey.Name, + }, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Name: explainerDeploymentKey.Name, + Namespace: "default", + Labels: map[string]string{ + "app": "isvc." + explainerDeploymentKey.Name, + constants.KServiceComponentLabel: constants.Explainer.String(), + constants.InferenceServicePodLabelKey: serviceName, + }, + Annotations: map[string]string{ + "serving.kserve.io/deploymentMode": "RawDeployment", + "serving.kserve.io/autoscalerClass": "hpa", + "serving.kserve.io/metrics": "cpu", + "serving.kserve.io/targetUtilizationPercentage": "75", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Image: "kserve/art-explainer:latest", + Name: constants.InferenceServiceContainerName, + Args: []string{ + "--model_name", + serviceKey.Name, + "--http_port", + "8080", + "--predictor_host", + fmt.Sprintf("%s.%s", predictorServiceKey.Name, predictorServiceKey.Namespace), + "--adversary_type", + "SquareAttack", + "--nb_classes", + "10", + }, + Resources: defaultResource, + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + TCPSocket: &corev1.TCPSocketAction{ + Port: intstr.IntOrString{ + IntVal: 8080, + }, + }, + }, + InitialDelaySeconds: 0, + TimeoutSeconds: 1, + PeriodSeconds: 10, + SuccessThreshold: 1, + FailureThreshold: 3, + }, + TerminationMessagePath: "/dev/termination-log", + TerminationMessagePolicy: "File", + ImagePullPolicy: "IfNotPresent", + }, + }, + SchedulerName: "default-scheduler", + RestartPolicy: "Always", + TerminationGracePeriodSeconds: &gracePeriod, + DNSPolicy: "ClusterFirst", + SecurityContext: &corev1.PodSecurityContext{ + SELinuxOptions: nil, + WindowsOptions: nil, + RunAsUser: nil, + RunAsGroup: nil, + RunAsNonRoot: nil, + SupplementalGroups: nil, + FSGroup: nil, + Sysctls: nil, + FSGroupChangePolicy: nil, + SeccompProfile: nil, + }, + AutomountServiceAccountToken: proto.Bool(false), + }, + }, + Strategy: appsv1.DeploymentStrategy{ + Type: "RollingUpdate", + RollingUpdate: &appsv1.RollingUpdateDeployment{ + MaxUnavailable: &intstr.IntOrString{Type: 1, IntVal: 0, StrVal: "25%"}, + MaxSurge: &intstr.IntOrString{Type: 1, IntVal: 0, StrVal: "25%"}, + }, + }, + RevisionHistoryLimit: &revisionHistory, + ProgressDeadlineSeconds: &progressDeadlineSeconds, + }, + } + Expect(actualExplainerDeployment.Spec).To(BeComparableTo(expectedExplainerDeployment.Spec)) + + // check predictor service + actualPredictorService := &corev1.Service{} + Eventually(func() error { return k8sClient.Get(context.TODO(), predictorServiceKey, actualPredictorService) }, timeout). + Should(Succeed()) + expectedPredictorService := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: predictorServiceKey.Name, + Namespace: predictorServiceKey.Namespace, + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Name: predictorServiceKey.Name, + Protocol: "TCP", + Port: 80, + TargetPort: intstr.IntOrString{Type: 0, IntVal: 8080, StrVal: ""}, + }, + }, + Type: "ClusterIP", + SessionAffinity: "None", + Selector: map[string]string{ + "app": "isvc." + predictorServiceKey.Name, + }, + }, + } + actualPredictorService.Spec.ClusterIP = "" + actualPredictorService.Spec.ClusterIPs = nil + actualPredictorService.Spec.IPFamilies = nil + actualPredictorService.Spec.IPFamilyPolicy = nil + actualPredictorService.Spec.InternalTrafficPolicy = nil + Expect(actualPredictorService.Spec).To(BeComparableTo(expectedPredictorService.Spec)) + + // check Explainer service + actualExplainerService := &corev1.Service{} + Eventually(func() error { return k8sClient.Get(context.TODO(), explainerServiceKey, actualExplainerService) }, timeout). + Should(Succeed()) + expectedExplainerService := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: explainerServiceKey.Name, + Namespace: explainerServiceKey.Namespace, + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Name: explainerServiceKey.Name, + Protocol: "TCP", + Port: 80, + TargetPort: intstr.IntOrString{Type: 0, IntVal: 8080, StrVal: ""}, + }, + }, + Type: "ClusterIP", + SessionAffinity: "None", + Selector: map[string]string{ + "app": "isvc." + explainerServiceKey.Name, + }, + }, + } + actualExplainerService.Spec.ClusterIP = "" + actualExplainerService.Spec.ClusterIPs = nil + actualExplainerService.Spec.IPFamilies = nil + actualExplainerService.Spec.IPFamilyPolicy = nil + actualExplainerService.Spec.InternalTrafficPolicy = nil + Expect(actualExplainerService.Spec).To(BeComparableTo(expectedExplainerService.Spec)) + + // update deployment status to make isvc ready + updatedPredictorDeployment := actualPredictorDeployment.DeepCopy() + updatedPredictorDeployment.Status.Conditions = []appsv1.DeploymentCondition{ + { + Type: appsv1.DeploymentAvailable, + Status: corev1.ConditionTrue, + }, + } + Expect(k8sClient.Status().Update(context.TODO(), updatedPredictorDeployment)).NotTo(HaveOccurred()) + updatedExplainerDeployment := actualExplainerDeployment.DeepCopy() + updatedExplainerDeployment.Status.Conditions = []appsv1.DeploymentCondition{ + { + Type: appsv1.DeploymentAvailable, + Status: corev1.ConditionTrue, + }, + } + Expect(k8sClient.Status().Update(context.TODO(), updatedExplainerDeployment)).NotTo(HaveOccurred()) + + // check http route + actualToplevelHttpRoute := &gatewayapiv1.HTTPRoute{} + Eventually(func() error { + return k8sClient.Get(context.TODO(), types.NamespacedName{ + Name: serviceKey.Name, + Namespace: serviceKey.Namespace, + }, actualToplevelHttpRoute) + }, timeout).Should(Succeed()) + topLevelHost := fmt.Sprintf("%s-%s.%s", serviceKey.Name, serviceKey.Namespace, "example.com") + expectedToplevelHttpRoute := gatewayapiv1.HTTPRoute{ + Spec: gatewayapiv1.HTTPRouteSpec{ + Hostnames: []gatewayapiv1.Hostname{gatewayapiv1.Hostname(topLevelHost), "additional.example.com"}, + Rules: []gatewayapiv1.HTTPRouteRule{ + { + Matches: []gatewayapiv1.HTTPRouteMatch{ + { + Path: &gatewayapiv1.HTTPPathMatch{ + Type: ptr.To(gatewayapiv1.PathMatchRegularExpression), + Value: ptr.To(constants.ExplainPrefix()), + }, + }, + }, + Filters: []gatewayapiv1.HTTPRouteFilter{ + { + Type: gatewayapiv1.HTTPRouteFilterRequestHeaderModifier, + RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ + Set: []gatewayapiv1.HTTPHeader{ + { + Name: constants.IsvcNameHeader, + Value: serviceKey.Name, + }, + { + Name: constants.IsvcNamespaceHeader, + Value: serviceKey.Namespace, + }, + }, + }, + }, + }, + BackendRefs: []gatewayapiv1.HTTPBackendRef{ + { + BackendRef: gatewayapiv1.BackendRef{ + BackendObjectReference: gatewayapiv1.BackendObjectReference{ + Group: (*gatewayapiv1.Group)(ptr.To("")), + Kind: ptr.To(gatewayapiv1.Kind(constants.ServiceKind)), + Name: gatewayapiv1.ObjectName(explainerServiceKey.Name), + Namespace: (*gatewayapiv1.Namespace)(ptr.To(serviceKey.Namespace)), + Port: (*gatewayapiv1.PortNumber)(ptr.To(int32(constants.CommonDefaultHttpPort))), + }, + Weight: ptr.To(int32(1)), + }, + }, + }, + Timeouts: &gatewayapiv1.HTTPRouteTimeouts{ + Request: ptr.To(gatewayapiv1.Duration("30s")), + }, + }, + { + Matches: []gatewayapiv1.HTTPRouteMatch{ + { + Path: &gatewayapiv1.HTTPPathMatch{ + Type: ptr.To(gatewayapiv1.PathMatchRegularExpression), + Value: ptr.To(constants.FallbackPrefix()), + }, + }, + }, + Filters: []gatewayapiv1.HTTPRouteFilter{ + { + Type: gatewayapiv1.HTTPRouteFilterRequestHeaderModifier, + RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ + Set: []gatewayapiv1.HTTPHeader{ + { + Name: constants.IsvcNameHeader, + Value: serviceKey.Name, + }, + { + Name: constants.IsvcNamespaceHeader, + Value: serviceKey.Namespace, + }, + }, + }, + }, + }, + BackendRefs: []gatewayapiv1.HTTPBackendRef{ + { + BackendRef: gatewayapiv1.BackendRef{ + BackendObjectReference: gatewayapiv1.BackendObjectReference{ + Group: (*gatewayapiv1.Group)(ptr.To("")), + Kind: ptr.To(gatewayapiv1.Kind(constants.ServiceKind)), + Name: gatewayapiv1.ObjectName(predictorServiceKey.Name), + Namespace: (*gatewayapiv1.Namespace)(ptr.To(serviceKey.Namespace)), + Port: (*gatewayapiv1.PortNumber)(ptr.To(int32(constants.CommonDefaultHttpPort))), + }, + Weight: ptr.To(int32(1)), + }, + }, + }, + Timeouts: &gatewayapiv1.HTTPRouteTimeouts{ + Request: ptr.To(gatewayapiv1.Duration("60s")), + }, + }, + }, + CommonRouteSpec: gatewayapiv1.CommonRouteSpec{ + ParentRefs: []gatewayapiv1.ParentReference{ + { + Name: gatewayapiv1.ObjectName(kserveGateway.Name), + Kind: ptr.To(gatewayapiv1.Kind(constants.GatewayKind)), + Group: (*gatewayapiv1.Group)(&gatewayapiv1.GroupVersion.Group), + Namespace: ptr.To(gatewayapiv1.Namespace(kserveGateway.Namespace)), + }, + }, + }, + }, + } + Expect(actualToplevelHttpRoute.Spec).To(BeComparableTo(expectedToplevelHttpRoute.Spec)) + + actualPredictorHttpRoute := &gatewayapiv1.HTTPRoute{} + Eventually(func() error { + return k8sClient.Get(context.TODO(), types.NamespacedName{ + Name: predictorServiceKey.Name, + Namespace: serviceKey.Namespace, + }, actualPredictorHttpRoute) + }, timeout). + Should(Succeed()) + predictorHost := fmt.Sprintf("%s-%s.%s", predictorServiceKey.Name, serviceKey.Namespace, "example.com") + expectedPredictorHttpRoute := gatewayapiv1.HTTPRoute{ + Spec: gatewayapiv1.HTTPRouteSpec{ + Hostnames: []gatewayapiv1.Hostname{gatewayapiv1.Hostname(predictorHost)}, + Rules: []gatewayapiv1.HTTPRouteRule{ + { + Matches: []gatewayapiv1.HTTPRouteMatch{ + { + Path: &gatewayapiv1.HTTPPathMatch{ + Type: ptr.To(gatewayapiv1.PathMatchRegularExpression), + Value: ptr.To(constants.FallbackPrefix()), + }, + }, + }, + Filters: []gatewayapiv1.HTTPRouteFilter{ + { + Type: gatewayapiv1.HTTPRouteFilterRequestHeaderModifier, + RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ + Set: []gatewayapiv1.HTTPHeader{ + { + Name: constants.IsvcNameHeader, + Value: serviceKey.Name, + }, + { + Name: constants.IsvcNamespaceHeader, + Value: serviceKey.Namespace, + }, + }, + }, + }, + }, + BackendRefs: []gatewayapiv1.HTTPBackendRef{ + { + BackendRef: gatewayapiv1.BackendRef{ + BackendObjectReference: gatewayapiv1.BackendObjectReference{ + Group: (*gatewayapiv1.Group)(ptr.To("")), + Kind: ptr.To(gatewayapiv1.Kind(constants.ServiceKind)), + Name: gatewayapiv1.ObjectName(predictorServiceKey.Name), + Namespace: (*gatewayapiv1.Namespace)(ptr.To(serviceKey.Namespace)), + Port: (*gatewayapiv1.PortNumber)(ptr.To(int32(constants.CommonDefaultHttpPort))), + }, + Weight: ptr.To(int32(1)), + }, + }, + }, + Timeouts: &gatewayapiv1.HTTPRouteTimeouts{ + Request: ptr.To(gatewayapiv1.Duration("60s")), + }, + }, + }, + CommonRouteSpec: gatewayapiv1.CommonRouteSpec{ + ParentRefs: []gatewayapiv1.ParentReference{ + { + Name: gatewayapiv1.ObjectName(kserveGateway.Name), + Kind: ptr.To(gatewayapiv1.Kind(constants.GatewayKind)), + Group: (*gatewayapiv1.Group)(&gatewayapiv1.GroupVersion.Group), + Namespace: ptr.To(gatewayapiv1.Namespace(kserveGateway.Namespace)), + }, + }, + }, + }, + } + Expect(actualPredictorHttpRoute.Spec).To(BeComparableTo(expectedPredictorHttpRoute.Spec)) + + actualExplainerHttpRoute := &gatewayapiv1.HTTPRoute{} + Eventually(func() error { + return k8sClient.Get(context.TODO(), types.NamespacedName{ + Name: explainerServiceKey.Name, + Namespace: serviceKey.Namespace, + }, actualExplainerHttpRoute) + }, timeout). + Should(Succeed()) + explainerHost := fmt.Sprintf("%s-%s.%s", explainerServiceKey.Name, serviceKey.Namespace, "example.com") + expectedExplainerHttpRoute := gatewayapiv1.HTTPRoute{ + Spec: gatewayapiv1.HTTPRouteSpec{ + Hostnames: []gatewayapiv1.Hostname{gatewayapiv1.Hostname(explainerHost)}, + Rules: []gatewayapiv1.HTTPRouteRule{ + { + Matches: []gatewayapiv1.HTTPRouteMatch{ + { + Path: &gatewayapiv1.HTTPPathMatch{ + Type: ptr.To(gatewayapiv1.PathMatchRegularExpression), + Value: ptr.To(constants.FallbackPrefix()), + }, + }, + }, + Filters: []gatewayapiv1.HTTPRouteFilter{ + { + Type: gatewayapiv1.HTTPRouteFilterRequestHeaderModifier, + RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ + Set: []gatewayapiv1.HTTPHeader{ + { + Name: constants.IsvcNameHeader, + Value: serviceKey.Name, + }, + { + Name: constants.IsvcNamespaceHeader, + Value: serviceKey.Namespace, + }, + }, + }, + }, + }, + BackendRefs: []gatewayapiv1.HTTPBackendRef{ + { + BackendRef: gatewayapiv1.BackendRef{ + BackendObjectReference: gatewayapiv1.BackendObjectReference{ + Group: (*gatewayapiv1.Group)(ptr.To("")), + Kind: ptr.To(gatewayapiv1.Kind(constants.ServiceKind)), + Name: gatewayapiv1.ObjectName(explainerServiceKey.Name), + Namespace: (*gatewayapiv1.Namespace)(ptr.To(serviceKey.Namespace)), + Port: (*gatewayapiv1.PortNumber)(ptr.To(int32(constants.CommonDefaultHttpPort))), + }, + Weight: ptr.To(int32(1)), + }, + }, + }, + Timeouts: &gatewayapiv1.HTTPRouteTimeouts{ + Request: ptr.To(gatewayapiv1.Duration("30s")), + }, + }, + }, + CommonRouteSpec: gatewayapiv1.CommonRouteSpec{ + ParentRefs: []gatewayapiv1.ParentReference{ + { + Name: gatewayapiv1.ObjectName(kserveGateway.Name), + Kind: ptr.To(gatewayapiv1.Kind(constants.GatewayKind)), + Group: (*gatewayapiv1.Group)(&gatewayapiv1.GroupVersion.Group), + Namespace: ptr.To(gatewayapiv1.Namespace(kserveGateway.Namespace)), + }, + }, + }, + }, + } + Expect(actualExplainerHttpRoute.Spec).To(BeComparableTo(expectedExplainerHttpRoute.Spec)) + + // Mark the Ingress as accepted to make isvc ready + httpRouteStatus := gatewayapiv1.HTTPRouteStatus{ + RouteStatus: gatewayapiv1.RouteStatus{ + Parents: []gatewayapiv1.RouteParentStatus{ + { + ParentRef: gatewayapiv1.ParentReference{ + Name: gatewayapiv1.ObjectName(kserveGateway.Name), + Kind: ptr.To(gatewayapiv1.Kind(constants.GatewayKind)), + Group: (*gatewayapiv1.Group)(&gatewayapiv1.GroupVersion.Group), + Namespace: ptr.To(gatewayapiv1.Namespace(kserveGateway.Namespace)), + }, + ControllerName: "istio.io/gateway-controller", + Conditions: []metav1.Condition{ + { + Type: string(gatewayapiv1.ListenerConditionAccepted), + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "Route was valid", + LastTransitionTime: metav1.Now(), + }, + }, + }, + }, + }, + } + actualPredictorHttpRoute.Status = httpRouteStatus + Expect(k8sClient.Status().Update(context.Background(), actualPredictorHttpRoute)).NotTo(HaveOccurred()) + actualExplainerHttpRoute.Status = httpRouteStatus + Expect(k8sClient.Status().Update(context.Background(), actualExplainerHttpRoute)).NotTo(HaveOccurred()) + actualToplevelHttpRoute.Status = httpRouteStatus + Expect(k8sClient.Status().Update(context.Background(), actualToplevelHttpRoute)).NotTo(HaveOccurred()) + + // verify if InferenceService status is updated + expectedIsvcStatus := v1beta1.InferenceServiceStatus{ + Status: duckv1.Status{ + Conditions: duckv1.Conditions{ + { + Type: v1beta1.ExplainerReady, + Status: "True", + Severity: "Info", + }, + { + Type: v1beta1.IngressReady, + Status: "True", + }, + { + Type: v1beta1.PredictorReady, + Status: "True", + }, + { + Type: apis.ConditionReady, + Status: "True", + }, + }, + }, + URL: &apis.URL{ + Scheme: "http", + Host: fmt.Sprintf("%s-%s.example.com", serviceKey.Name, serviceKey.Namespace), + }, + Address: &duckv1.Addressable{ + URL: &apis.URL{ + Scheme: "http", + Host: fmt.Sprintf("%s.%s.svc.cluster.local", predictorServiceKey.Name, serviceKey.Namespace), + }, + }, + Components: map[v1beta1.ComponentType]v1beta1.ComponentStatusSpec{ + v1beta1.PredictorComponent: { + LatestCreatedRevision: "", + URL: &apis.URL{ + Scheme: "http", + Host: fmt.Sprintf("%s-%s.example.com", predictorServiceKey.Name, serviceKey.Namespace), + }, + }, + v1beta1.ExplainerComponent: { + LatestCreatedRevision: "", + URL: &apis.URL{ + Scheme: "http", + Host: fmt.Sprintf("%s-%s.example.com", explainerServiceKey.Name, serviceKey.Namespace), + }, + }, + }, + ModelStatus: v1beta1.ModelStatus{ + TransitionStatus: "InProgress", + ModelRevisionStates: &v1beta1.ModelRevisionStates{TargetModelState: "Pending"}, + }, + } + Eventually(func() string { + isvc := &v1beta1.InferenceService{} + if err := k8sClient.Get(context.TODO(), serviceKey, isvc); err != nil { + return err.Error() + } + return cmp.Diff(&expectedIsvcStatus, &isvc.Status, cmpopts.IgnoreTypes(apis.VolatileTime{})) + }, timeout).Should(BeEmpty()) + + // check predictor HPA + var cpuUtilization int32 = 75 + var stabilizationWindowSeconds int32 = 0 + selectPolicy := autoscalingv2.MaxChangePolicySelect + actualPredictorHPA := &autoscalingv2.HorizontalPodAutoscaler{} + predictorHPAKey := types.NamespacedName{ + Name: predictorServiceKey.Name, + Namespace: serviceKey.Namespace, + } + Eventually(func() error { return k8sClient.Get(context.TODO(), predictorHPAKey, actualPredictorHPA) }, timeout). + Should(Succeed()) + expectedPredictorHPA := &autoscalingv2.HorizontalPodAutoscaler{ + Spec: autoscalingv2.HorizontalPodAutoscalerSpec{ + ScaleTargetRef: autoscalingv2.CrossVersionObjectReference{ + APIVersion: "apps/v1", + Kind: "Deployment", + Name: predictorServiceKey.Name, + }, + MinReplicas: &minReplicas, + MaxReplicas: maxReplicas, + Metrics: []autoscalingv2.MetricSpec{ + { + Type: autoscalingv2.ResourceMetricSourceType, + Resource: &autoscalingv2.ResourceMetricSource{ + Name: corev1.ResourceCPU, + Target: autoscalingv2.MetricTarget{ + Type: "Utilization", + AverageUtilization: &cpuUtilization, + }, + }, + }, + }, + Behavior: &autoscalingv2.HorizontalPodAutoscalerBehavior{ + ScaleUp: &autoscalingv2.HPAScalingRules{ + StabilizationWindowSeconds: &stabilizationWindowSeconds, + SelectPolicy: &selectPolicy, + Policies: []autoscalingv2.HPAScalingPolicy{ + { + Type: "Pods", + Value: 4, + PeriodSeconds: 15, + }, + { + Type: "Percent", + Value: 100, + PeriodSeconds: 15, + }, + }, + }, + ScaleDown: &autoscalingv2.HPAScalingRules{ + StabilizationWindowSeconds: nil, + SelectPolicy: &selectPolicy, + Policies: []autoscalingv2.HPAScalingPolicy{ + { + Type: "Percent", + Value: 100, + PeriodSeconds: 15, + }, + }, + }, + }, + }, + } + Expect(actualPredictorHPA.Spec).To(BeComparableTo(expectedPredictorHPA.Spec)) + + // check Explainer HPA + actualExplainerHPA := &autoscalingv2.HorizontalPodAutoscaler{} + explainerHPAKey := types.NamespacedName{ + Name: explainerServiceKey.Name, + Namespace: serviceKey.Namespace, + } + Eventually(func() error { return k8sClient.Get(context.TODO(), explainerHPAKey, actualExplainerHPA) }, timeout). + Should(Succeed()) + expectedExplainerHPA := &autoscalingv2.HorizontalPodAutoscaler{ + Spec: autoscalingv2.HorizontalPodAutoscalerSpec{ + ScaleTargetRef: autoscalingv2.CrossVersionObjectReference{ + APIVersion: "apps/v1", + Kind: "Deployment", + Name: explainerServiceKey.Name, + }, + MinReplicas: &explainerMinReplicas, + MaxReplicas: explainerMaxReplicas, + Metrics: []autoscalingv2.MetricSpec{ + { + Type: autoscalingv2.ResourceMetricSourceType, + Resource: &autoscalingv2.ResourceMetricSource{ + Name: corev1.ResourceCPU, + Target: autoscalingv2.MetricTarget{ + Type: "Utilization", + AverageUtilization: &explainerCpuUtilization, + }, + }, + }, + }, + Behavior: &autoscalingv2.HorizontalPodAutoscalerBehavior{ + ScaleUp: &autoscalingv2.HPAScalingRules{ + StabilizationWindowSeconds: &explainerStabilizationWindowSeconds, + SelectPolicy: &ExplainerSelectPolicy, + Policies: []autoscalingv2.HPAScalingPolicy{ + { + Type: "Pods", + Value: 4, + PeriodSeconds: 15, + }, + { + Type: "Percent", + Value: 100, + PeriodSeconds: 15, + }, + }, + }, + ScaleDown: &autoscalingv2.HPAScalingRules{ + StabilizationWindowSeconds: nil, + SelectPolicy: &ExplainerSelectPolicy, + Policies: []autoscalingv2.HPAScalingPolicy{ + { + Type: "Percent", + Value: 100, + PeriodSeconds: 15, + }, + }, + }, + }, + }, + } + Expect(actualExplainerHPA.Spec).To(BeComparableTo(expectedExplainerHPA.Spec)) + }) + }) + Context("When creating inference service with raw kube path based routing predictor", func() { + configs := map[string]string{ + "explainers": `{ + "alibi": { + "image": "kserve/alibi-explainer", + "defaultImageVersion": "latest" + } + }`, + "ingress": `{ + "enableGatewayApi": true, + "kserveIngressGateway": "kserve/kserve-ingress-gateway", + "ingressGateway": "knative-serving/knative-ingress-gateway", + "localGateway": "knative-serving/knative-local-gateway", + "localGatewayService": "knative-local-gateway.istio-system.svc.cluster.local", + "additionalIngressDomains": ["additional.example.com"], + "ingressDomain": "example.com", + "pathTemplate": "/serving/{{ .Namespace }}/{{ .Name }}" + }`, + "storageInitializer": `{ + "image" : "kserve/storage-initializer:latest", + "memoryRequest": "100Mi", + "memoryLimit": "1Gi", + "cpuRequest": "100m", + "cpuLimit": "1", + "CaBundleConfigMapName": "", + "caBundleVolumeMountPath": "/etc/ssl/custom-certs", + "enableDirectPvcVolumeMount": false + }`, + } + + It("Should have httproute/service/deployment/hpa created", func() { + By("By creating a new InferenceService") + // Create configmap + configMap := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: constants.InferenceServiceConfigMapName, + Namespace: constants.KServeNamespace, + }, + Data: configs, + } + Expect(k8sClient.Create(context.TODO(), configMap)).NotTo(HaveOccurred()) + defer k8sClient.Delete(context.TODO(), configMap) + // Create ServingRuntime + servingRuntime := &v1alpha1.ServingRuntime{ + ObjectMeta: metav1.ObjectMeta{ + Name: "tf-serving-raw", + Namespace: "default", + }, + Spec: v1alpha1.ServingRuntimeSpec{ + SupportedModelFormats: []v1alpha1.SupportedModelFormat{ + { + Name: "tensorflow", + Version: proto.String("1"), + AutoSelect: proto.Bool(true), + }, + }, + ServingRuntimePodSpec: v1alpha1.ServingRuntimePodSpec{ + Containers: []corev1.Container{ + { + Name: "kserve-container", + Image: "tensorflow/serving:1.14.0", + Command: []string{"/usr/bin/tensorflow_model_server"}, + Args: []string{ + "--port=9000", + "--rest_api_port=8080", + "--model_base_path=/mnt/models", + "--rest_api_timeout_in_ms=60000", + }, + Resources: defaultResource, + }, + }, + }, + Disabled: proto.Bool(false), + }, + } + k8sClient.Create(context.TODO(), servingRuntime) + defer k8sClient.Delete(context.TODO(), servingRuntime) + serviceName := "raw-foo-path" + expectedRequest := reconcile.Request{NamespacedName: types.NamespacedName{Name: serviceName, Namespace: "default"}} + serviceKey := expectedRequest.NamespacedName + storageUri := "s3://test/mnist/export" + ctx := context.Background() + isvc := &v1beta1.InferenceService{ + ObjectMeta: metav1.ObjectMeta{ + Name: serviceKey.Name, + Namespace: serviceKey.Namespace, + Annotations: map[string]string{ + "serving.kserve.io/deploymentMode": "RawDeployment", + "serving.kserve.io/autoscalerClass": "hpa", + "serving.kserve.io/metrics": "cpu", + "serving.kserve.io/targetUtilizationPercentage": "75", + }, + }, + Spec: v1beta1.InferenceServiceSpec{ + Predictor: v1beta1.PredictorSpec{ + ComponentExtensionSpec: v1beta1.ComponentExtensionSpec{ + MinReplicas: ptr.To(int32(1)), + MaxReplicas: 3, + TimeoutSeconds: ptr.To(int64(30)), + }, + Tensorflow: &v1beta1.TFServingSpec{ + PredictorExtensionSpec: v1beta1.PredictorExtensionSpec{ + StorageURI: &storageUri, + RuntimeVersion: proto.String("1.14.0"), + Container: corev1.Container{ + Name: constants.InferenceServiceContainerName, + Resources: defaultResource, + }, + }, + }, + }, + }, + } + isvc.DefaultInferenceService(nil, nil, &v1beta1.SecurityConfig{AutoMountServiceAccountToken: false}, nil) + Expect(k8sClient.Create(ctx, isvc)).Should(Succeed()) + defer k8sClient.Delete(ctx, isvc) + + inferenceService := &v1beta1.InferenceService{} + + Eventually(func() bool { + err := k8sClient.Get(ctx, serviceKey, inferenceService) + return err == nil + }, timeout, interval).Should(BeTrue()) + + actualDeployment := &appsv1.Deployment{} + predictorDeploymentKey := types.NamespacedName{ + Name: constants.PredictorServiceName(serviceKey.Name), + Namespace: serviceKey.Namespace, + } + Eventually(func() error { return k8sClient.Get(context.TODO(), predictorDeploymentKey, actualDeployment) }, timeout). + Should(Succeed()) + var replicas int32 = 1 + var revisionHistory int32 = 10 + var progressDeadlineSeconds int32 = 600 + var gracePeriod int64 = 30 + expectedDeployment := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: predictorDeploymentKey.Name, + Namespace: predictorDeploymentKey.Namespace, + }, + Spec: appsv1.DeploymentSpec{ + Replicas: &replicas, + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "isvc." + predictorDeploymentKey.Name, + }, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Name: predictorDeploymentKey.Name, + Namespace: "default", + Labels: map[string]string{ + "app": "isvc." + predictorDeploymentKey.Name, + constants.KServiceComponentLabel: constants.Predictor.String(), + constants.InferenceServicePodLabelKey: serviceName, + }, + Annotations: map[string]string{ + constants.StorageInitializerSourceUriInternalAnnotationKey: *isvc.Spec.Predictor.Model.StorageURI, + "serving.kserve.io/deploymentMode": "RawDeployment", + "serving.kserve.io/autoscalerClass": "hpa", + "serving.kserve.io/metrics": "cpu", + "serving.kserve.io/targetUtilizationPercentage": "75", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Image: "tensorflow/serving:" + + *isvc.Spec.Predictor.Model.RuntimeVersion, + Name: constants.InferenceServiceContainerName, + Command: []string{v1beta1.TensorflowEntrypointCommand}, + Args: []string{ + "--port=" + v1beta1.TensorflowServingGRPCPort, + "--rest_api_port=" + v1beta1.TensorflowServingRestPort, + "--model_base_path=" + constants.DefaultModelLocalMountPath, + "--rest_api_timeout_in_ms=60000", + }, + Resources: defaultResource, + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + TCPSocket: &corev1.TCPSocketAction{ + Port: intstr.IntOrString{ + IntVal: 8080, + }, + }, + }, + InitialDelaySeconds: 0, + TimeoutSeconds: 1, + PeriodSeconds: 10, + SuccessThreshold: 1, + FailureThreshold: 3, + }, + TerminationMessagePath: "/dev/termination-log", + TerminationMessagePolicy: "File", + ImagePullPolicy: "IfNotPresent", + }, + }, + SchedulerName: "default-scheduler", + RestartPolicy: "Always", + TerminationGracePeriodSeconds: &gracePeriod, + DNSPolicy: "ClusterFirst", + SecurityContext: &corev1.PodSecurityContext{ + SELinuxOptions: nil, + WindowsOptions: nil, + RunAsUser: nil, + RunAsGroup: nil, + RunAsNonRoot: nil, + SupplementalGroups: nil, + FSGroup: nil, + Sysctls: nil, + FSGroupChangePolicy: nil, + SeccompProfile: nil, + }, + AutomountServiceAccountToken: proto.Bool(false), + }, + }, + Strategy: appsv1.DeploymentStrategy{ + Type: "RollingUpdate", + RollingUpdate: &appsv1.RollingUpdateDeployment{ + MaxUnavailable: &intstr.IntOrString{Type: 1, IntVal: 0, StrVal: "25%"}, + MaxSurge: &intstr.IntOrString{Type: 1, IntVal: 0, StrVal: "25%"}, + }, + }, + RevisionHistoryLimit: &revisionHistory, + ProgressDeadlineSeconds: &progressDeadlineSeconds, + }, + } + Expect(actualDeployment.Spec).To(BeComparableTo(expectedDeployment.Spec)) + + // check service + actualService := &corev1.Service{} + predictorServiceKey := types.NamespacedName{ + Name: constants.PredictorServiceName(serviceKey.Name), + Namespace: serviceKey.Namespace, + } + Eventually(func() error { return k8sClient.Get(context.TODO(), predictorServiceKey, actualService) }, timeout). + Should(Succeed()) + + expectedService := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: predictorServiceKey.Name, + Namespace: predictorServiceKey.Namespace, + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Name: constants.PredictorServiceName(serviceName), + Protocol: "TCP", + Port: 80, + TargetPort: intstr.IntOrString{Type: 0, IntVal: 8080, StrVal: ""}, + }, + }, + Type: "ClusterIP", + SessionAffinity: "None", + Selector: map[string]string{ + "app": "isvc." + constants.PredictorServiceName(serviceName), + }, + }, + } + actualService.Spec.ClusterIP = "" + actualService.Spec.ClusterIPs = nil + actualService.Spec.IPFamilies = nil + actualService.Spec.IPFamilyPolicy = nil + actualService.Spec.InternalTrafficPolicy = nil + Expect(actualService.Spec).To(BeComparableTo(expectedService.Spec)) + + // check isvc status + updatedDeployment := actualDeployment.DeepCopy() + updatedDeployment.Status.Conditions = []appsv1.DeploymentCondition{ + { + Type: appsv1.DeploymentAvailable, + Status: corev1.ConditionTrue, + }, + } + Expect(k8sClient.Status().Update(context.TODO(), updatedDeployment)).NotTo(HaveOccurred()) + + // check http route + actualToplevelHttpRoute := &gatewayapiv1.HTTPRoute{} + Eventually(func() error { + return k8sClient.Get(context.TODO(), types.NamespacedName{ + Name: serviceKey.Name, + Namespace: serviceKey.Namespace, + }, actualToplevelHttpRoute) + }, timeout). + Should(Succeed()) + topLevelHost := fmt.Sprintf("%s-%s.%s", serviceKey.Name, serviceKey.Namespace, "example.com") + prefixUrlPath := fmt.Sprintf("/serving/%s/%s", serviceKey.Namespace, serviceKey.Name) + expectedToplevelHttpRoute := gatewayapiv1.HTTPRoute{ + Spec: gatewayapiv1.HTTPRouteSpec{ + Hostnames: []gatewayapiv1.Hostname{gatewayapiv1.Hostname(topLevelHost), "additional.example.com", "example.com"}, + Rules: []gatewayapiv1.HTTPRouteRule{ + { + Matches: []gatewayapiv1.HTTPRouteMatch{ + { + Path: &gatewayapiv1.HTTPPathMatch{ + Type: ptr.To(gatewayapiv1.PathMatchRegularExpression), + Value: ptr.To(constants.FallbackPrefix()), + }, + }, + }, + Filters: []gatewayapiv1.HTTPRouteFilter{ + { + Type: gatewayapiv1.HTTPRouteFilterRequestHeaderModifier, + RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ + Set: []gatewayapiv1.HTTPHeader{ + { + Name: constants.IsvcNameHeader, + Value: serviceKey.Name, + }, + { + Name: constants.IsvcNamespaceHeader, + Value: serviceKey.Namespace, + }, + }, + }, + }, + }, + BackendRefs: []gatewayapiv1.HTTPBackendRef{ + { + BackendRef: gatewayapiv1.BackendRef{ + BackendObjectReference: gatewayapiv1.BackendObjectReference{ + Group: (*gatewayapiv1.Group)(ptr.To("")), + Kind: ptr.To(gatewayapiv1.Kind(constants.ServiceKind)), + Name: gatewayapiv1.ObjectName(predictorServiceKey.Name), + Namespace: (*gatewayapiv1.Namespace)(ptr.To(serviceKey.Namespace)), + Port: (*gatewayapiv1.PortNumber)(ptr.To(int32(constants.CommonDefaultHttpPort))), + }, + Weight: ptr.To(int32(1)), + }, + }, + }, + Timeouts: &gatewayapiv1.HTTPRouteTimeouts{ + Request: ptr.To(gatewayapiv1.Duration("30s")), + }, + }, + { + Matches: []gatewayapiv1.HTTPRouteMatch{ + { + Path: &gatewayapiv1.HTTPPathMatch{ + Type: ptr.To(gatewayapiv1.PathMatchRegularExpression), + Value: ptr.To(prefixUrlPath + "/"), + }, + }, + }, + Filters: []gatewayapiv1.HTTPRouteFilter{ + { + Type: gatewayapiv1.HTTPRouteFilterRequestHeaderModifier, + RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ + Set: []gatewayapiv1.HTTPHeader{ + { + Name: constants.IsvcNameHeader, + Value: serviceKey.Name, + }, + { + Name: constants.IsvcNamespaceHeader, + Value: serviceKey.Namespace, + }, + }, + }, + }, + }, + BackendRefs: []gatewayapiv1.HTTPBackendRef{ + { + BackendRef: gatewayapiv1.BackendRef{ + BackendObjectReference: gatewayapiv1.BackendObjectReference{ + Group: (*gatewayapiv1.Group)(ptr.To("")), + Kind: ptr.To(gatewayapiv1.Kind(constants.ServiceKind)), + Name: gatewayapiv1.ObjectName(predictorServiceKey.Name), + Namespace: (*gatewayapiv1.Namespace)(ptr.To(serviceKey.Namespace)), + Port: (*gatewayapiv1.PortNumber)(ptr.To(int32(constants.CommonDefaultHttpPort))), + }, + Weight: ptr.To(int32(1)), + }, + }, + }, + Timeouts: &gatewayapiv1.HTTPRouteTimeouts{ + Request: ptr.To(gatewayapiv1.Duration("30s")), + }, + }, + }, + CommonRouteSpec: gatewayapiv1.CommonRouteSpec{ + ParentRefs: []gatewayapiv1.ParentReference{ + { + Name: gatewayapiv1.ObjectName(kserveGateway.Name), + Kind: ptr.To(gatewayapiv1.Kind(constants.GatewayKind)), + Group: (*gatewayapiv1.Group)(&gatewayapiv1.GroupVersion.Group), + Namespace: ptr.To(gatewayapiv1.Namespace(kserveGateway.Namespace)), + }, + }, + }, + }, + } + Expect(actualToplevelHttpRoute.Spec).To(BeComparableTo(expectedToplevelHttpRoute.Spec)) + + actualPredictorHttpRoute := &gatewayapiv1.HTTPRoute{} + Eventually(func() error { + return k8sClient.Get(context.TODO(), types.NamespacedName{ + Name: predictorServiceKey.Name, + Namespace: serviceKey.Namespace, + }, actualPredictorHttpRoute) + }, timeout). + Should(Succeed()) + predictorHost := fmt.Sprintf("%s-%s.%s", predictorServiceKey.Name, serviceKey.Namespace, "example.com") + expectedPredictorHttpRoute := gatewayapiv1.HTTPRoute{ + Spec: gatewayapiv1.HTTPRouteSpec{ + Hostnames: []gatewayapiv1.Hostname{gatewayapiv1.Hostname(predictorHost)}, + Rules: []gatewayapiv1.HTTPRouteRule{ + { + Matches: []gatewayapiv1.HTTPRouteMatch{ + { + Path: &gatewayapiv1.HTTPPathMatch{ + Type: ptr.To(gatewayapiv1.PathMatchRegularExpression), + Value: ptr.To(constants.FallbackPrefix()), + }, + }, + }, + Filters: []gatewayapiv1.HTTPRouteFilter{ + { + Type: gatewayapiv1.HTTPRouteFilterRequestHeaderModifier, + RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ + Set: []gatewayapiv1.HTTPHeader{ + { + Name: constants.IsvcNameHeader, + Value: serviceKey.Name, + }, + { + Name: constants.IsvcNamespaceHeader, + Value: serviceKey.Namespace, + }, + }, + }, + }, + }, + BackendRefs: []gatewayapiv1.HTTPBackendRef{ + { + BackendRef: gatewayapiv1.BackendRef{ + BackendObjectReference: gatewayapiv1.BackendObjectReference{ + Group: (*gatewayapiv1.Group)(ptr.To("")), + Kind: ptr.To(gatewayapiv1.Kind(constants.ServiceKind)), + Name: gatewayapiv1.ObjectName(predictorServiceKey.Name), + Namespace: (*gatewayapiv1.Namespace)(ptr.To(serviceKey.Namespace)), + Port: (*gatewayapiv1.PortNumber)(ptr.To(int32(constants.CommonDefaultHttpPort))), + }, + Weight: ptr.To(int32(1)), + }, + }, + }, + Timeouts: &gatewayapiv1.HTTPRouteTimeouts{ + Request: ptr.To(gatewayapiv1.Duration("30s")), + }, + }, + }, + CommonRouteSpec: gatewayapiv1.CommonRouteSpec{ + ParentRefs: []gatewayapiv1.ParentReference{ + { + Name: gatewayapiv1.ObjectName(kserveGateway.Name), + Kind: ptr.To(gatewayapiv1.Kind(constants.GatewayKind)), + Group: (*gatewayapiv1.Group)(&gatewayapiv1.GroupVersion.Group), + Namespace: ptr.To(gatewayapiv1.Namespace(kserveGateway.Namespace)), + }, + }, + }, + }, + } + Expect(actualPredictorHttpRoute.Spec).To(BeComparableTo(expectedPredictorHttpRoute.Spec)) + + // Mark the Ingress as accepted to make isvc ready + httpRouteStatus := gatewayapiv1.HTTPRouteStatus{ + RouteStatus: gatewayapiv1.RouteStatus{ + Parents: []gatewayapiv1.RouteParentStatus{ + { + ParentRef: gatewayapiv1.ParentReference{ + Name: gatewayapiv1.ObjectName(kserveGateway.Name), + Kind: ptr.To(gatewayapiv1.Kind(constants.GatewayKind)), + Group: (*gatewayapiv1.Group)(&gatewayapiv1.GroupVersion.Group), + Namespace: ptr.To(gatewayapiv1.Namespace(kserveGateway.Namespace)), + }, + ControllerName: "istio.io/gateway-controller", + Conditions: []metav1.Condition{ + { + Type: string(gatewayapiv1.ListenerConditionAccepted), + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "Route was valid", + LastTransitionTime: metav1.Now(), + }, + }, + }, + }, + }, + } + actualPredictorHttpRoute.Status = httpRouteStatus + Expect(k8sClient.Status().Update(context.Background(), actualPredictorHttpRoute)).NotTo(HaveOccurred()) + actualToplevelHttpRoute.Status = httpRouteStatus + Expect(k8sClient.Status().Update(context.Background(), actualToplevelHttpRoute)).NotTo(HaveOccurred()) + + // verify if InferenceService status is updated + expectedIsvcStatus := v1beta1.InferenceServiceStatus{ + Status: duckv1.Status{ + Conditions: duckv1.Conditions{ + { + Type: v1beta1.IngressReady, + Status: "True", + }, + { + Type: v1beta1.PredictorReady, + Status: "True", + }, + { + Type: apis.ConditionReady, + Status: "True", + }, + }, + }, + URL: &apis.URL{ + Scheme: "http", + Host: fmt.Sprintf("%s-%s.example.com", serviceKey.Name, serviceKey.Namespace), + }, + Address: &duckv1.Addressable{ + URL: &apis.URL{ + Scheme: "http", + Host: fmt.Sprintf("%s-predictor.%s.svc.cluster.local", serviceKey.Name, serviceKey.Namespace), + }, + }, + Components: map[v1beta1.ComponentType]v1beta1.ComponentStatusSpec{ + v1beta1.PredictorComponent: { + LatestCreatedRevision: "", + URL: &apis.URL{ + Scheme: "http", + Host: fmt.Sprintf("%s-%s.example.com", predictorServiceKey.Name, serviceKey.Namespace), + }, + }, + }, + ModelStatus: v1beta1.ModelStatus{ + TransitionStatus: "InProgress", + ModelRevisionStates: &v1beta1.ModelRevisionStates{TargetModelState: "Pending"}, + }, + } + Eventually(func() string { + isvc := &v1beta1.InferenceService{} + if err := k8sClient.Get(context.TODO(), serviceKey, isvc); err != nil { + return err.Error() + } + return cmp.Diff(&expectedIsvcStatus, &isvc.Status, cmpopts.IgnoreTypes(apis.VolatileTime{})) + }, timeout).Should(BeEmpty()) + + // check HPA + var minReplicas int32 = 1 + var maxReplicas int32 = 3 + var cpuUtilization int32 = 75 + var stabilizationWindowSeconds int32 = 0 + selectPolicy := autoscalingv2.MaxChangePolicySelect + actualHPA := &autoscalingv2.HorizontalPodAutoscaler{} + predictorHPAKey := types.NamespacedName{ + Name: predictorServiceKey.Name, + Namespace: serviceKey.Namespace, + } + Eventually(func() error { return k8sClient.Get(context.TODO(), predictorHPAKey, actualHPA) }, timeout). + Should(Succeed()) + expectedHPA := &autoscalingv2.HorizontalPodAutoscaler{ + Spec: autoscalingv2.HorizontalPodAutoscalerSpec{ + ScaleTargetRef: autoscalingv2.CrossVersionObjectReference{ + APIVersion: "apps/v1", + Kind: "Deployment", + Name: predictorServiceKey.Name, + }, + MinReplicas: &minReplicas, + MaxReplicas: maxReplicas, + Metrics: []autoscalingv2.MetricSpec{ + { + Type: autoscalingv2.ResourceMetricSourceType, + Resource: &autoscalingv2.ResourceMetricSource{ + Name: corev1.ResourceCPU, + Target: autoscalingv2.MetricTarget{ + Type: "Utilization", + AverageUtilization: &cpuUtilization, + }, + }, + }, + }, + Behavior: &autoscalingv2.HorizontalPodAutoscalerBehavior{ + ScaleUp: &autoscalingv2.HPAScalingRules{ + StabilizationWindowSeconds: &stabilizationWindowSeconds, + SelectPolicy: &selectPolicy, + Policies: []autoscalingv2.HPAScalingPolicy{ + { + Type: "Pods", + Value: 4, + PeriodSeconds: 15, + }, + { + Type: "Percent", + Value: 100, + PeriodSeconds: 15, + }, + }, + }, + ScaleDown: &autoscalingv2.HPAScalingRules{ + StabilizationWindowSeconds: nil, + SelectPolicy: &selectPolicy, + Policies: []autoscalingv2.HPAScalingPolicy{ + { + Type: "Percent", + Value: 100, + PeriodSeconds: 15, + }, + }, + }, + }, + }, + } + Expect(actualHPA.Spec).To(BeComparableTo(expectedHPA.Spec)) + }) + }) + Context("When creating inference service with raw kube path based routing predictor and transformer", func() { + configs := map[string]string{ + "explainers": `{ + "alibi": { + "image": "kserve/alibi-explainer", + "defaultImageVersion": "latest" + } + }`, + "ingress": `{ + "enableGatewayApi": true, + "kserveIngressGateway": "kserve/kserve-ingress-gateway", + "ingressGateway": "knative-serving/knative-ingress-gateway", + "localGateway": "knative-serving/knative-local-gateway", + "localGatewayService": "knative-local-gateway.istio-system.svc.cluster.local", + "additionalIngressDomains": ["additional.example.com"], + "ingressDomain": "example.com", + "pathTemplate": "/serving/{{ .Namespace }}/{{ .Name }}" + }`, + "storageInitializer": `{ + "image" : "kserve/storage-initializer:latest", + "memoryRequest": "100Mi", + "memoryLimit": "1Gi", + "cpuRequest": "100m", + "cpuLimit": "1", + "CaBundleConfigMapName": "", + "caBundleVolumeMountPath": "/etc/ssl/custom-certs", + "enableDirectPvcVolumeMount": false + }`, + } + + It("Should have ingress/service/deployment/hpa created for transformer and predictor", func() { + By("By creating a new InferenceService") + // Create configmap + configMap := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: constants.InferenceServiceConfigMapName, + Namespace: constants.KServeNamespace, + }, + Data: configs, + } + Expect(k8sClient.Create(context.TODO(), configMap)).NotTo(HaveOccurred()) + defer k8sClient.Delete(context.TODO(), configMap) + // Create ServingRuntime + servingRuntime := &v1alpha1.ServingRuntime{ + ObjectMeta: metav1.ObjectMeta{ + Name: "tf-serving-raw", + Namespace: "default", + }, + Spec: v1alpha1.ServingRuntimeSpec{ + SupportedModelFormats: []v1alpha1.SupportedModelFormat{ + { + Name: "tensorflow", + Version: proto.String("1"), + AutoSelect: proto.Bool(true), + }, + }, + ServingRuntimePodSpec: v1alpha1.ServingRuntimePodSpec{ + Containers: []corev1.Container{ + { + Name: "kserve-container", + Image: "tensorflow/serving:1.14.0", + Command: []string{"/usr/bin/tensorflow_model_server"}, + Args: []string{ + "--port=9000", + "--rest_api_port=8080", + "--model_base_path=/mnt/models", + "--rest_api_timeout_in_ms=60000", + }, + Resources: defaultResource, + }, + }, + }, + Disabled: proto.Bool(false), + }, + } + k8sClient.Create(context.TODO(), servingRuntime) + defer k8sClient.Delete(context.TODO(), servingRuntime) + serviceName := "raw-foo-trans-path" + namespace := "default" + expectedRequest := reconcile.Request{NamespacedName: types.NamespacedName{Name: serviceName, Namespace: namespace}} + serviceKey := expectedRequest.NamespacedName + predictorServiceKey := types.NamespacedName{ + Name: constants.PredictorServiceName(serviceName), + Namespace: namespace, + } + transformerServiceKey := types.NamespacedName{ + Name: constants.TransformerServiceName(serviceName), + Namespace: namespace, + } + storageUri := "s3://test/mnist/export" + var minReplicas int32 = 1 + var maxReplicas int32 = 3 + var transformerMinReplicas int32 = 1 + var transformerMaxReplicas int32 = 2 + var transformerCpuUtilization int32 = 80 + var transformerStabilizationWindowSeconds int32 = 0 + transformerSelectPolicy := autoscalingv2.MaxChangePolicySelect + ctx := context.Background() + isvc := &v1beta1.InferenceService{ + ObjectMeta: metav1.ObjectMeta{ + Name: serviceKey.Name, + Namespace: serviceKey.Namespace, + Annotations: map[string]string{ + "serving.kserve.io/deploymentMode": "RawDeployment", + "serving.kserve.io/autoscalerClass": "hpa", + "serving.kserve.io/metrics": "cpu", + "serving.kserve.io/targetUtilizationPercentage": "75", + }, + }, + Spec: v1beta1.InferenceServiceSpec{ + Predictor: v1beta1.PredictorSpec{ + ComponentExtensionSpec: v1beta1.ComponentExtensionSpec{ + MinReplicas: ptr.To(minReplicas), + MaxReplicas: maxReplicas, + }, + Tensorflow: &v1beta1.TFServingSpec{ + PredictorExtensionSpec: v1beta1.PredictorExtensionSpec{ + StorageURI: &storageUri, + RuntimeVersion: proto.String("1.14.0"), + Container: corev1.Container{ + Name: constants.InferenceServiceContainerName, + Resources: defaultResource, + }, + }, + }, + }, + Transformer: &v1beta1.TransformerSpec{ + ComponentExtensionSpec: v1beta1.ComponentExtensionSpec{ + MinReplicas: ptr.To(transformerMinReplicas), + MaxReplicas: transformerMaxReplicas, + ScaleTarget: ptr.To(transformerCpuUtilization), + TimeoutSeconds: ptr.To(int64(30)), + }, + PodSpec: v1beta1.PodSpec{ + Containers: []corev1.Container{ + { + Image: "transformer:v1", + Resources: defaultResource, + Args: []string{ + "--port=8080", + }, + }, + }, + }, + }, + }, + } + isvcConfigMap, err := v1beta1.GetInferenceServiceConfigMap(context.Background(), clientset) + Expect(err).NotTo(HaveOccurred()) + isvcConfig, err := v1beta1.NewInferenceServicesConfig(isvcConfigMap) + Expect(err).NotTo(HaveOccurred()) + isvc.DefaultInferenceService(isvcConfig, nil, &v1beta1.SecurityConfig{AutoMountServiceAccountToken: false}, nil) + Expect(k8sClient.Create(ctx, isvc)).Should(Succeed()) + defer k8sClient.Delete(ctx, isvc) + + inferenceService := &v1beta1.InferenceService{} + + Eventually(func() bool { + err := k8sClient.Get(ctx, serviceKey, inferenceService) + return err == nil + }, timeout, interval).Should(BeTrue()) + + // check predictor deployment + actualPredictorDeployment := &appsv1.Deployment{} + predictorDeploymentKey := types.NamespacedName{ + Name: predictorServiceKey.Name, + Namespace: serviceKey.Namespace, + } + Eventually(func() error { return k8sClient.Get(context.TODO(), predictorDeploymentKey, actualPredictorDeployment) }, timeout). + Should(Succeed()) + var replicas int32 = 1 + var revisionHistory int32 = 10 + var progressDeadlineSeconds int32 = 600 + var gracePeriod int64 = 30 + expectedPredictorDeployment := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: predictorDeploymentKey.Name, + Namespace: predictorDeploymentKey.Namespace, + }, + Spec: appsv1.DeploymentSpec{ + Replicas: &replicas, + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "isvc." + predictorDeploymentKey.Name, + }, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Name: predictorDeploymentKey.Name, + Namespace: "default", + Labels: map[string]string{ + "app": "isvc." + predictorDeploymentKey.Name, + constants.KServiceComponentLabel: constants.Predictor.String(), + constants.InferenceServicePodLabelKey: serviceName, + }, + Annotations: map[string]string{ + constants.StorageInitializerSourceUriInternalAnnotationKey: *isvc.Spec.Predictor.Model.StorageURI, + "serving.kserve.io/deploymentMode": "RawDeployment", + "serving.kserve.io/autoscalerClass": "hpa", + "serving.kserve.io/metrics": "cpu", + "serving.kserve.io/targetUtilizationPercentage": "75", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Image: "tensorflow/serving:" + + *isvc.Spec.Predictor.Model.RuntimeVersion, + Name: constants.InferenceServiceContainerName, + Command: []string{v1beta1.TensorflowEntrypointCommand}, + Args: []string{ + "--port=" + v1beta1.TensorflowServingGRPCPort, + "--rest_api_port=" + v1beta1.TensorflowServingRestPort, + "--model_base_path=" + constants.DefaultModelLocalMountPath, + "--rest_api_timeout_in_ms=60000", + }, + Resources: defaultResource, + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + TCPSocket: &corev1.TCPSocketAction{ + Port: intstr.IntOrString{ + IntVal: 8080, + }, + }, + }, + InitialDelaySeconds: 0, + TimeoutSeconds: 1, + PeriodSeconds: 10, + SuccessThreshold: 1, + FailureThreshold: 3, + }, + TerminationMessagePath: "/dev/termination-log", + TerminationMessagePolicy: "File", + ImagePullPolicy: "IfNotPresent", + }, + }, + SchedulerName: "default-scheduler", + RestartPolicy: "Always", + TerminationGracePeriodSeconds: &gracePeriod, + DNSPolicy: "ClusterFirst", + SecurityContext: &corev1.PodSecurityContext{ + SELinuxOptions: nil, + WindowsOptions: nil, + RunAsUser: nil, + RunAsGroup: nil, + RunAsNonRoot: nil, + SupplementalGroups: nil, + FSGroup: nil, + Sysctls: nil, + FSGroupChangePolicy: nil, + SeccompProfile: nil, + }, + AutomountServiceAccountToken: proto.Bool(false), + }, + }, + Strategy: appsv1.DeploymentStrategy{ + Type: "RollingUpdate", + RollingUpdate: &appsv1.RollingUpdateDeployment{ + MaxUnavailable: &intstr.IntOrString{Type: 1, IntVal: 0, StrVal: "25%"}, + MaxSurge: &intstr.IntOrString{Type: 1, IntVal: 0, StrVal: "25%"}, + }, + }, + RevisionHistoryLimit: &revisionHistory, + ProgressDeadlineSeconds: &progressDeadlineSeconds, + }, + } + Expect(actualPredictorDeployment.Spec).To(BeComparableTo(expectedPredictorDeployment.Spec)) + + // check transformer deployment + actualTransformerDeployment := &appsv1.Deployment{} + transformerDeploymentKey := types.NamespacedName{ + Name: transformerServiceKey.Name, + Namespace: serviceKey.Namespace, + } + Eventually(func() error { + return k8sClient.Get(context.TODO(), transformerDeploymentKey, actualTransformerDeployment) + }, timeout). + Should(Succeed()) + expectedTransformerDeployment := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: transformerDeploymentKey.Name, + Namespace: transformerDeploymentKey.Namespace, + }, + Spec: appsv1.DeploymentSpec{ + Replicas: &replicas, + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "isvc." + transformerDeploymentKey.Name, + }, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Name: transformerDeploymentKey.Name, + Namespace: "default", + Labels: map[string]string{ + "app": "isvc." + transformerDeploymentKey.Name, + constants.KServiceComponentLabel: constants.Transformer.String(), + constants.InferenceServicePodLabelKey: serviceName, + }, + Annotations: map[string]string{ + "serving.kserve.io/deploymentMode": "RawDeployment", + "serving.kserve.io/autoscalerClass": "hpa", + "serving.kserve.io/metrics": "cpu", + "serving.kserve.io/targetUtilizationPercentage": "75", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Image: "transformer:v1", + Name: constants.InferenceServiceContainerName, + Args: []string{ + "--port=8080", + "--model_name", + serviceKey.Name, + "--predictor_host", + fmt.Sprintf("%s.%s", predictorServiceKey.Name, predictorServiceKey.Namespace), + "--http_port", + "8080", + }, + Resources: defaultResource, + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + TCPSocket: &corev1.TCPSocketAction{ + Port: intstr.IntOrString{ + IntVal: 8080, + }, + }, + }, + InitialDelaySeconds: 0, + TimeoutSeconds: 1, + PeriodSeconds: 10, + SuccessThreshold: 1, + FailureThreshold: 3, + }, + TerminationMessagePath: "/dev/termination-log", + TerminationMessagePolicy: "File", + ImagePullPolicy: "IfNotPresent", + }, + }, + SchedulerName: "default-scheduler", + RestartPolicy: "Always", + TerminationGracePeriodSeconds: &gracePeriod, + DNSPolicy: "ClusterFirst", + SecurityContext: &corev1.PodSecurityContext{ + SELinuxOptions: nil, + WindowsOptions: nil, + RunAsUser: nil, + RunAsGroup: nil, + RunAsNonRoot: nil, + SupplementalGroups: nil, + FSGroup: nil, + Sysctls: nil, + FSGroupChangePolicy: nil, + SeccompProfile: nil, + }, + AutomountServiceAccountToken: proto.Bool(false), + }, + }, + Strategy: appsv1.DeploymentStrategy{ + Type: "RollingUpdate", + RollingUpdate: &appsv1.RollingUpdateDeployment{ + MaxUnavailable: &intstr.IntOrString{Type: 1, IntVal: 0, StrVal: "25%"}, + MaxSurge: &intstr.IntOrString{Type: 1, IntVal: 0, StrVal: "25%"}, + }, + }, + RevisionHistoryLimit: &revisionHistory, + ProgressDeadlineSeconds: &progressDeadlineSeconds, + }, + } + Expect(actualTransformerDeployment.Spec).To(BeComparableTo(expectedTransformerDeployment.Spec)) + + // check predictor service + actualPredictorService := &corev1.Service{} + Eventually(func() error { return k8sClient.Get(context.TODO(), predictorServiceKey, actualPredictorService) }, timeout). + Should(Succeed()) + expectedPredictorService := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: predictorServiceKey.Name, + Namespace: predictorServiceKey.Namespace, + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Name: predictorServiceKey.Name, + Protocol: "TCP", + Port: 80, + TargetPort: intstr.IntOrString{Type: 0, IntVal: 8080, StrVal: ""}, + }, + }, + Type: "ClusterIP", + SessionAffinity: "None", + Selector: map[string]string{ + "app": "isvc." + predictorServiceKey.Name, + }, + }, + } + actualPredictorService.Spec.ClusterIP = "" + actualPredictorService.Spec.ClusterIPs = nil + actualPredictorService.Spec.IPFamilies = nil + actualPredictorService.Spec.IPFamilyPolicy = nil + actualPredictorService.Spec.InternalTrafficPolicy = nil + Expect(actualPredictorService.Spec).To(BeComparableTo(expectedPredictorService.Spec)) + + // check transformer service + actualTransformerService := &corev1.Service{} + Eventually(func() error { return k8sClient.Get(context.TODO(), transformerServiceKey, actualTransformerService) }, timeout). + Should(Succeed()) + expectedTransformerService := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: transformerServiceKey.Name, + Namespace: transformerServiceKey.Namespace, + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Name: transformerServiceKey.Name, + Protocol: "TCP", + Port: 80, + TargetPort: intstr.IntOrString{Type: 0, IntVal: 8080, StrVal: ""}, + }, + }, + Type: "ClusterIP", + SessionAffinity: "None", + Selector: map[string]string{ + "app": "isvc." + transformerServiceKey.Name, + }, + }, + } + actualTransformerService.Spec.ClusterIP = "" + actualTransformerService.Spec.ClusterIPs = nil + actualTransformerService.Spec.IPFamilies = nil + actualTransformerService.Spec.IPFamilyPolicy = nil + actualTransformerService.Spec.InternalTrafficPolicy = nil + Expect(actualTransformerService.Spec).To(BeComparableTo(expectedTransformerService.Spec)) + + // update deployment status to make isvc ready + updatedPredictorDeployment := actualPredictorDeployment.DeepCopy() + updatedPredictorDeployment.Status.Conditions = []appsv1.DeploymentCondition{ + { + Type: appsv1.DeploymentAvailable, + Status: corev1.ConditionTrue, + }, + } + Expect(k8sClient.Status().Update(context.TODO(), updatedPredictorDeployment)).NotTo(HaveOccurred()) + updatedTransformerDeployment := actualTransformerDeployment.DeepCopy() + updatedTransformerDeployment.Status.Conditions = []appsv1.DeploymentCondition{ + { + Type: appsv1.DeploymentAvailable, + Status: corev1.ConditionTrue, + }, + } + Expect(k8sClient.Status().Update(context.TODO(), updatedTransformerDeployment)).NotTo(HaveOccurred()) + + // check http route + actualToplevelHttpRoute := &gatewayapiv1.HTTPRoute{} + Eventually(func() error { + return k8sClient.Get(context.TODO(), types.NamespacedName{ + Name: serviceKey.Name, + Namespace: serviceKey.Namespace, + }, actualToplevelHttpRoute) + }, timeout). + Should(Succeed()) + topLevelHost := fmt.Sprintf("%s-%s.%s", serviceKey.Name, serviceKey.Namespace, "example.com") + prefixUrlPath := fmt.Sprintf("/serving/%s/%s", serviceKey.Namespace, serviceKey.Name) + expectedToplevelHttpRoute := gatewayapiv1.HTTPRoute{ + Spec: gatewayapiv1.HTTPRouteSpec{ + Hostnames: []gatewayapiv1.Hostname{gatewayapiv1.Hostname(topLevelHost), "additional.example.com", "example.com"}, + Rules: []gatewayapiv1.HTTPRouteRule{ + { + Matches: []gatewayapiv1.HTTPRouteMatch{ + { + Path: &gatewayapiv1.HTTPPathMatch{ + Type: ptr.To(gatewayapiv1.PathMatchRegularExpression), + Value: ptr.To(constants.FallbackPrefix()), + }, + }, + }, + Filters: []gatewayapiv1.HTTPRouteFilter{ + { + Type: gatewayapiv1.HTTPRouteFilterRequestHeaderModifier, + RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ + Set: []gatewayapiv1.HTTPHeader{ + { + Name: constants.IsvcNameHeader, + Value: serviceKey.Name, + }, + { + Name: constants.IsvcNamespaceHeader, + Value: serviceKey.Namespace, + }, + }, + }, + }, + }, + BackendRefs: []gatewayapiv1.HTTPBackendRef{ + { + BackendRef: gatewayapiv1.BackendRef{ + BackendObjectReference: gatewayapiv1.BackendObjectReference{ + Group: (*gatewayapiv1.Group)(ptr.To("")), + Kind: ptr.To(gatewayapiv1.Kind(constants.ServiceKind)), + Name: gatewayapiv1.ObjectName(transformerServiceKey.Name), + Namespace: (*gatewayapiv1.Namespace)(ptr.To(serviceKey.Namespace)), + Port: (*gatewayapiv1.PortNumber)(ptr.To(int32(constants.CommonDefaultHttpPort))), + }, + Weight: ptr.To(int32(1)), + }, + }, + }, + Timeouts: &gatewayapiv1.HTTPRouteTimeouts{ + Request: ptr.To(gatewayapiv1.Duration("30s")), + }, + }, + { + Matches: []gatewayapiv1.HTTPRouteMatch{ + { + Path: &gatewayapiv1.HTTPPathMatch{ + Type: ptr.To(gatewayapiv1.PathMatchRegularExpression), + Value: ptr.To(prefixUrlPath + "/"), + }, + }, + }, + Filters: []gatewayapiv1.HTTPRouteFilter{ + { + Type: gatewayapiv1.HTTPRouteFilterRequestHeaderModifier, + RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ + Set: []gatewayapiv1.HTTPHeader{ + { + Name: constants.IsvcNameHeader, + Value: serviceKey.Name, + }, + { + Name: constants.IsvcNamespaceHeader, + Value: serviceKey.Namespace, + }, + }, + }, + }, + }, + BackendRefs: []gatewayapiv1.HTTPBackendRef{ + { + BackendRef: gatewayapiv1.BackendRef{ + BackendObjectReference: gatewayapiv1.BackendObjectReference{ + Group: (*gatewayapiv1.Group)(ptr.To("")), + Kind: ptr.To(gatewayapiv1.Kind(constants.ServiceKind)), + Name: gatewayapiv1.ObjectName(transformerServiceKey.Name), + Namespace: (*gatewayapiv1.Namespace)(ptr.To(serviceKey.Namespace)), + Port: (*gatewayapiv1.PortNumber)(ptr.To(int32(constants.CommonDefaultHttpPort))), + }, + Weight: ptr.To(int32(1)), + }, + }, + }, + Timeouts: &gatewayapiv1.HTTPRouteTimeouts{ + Request: ptr.To(gatewayapiv1.Duration("30s")), + }, + }, + }, + CommonRouteSpec: gatewayapiv1.CommonRouteSpec{ + ParentRefs: []gatewayapiv1.ParentReference{ + { + Name: gatewayapiv1.ObjectName(kserveGateway.Name), + Kind: ptr.To(gatewayapiv1.Kind(constants.GatewayKind)), + Group: (*gatewayapiv1.Group)(&gatewayapiv1.GroupVersion.Group), + Namespace: ptr.To(gatewayapiv1.Namespace(kserveGateway.Namespace)), + }, + }, + }, + }, + } + Expect(actualToplevelHttpRoute.Spec).To(BeComparableTo(expectedToplevelHttpRoute.Spec)) + + actualPredictorHttpRoute := &gatewayapiv1.HTTPRoute{} + Eventually(func() error { + return k8sClient.Get(context.TODO(), types.NamespacedName{ + Name: predictorServiceKey.Name, + Namespace: serviceKey.Namespace, + }, actualPredictorHttpRoute) + }, timeout). + Should(Succeed()) + predictorHost := fmt.Sprintf("%s-%s.%s", predictorServiceKey.Name, serviceKey.Namespace, "example.com") + expectedPredictorHttpRoute := gatewayapiv1.HTTPRoute{ + Spec: gatewayapiv1.HTTPRouteSpec{ + Hostnames: []gatewayapiv1.Hostname{gatewayapiv1.Hostname(predictorHost)}, + Rules: []gatewayapiv1.HTTPRouteRule{ + { + Matches: []gatewayapiv1.HTTPRouteMatch{ + { + Path: &gatewayapiv1.HTTPPathMatch{ + Type: ptr.To(gatewayapiv1.PathMatchRegularExpression), + Value: ptr.To(constants.FallbackPrefix()), + }, + }, + }, + Filters: []gatewayapiv1.HTTPRouteFilter{ + { + Type: gatewayapiv1.HTTPRouteFilterRequestHeaderModifier, + RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ + Set: []gatewayapiv1.HTTPHeader{ + { + Name: constants.IsvcNameHeader, + Value: serviceKey.Name, + }, + { + Name: constants.IsvcNamespaceHeader, + Value: serviceKey.Namespace, + }, + }, + }, + }, + }, + BackendRefs: []gatewayapiv1.HTTPBackendRef{ + { + BackendRef: gatewayapiv1.BackendRef{ + BackendObjectReference: gatewayapiv1.BackendObjectReference{ + Group: (*gatewayapiv1.Group)(ptr.To("")), + Kind: ptr.To(gatewayapiv1.Kind(constants.ServiceKind)), + Name: gatewayapiv1.ObjectName(predictorServiceKey.Name), + Namespace: (*gatewayapiv1.Namespace)(ptr.To(serviceKey.Namespace)), + Port: (*gatewayapiv1.PortNumber)(ptr.To(int32(constants.CommonDefaultHttpPort))), + }, + Weight: ptr.To(int32(1)), + }, + }, + }, + Timeouts: &gatewayapiv1.HTTPRouteTimeouts{ + Request: ptr.To(gatewayapiv1.Duration("60s")), + }, + }, + }, + CommonRouteSpec: gatewayapiv1.CommonRouteSpec{ + ParentRefs: []gatewayapiv1.ParentReference{ + { + Name: gatewayapiv1.ObjectName(kserveGateway.Name), + Kind: ptr.To(gatewayapiv1.Kind(constants.GatewayKind)), + Group: (*gatewayapiv1.Group)(&gatewayapiv1.GroupVersion.Group), + Namespace: ptr.To(gatewayapiv1.Namespace(kserveGateway.Namespace)), + }, + }, + }, + }, + } + Expect(actualPredictorHttpRoute.Spec).To(BeComparableTo(expectedPredictorHttpRoute.Spec)) + + actualTransformerHttpRoute := &gatewayapiv1.HTTPRoute{} + Eventually(func() error { + return k8sClient.Get(context.TODO(), types.NamespacedName{ + Name: transformerServiceKey.Name, + Namespace: serviceKey.Namespace, + }, actualTransformerHttpRoute) + }, timeout). + Should(Succeed()) + transformerHost := fmt.Sprintf("%s-%s.%s", transformerServiceKey.Name, serviceKey.Namespace, "example.com") + expectedTransformerHttpRoute := gatewayapiv1.HTTPRoute{ + Spec: gatewayapiv1.HTTPRouteSpec{ + Hostnames: []gatewayapiv1.Hostname{gatewayapiv1.Hostname(transformerHost)}, + Rules: []gatewayapiv1.HTTPRouteRule{ + { + Matches: []gatewayapiv1.HTTPRouteMatch{ + { + Path: &gatewayapiv1.HTTPPathMatch{ + Type: ptr.To(gatewayapiv1.PathMatchRegularExpression), + Value: ptr.To(constants.FallbackPrefix()), + }, + }, + }, + Filters: []gatewayapiv1.HTTPRouteFilter{ + { + Type: gatewayapiv1.HTTPRouteFilterRequestHeaderModifier, + RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ + Set: []gatewayapiv1.HTTPHeader{ + { + Name: constants.IsvcNameHeader, + Value: serviceKey.Name, + }, + { + Name: constants.IsvcNamespaceHeader, + Value: serviceKey.Namespace, + }, + }, + }, + }, + }, + BackendRefs: []gatewayapiv1.HTTPBackendRef{ + { + BackendRef: gatewayapiv1.BackendRef{ + BackendObjectReference: gatewayapiv1.BackendObjectReference{ + Group: (*gatewayapiv1.Group)(ptr.To("")), + Kind: ptr.To(gatewayapiv1.Kind(constants.ServiceKind)), + Name: gatewayapiv1.ObjectName(transformerServiceKey.Name), + Namespace: (*gatewayapiv1.Namespace)(ptr.To(serviceKey.Namespace)), + Port: (*gatewayapiv1.PortNumber)(ptr.To(int32(constants.CommonDefaultHttpPort))), + }, + Weight: ptr.To(int32(1)), + }, + }, + }, + Timeouts: &gatewayapiv1.HTTPRouteTimeouts{ + Request: ptr.To(gatewayapiv1.Duration("30s")), + }, + }, + }, + CommonRouteSpec: gatewayapiv1.CommonRouteSpec{ + ParentRefs: []gatewayapiv1.ParentReference{ + { + Name: gatewayapiv1.ObjectName(kserveGateway.Name), + Kind: ptr.To(gatewayapiv1.Kind(constants.GatewayKind)), + Group: (*gatewayapiv1.Group)(&gatewayapiv1.GroupVersion.Group), + Namespace: ptr.To(gatewayapiv1.Namespace(kserveGateway.Namespace)), + }, + }, + }, + }, + } + Expect(actualTransformerHttpRoute.Spec).To(BeComparableTo(expectedTransformerHttpRoute.Spec)) + + // Mark the Ingress as accepted to make isvc ready + httpRouteStatus := gatewayapiv1.HTTPRouteStatus{ + RouteStatus: gatewayapiv1.RouteStatus{ + Parents: []gatewayapiv1.RouteParentStatus{ + { + ParentRef: gatewayapiv1.ParentReference{ + Name: gatewayapiv1.ObjectName(kserveGateway.Name), + Kind: ptr.To(gatewayapiv1.Kind(constants.GatewayKind)), + Group: (*gatewayapiv1.Group)(&gatewayapiv1.GroupVersion.Group), + Namespace: ptr.To(gatewayapiv1.Namespace(kserveGateway.Namespace)), + }, + ControllerName: "istio.io/gateway-controller", + Conditions: []metav1.Condition{ + { + Type: string(gatewayapiv1.ListenerConditionAccepted), + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "Route was valid", + LastTransitionTime: metav1.Now(), + }, + }, + }, + }, + }, + } + actualPredictorHttpRoute.Status = httpRouteStatus + Expect(k8sClient.Status().Update(context.Background(), actualPredictorHttpRoute)).NotTo(HaveOccurred()) + actualTransformerHttpRoute.Status = httpRouteStatus + Expect(k8sClient.Status().Update(context.Background(), actualTransformerHttpRoute)).NotTo(HaveOccurred()) + actualToplevelHttpRoute.Status = httpRouteStatus + Expect(k8sClient.Status().Update(context.Background(), actualToplevelHttpRoute)).NotTo(HaveOccurred()) + + // verify if InferenceService status is updated + expectedIsvcStatus := v1beta1.InferenceServiceStatus{ + Status: duckv1.Status{ + Conditions: duckv1.Conditions{ + { + Type: v1beta1.IngressReady, + Status: "True", + }, + { + Type: v1beta1.PredictorReady, + Status: "True", + }, + { + Type: apis.ConditionReady, + Status: "True", + }, + { + Type: v1beta1.TransformerReady, + Status: "True", + Severity: "Info", + }, + }, + }, + URL: &apis.URL{ + Scheme: "http", + Host: fmt.Sprintf("%s-%s.example.com", serviceKey.Name, serviceKey.Namespace), + }, + Address: &duckv1.Addressable{ + URL: &apis.URL{ + Scheme: "http", + Host: fmt.Sprintf("%s.%s.svc.cluster.local", transformerServiceKey.Name, serviceKey.Namespace), + }, + }, + Components: map[v1beta1.ComponentType]v1beta1.ComponentStatusSpec{ + v1beta1.PredictorComponent: { + LatestCreatedRevision: "", + URL: &apis.URL{ + Scheme: "http", + Host: fmt.Sprintf("%s-%s.example.com", predictorServiceKey.Name, serviceKey.Namespace), + }, + }, + v1beta1.TransformerComponent: { + LatestCreatedRevision: "", + URL: &apis.URL{ + Scheme: "http", + Host: fmt.Sprintf("%s-%s.example.com", transformerServiceKey.Name, serviceKey.Namespace), + }, + }, + }, + ModelStatus: v1beta1.ModelStatus{ + TransitionStatus: "InProgress", + ModelRevisionStates: &v1beta1.ModelRevisionStates{TargetModelState: "Pending"}, + }, + } + Eventually(func() string { + isvc := &v1beta1.InferenceService{} + if err := k8sClient.Get(context.TODO(), serviceKey, isvc); err != nil { + return err.Error() + } + return cmp.Diff(&expectedIsvcStatus, &isvc.Status, cmpopts.IgnoreTypes(apis.VolatileTime{})) + }, timeout).Should(BeEmpty()) + + // check predictor HPA + var cpuUtilization int32 = 75 + var stabilizationWindowSeconds int32 = 0 + selectPolicy := autoscalingv2.MaxChangePolicySelect + actualPredictorHPA := &autoscalingv2.HorizontalPodAutoscaler{} + predictorHPAKey := types.NamespacedName{ + Name: predictorServiceKey.Name, + Namespace: serviceKey.Namespace, + } + Eventually(func() error { return k8sClient.Get(context.TODO(), predictorHPAKey, actualPredictorHPA) }, timeout). + Should(Succeed()) + expectedPredictorHPA := &autoscalingv2.HorizontalPodAutoscaler{ + Spec: autoscalingv2.HorizontalPodAutoscalerSpec{ + ScaleTargetRef: autoscalingv2.CrossVersionObjectReference{ + APIVersion: "apps/v1", + Kind: "Deployment", + Name: predictorServiceKey.Name, + }, + MinReplicas: &minReplicas, + MaxReplicas: maxReplicas, + Metrics: []autoscalingv2.MetricSpec{ + { + Type: autoscalingv2.ResourceMetricSourceType, + Resource: &autoscalingv2.ResourceMetricSource{ + Name: corev1.ResourceCPU, + Target: autoscalingv2.MetricTarget{ + Type: "Utilization", + AverageUtilization: &cpuUtilization, + }, + }, + }, + }, + Behavior: &autoscalingv2.HorizontalPodAutoscalerBehavior{ + ScaleUp: &autoscalingv2.HPAScalingRules{ + StabilizationWindowSeconds: &stabilizationWindowSeconds, + SelectPolicy: &selectPolicy, + Policies: []autoscalingv2.HPAScalingPolicy{ + { + Type: "Pods", + Value: 4, + PeriodSeconds: 15, + }, + { + Type: "Percent", + Value: 100, + PeriodSeconds: 15, + }, + }, + }, + ScaleDown: &autoscalingv2.HPAScalingRules{ + StabilizationWindowSeconds: nil, + SelectPolicy: &selectPolicy, + Policies: []autoscalingv2.HPAScalingPolicy{ + { + Type: "Percent", + Value: 100, + PeriodSeconds: 15, + }, + }, + }, + }, + }, + } + Expect(actualPredictorHPA.Spec).To(BeComparableTo(expectedPredictorHPA.Spec)) + + // check transformer HPA + actualTransformerHPA := &autoscalingv2.HorizontalPodAutoscaler{} + transformerHPAKey := types.NamespacedName{ + Name: transformerServiceKey.Name, + Namespace: serviceKey.Namespace, + } + Eventually(func() error { return k8sClient.Get(context.TODO(), transformerHPAKey, actualTransformerHPA) }, timeout). + Should(Succeed()) + expectedTransformerHPA := &autoscalingv2.HorizontalPodAutoscaler{ + Spec: autoscalingv2.HorizontalPodAutoscalerSpec{ + ScaleTargetRef: autoscalingv2.CrossVersionObjectReference{ + APIVersion: "apps/v1", + Kind: "Deployment", + Name: transformerServiceKey.Name, + }, + MinReplicas: &transformerMinReplicas, + MaxReplicas: transformerMaxReplicas, + Metrics: []autoscalingv2.MetricSpec{ + { + Type: autoscalingv2.ResourceMetricSourceType, + Resource: &autoscalingv2.ResourceMetricSource{ + Name: corev1.ResourceCPU, + Target: autoscalingv2.MetricTarget{ + Type: "Utilization", + AverageUtilization: &transformerCpuUtilization, + }, + }, + }, + }, + Behavior: &autoscalingv2.HorizontalPodAutoscalerBehavior{ + ScaleUp: &autoscalingv2.HPAScalingRules{ + StabilizationWindowSeconds: &transformerStabilizationWindowSeconds, + SelectPolicy: &transformerSelectPolicy, + Policies: []autoscalingv2.HPAScalingPolicy{ + { + Type: "Pods", + Value: 4, + PeriodSeconds: 15, + }, + { + Type: "Percent", + Value: 100, + PeriodSeconds: 15, + }, + }, + }, + ScaleDown: &autoscalingv2.HPAScalingRules{ + StabilizationWindowSeconds: nil, + SelectPolicy: &transformerSelectPolicy, + Policies: []autoscalingv2.HPAScalingPolicy{ + { + Type: "Percent", + Value: 100, + PeriodSeconds: 15, + }, + }, + }, + }, + }, + } + Expect(actualTransformerHPA.Spec).To(BeComparableTo(expectedTransformerHPA.Spec)) + }) + }) + Context("When creating inference service with raw kube path based routing predictor and explainer", func() { + configs := map[string]string{ + "explainers": `{ + "art": { + "image": "kserve/art-explainer", + "defaultImageVersion": "latest" + } + }`, + "ingress": `{ + "enableGatewayApi": true, + "kserveIngressGateway": "kserve/kserve-ingress-gateway", + "ingressGateway": "knative-serving/knative-ingress-gateway", + "localGateway": "knative-serving/knative-local-gateway", + "localGatewayService": "knative-local-gateway.istio-system.svc.cluster.local", + "additionalIngressDomains": ["additional.example.com"], + "ingressDomain": "example.com", + "pathTemplate": "/serving/{{ .Namespace }}/{{ .Name }}" + }`, + "storageInitializer": `{ + "image" : "kserve/storage-initializer:latest", + "memoryRequest": "100Mi", + "memoryLimit": "1Gi", + "cpuRequest": "100m", + "cpuLimit": "1", + "CaBundleConfigMapName": "", + "caBundleVolumeMountPath": "/etc/ssl/custom-certs", + "enableDirectPvcVolumeMount": false + }`, + } + + It("Should have httproute/service/deployment/hpa created for explainer and predictor", func() { + By("By creating a new InferenceService") + // Create configmap + configMap := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: constants.InferenceServiceConfigMapName, + Namespace: constants.KServeNamespace, + }, + Data: configs, + } + Expect(k8sClient.Create(context.TODO(), configMap)).NotTo(HaveOccurred()) + defer k8sClient.Delete(context.TODO(), configMap) + // Create ServingRuntime + servingRuntime := &v1alpha1.ServingRuntime{ + ObjectMeta: metav1.ObjectMeta{ + Name: "tf-serving-raw", + Namespace: "default", + }, + Spec: v1alpha1.ServingRuntimeSpec{ + SupportedModelFormats: []v1alpha1.SupportedModelFormat{ + { + Name: "tensorflow", + Version: proto.String("1"), + AutoSelect: proto.Bool(true), + }, + }, + ServingRuntimePodSpec: v1alpha1.ServingRuntimePodSpec{ + Containers: []corev1.Container{ + { + Name: "kserve-container", + Image: "tensorflow/serving:1.14.0", + Command: []string{"/usr/bin/tensorflow_model_server"}, + Args: []string{ + "--port=9000", + "--rest_api_port=8080", + "--model_base_path=/mnt/models", + "--rest_api_timeout_in_ms=60000", + }, + Resources: defaultResource, + }, + }, + }, + Disabled: proto.Bool(false), + }, + } + k8sClient.Create(context.TODO(), servingRuntime) + defer k8sClient.Delete(context.TODO(), servingRuntime) + serviceName := "raw-foo-exp-path" + namespace := "default" + expectedRequest := reconcile.Request{NamespacedName: types.NamespacedName{Name: serviceName, Namespace: namespace}} + serviceKey := expectedRequest.NamespacedName + predictorServiceKey := types.NamespacedName{ + Name: constants.PredictorServiceName(serviceName), + Namespace: namespace, + } + explainerServiceKey := types.NamespacedName{ + Name: constants.ExplainerServiceName(serviceName), + Namespace: namespace, + } + storageUri := "s3://test/mnist/export" + var minReplicas int32 = 1 + var maxReplicas int32 = 3 + var explainerMinReplicas int32 = 1 + var explainerMaxReplicas int32 = 2 + var explainerCpuUtilization int32 = 80 + var explainerStabilizationWindowSeconds int32 = 0 + ExplainerSelectPolicy := autoscalingv2.MaxChangePolicySelect + ctx := context.Background() + isvc := &v1beta1.InferenceService{ + ObjectMeta: metav1.ObjectMeta{ + Name: serviceKey.Name, + Namespace: serviceKey.Namespace, + Annotations: map[string]string{ + "serving.kserve.io/deploymentMode": "RawDeployment", + "serving.kserve.io/autoscalerClass": "hpa", + "serving.kserve.io/metrics": "cpu", + "serving.kserve.io/targetUtilizationPercentage": "75", + }, + }, + Spec: v1beta1.InferenceServiceSpec{ + Predictor: v1beta1.PredictorSpec{ + ComponentExtensionSpec: v1beta1.ComponentExtensionSpec{ + MinReplicas: ptr.To(minReplicas), + MaxReplicas: maxReplicas, + }, + Tensorflow: &v1beta1.TFServingSpec{ + PredictorExtensionSpec: v1beta1.PredictorExtensionSpec{ + StorageURI: &storageUri, + RuntimeVersion: proto.String("1.14.0"), + Container: corev1.Container{ + Name: constants.InferenceServiceContainerName, + Resources: defaultResource, + }, + }, + }, + }, + Explainer: &v1beta1.ExplainerSpec{ + ART: &v1beta1.ARTExplainerSpec{ + Type: v1beta1.ARTSquareAttackExplainer, + ExplainerExtensionSpec: v1beta1.ExplainerExtensionSpec{ + Config: map[string]string{"nb_classes": "10"}, + Container: corev1.Container{ + Name: constants.InferenceServiceContainerName, + Resources: defaultResource, + }, + }, + }, + ComponentExtensionSpec: v1beta1.ComponentExtensionSpec{ + MinReplicas: ptr.To(explainerMinReplicas), + MaxReplicas: explainerMaxReplicas, + ScaleTarget: ptr.To(explainerCpuUtilization), + TimeoutSeconds: ptr.To(int64(30)), + }, + }, + }, + } + isvcConfigMap, err := v1beta1.GetInferenceServiceConfigMap(context.Background(), clientset) + Expect(err).NotTo(HaveOccurred()) + isvcConfig, err := v1beta1.NewInferenceServicesConfig(isvcConfigMap) + Expect(err).NotTo(HaveOccurred()) + isvc.DefaultInferenceService(isvcConfig, nil, &v1beta1.SecurityConfig{AutoMountServiceAccountToken: false}, nil) + Expect(k8sClient.Create(ctx, isvc)).Should(Succeed()) + defer k8sClient.Delete(ctx, isvc) + + inferenceService := &v1beta1.InferenceService{} + + Eventually(func() bool { + err := k8sClient.Get(ctx, serviceKey, inferenceService) + return err == nil + }, timeout, interval).Should(BeTrue()) + + // check predictor deployment + actualPredictorDeployment := &appsv1.Deployment{} + predictorDeploymentKey := types.NamespacedName{ + Name: predictorServiceKey.Name, + Namespace: serviceKey.Namespace, + } + Eventually(func() error { return k8sClient.Get(context.TODO(), predictorDeploymentKey, actualPredictorDeployment) }, timeout). + Should(Succeed()) + var replicas int32 = 1 + var revisionHistory int32 = 10 + var progressDeadlineSeconds int32 = 600 + var gracePeriod int64 = 30 + expectedPredictorDeployment := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: predictorDeploymentKey.Name, + Namespace: predictorDeploymentKey.Namespace, + }, + Spec: appsv1.DeploymentSpec{ + Replicas: &replicas, + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "isvc." + predictorDeploymentKey.Name, + }, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Name: predictorDeploymentKey.Name, + Namespace: "default", + Labels: map[string]string{ + "app": "isvc." + predictorDeploymentKey.Name, + constants.KServiceComponentLabel: constants.Predictor.String(), + constants.InferenceServicePodLabelKey: serviceName, + }, + Annotations: map[string]string{ + constants.StorageInitializerSourceUriInternalAnnotationKey: *isvc.Spec.Predictor.Model.StorageURI, + "serving.kserve.io/deploymentMode": "RawDeployment", + "serving.kserve.io/autoscalerClass": "hpa", + "serving.kserve.io/metrics": "cpu", + "serving.kserve.io/targetUtilizationPercentage": "75", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Image: "tensorflow/serving:" + + *isvc.Spec.Predictor.Model.RuntimeVersion, + Name: constants.InferenceServiceContainerName, + Command: []string{v1beta1.TensorflowEntrypointCommand}, + Args: []string{ + "--port=" + v1beta1.TensorflowServingGRPCPort, + "--rest_api_port=" + v1beta1.TensorflowServingRestPort, + "--model_base_path=" + constants.DefaultModelLocalMountPath, + "--rest_api_timeout_in_ms=60000", + }, + Resources: defaultResource, + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + TCPSocket: &corev1.TCPSocketAction{ + Port: intstr.IntOrString{ + IntVal: 8080, + }, + }, + }, + InitialDelaySeconds: 0, + TimeoutSeconds: 1, + PeriodSeconds: 10, + SuccessThreshold: 1, + FailureThreshold: 3, + }, + TerminationMessagePath: "/dev/termination-log", + TerminationMessagePolicy: "File", + ImagePullPolicy: "IfNotPresent", + }, + }, + SchedulerName: "default-scheduler", + RestartPolicy: "Always", + TerminationGracePeriodSeconds: &gracePeriod, + DNSPolicy: "ClusterFirst", + SecurityContext: &corev1.PodSecurityContext{ + SELinuxOptions: nil, + WindowsOptions: nil, + RunAsUser: nil, + RunAsGroup: nil, + RunAsNonRoot: nil, + SupplementalGroups: nil, + FSGroup: nil, + Sysctls: nil, + FSGroupChangePolicy: nil, + SeccompProfile: nil, + }, + AutomountServiceAccountToken: proto.Bool(false), + }, + }, + Strategy: appsv1.DeploymentStrategy{ + Type: "RollingUpdate", + RollingUpdate: &appsv1.RollingUpdateDeployment{ + MaxUnavailable: &intstr.IntOrString{Type: 1, IntVal: 0, StrVal: "25%"}, + MaxSurge: &intstr.IntOrString{Type: 1, IntVal: 0, StrVal: "25%"}, + }, + }, + RevisionHistoryLimit: &revisionHistory, + ProgressDeadlineSeconds: &progressDeadlineSeconds, + }, + } + Expect(actualPredictorDeployment.Spec).To(BeComparableTo(expectedPredictorDeployment.Spec)) + + // check Explainer deployment + actualExplainerDeployment := &appsv1.Deployment{} + explainerDeploymentKey := types.NamespacedName{ + Name: explainerServiceKey.Name, + Namespace: serviceKey.Namespace, + } + Eventually(func() error { + return k8sClient.Get(context.TODO(), explainerDeploymentKey, actualExplainerDeployment) + }, timeout). + Should(Succeed()) + expectedExplainerDeployment := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: explainerDeploymentKey.Name, + Namespace: explainerDeploymentKey.Namespace, + }, + Spec: appsv1.DeploymentSpec{ + Replicas: &replicas, + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "isvc." + explainerDeploymentKey.Name, + }, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Name: explainerDeploymentKey.Name, + Namespace: "default", + Labels: map[string]string{ + "app": "isvc." + explainerDeploymentKey.Name, + constants.KServiceComponentLabel: constants.Explainer.String(), + constants.InferenceServicePodLabelKey: serviceName, + }, + Annotations: map[string]string{ + "serving.kserve.io/deploymentMode": "RawDeployment", + "serving.kserve.io/autoscalerClass": "hpa", + "serving.kserve.io/metrics": "cpu", + "serving.kserve.io/targetUtilizationPercentage": "75", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Image: "kserve/art-explainer:latest", + Name: constants.InferenceServiceContainerName, + Args: []string{ + "--model_name", + serviceKey.Name, + "--http_port", + "8080", + "--predictor_host", + fmt.Sprintf("%s.%s", predictorServiceKey.Name, predictorServiceKey.Namespace), + "--adversary_type", + "SquareAttack", + "--nb_classes", + "10", + }, + Resources: defaultResource, + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + TCPSocket: &corev1.TCPSocketAction{ + Port: intstr.IntOrString{ + IntVal: 8080, + }, + }, + }, + InitialDelaySeconds: 0, + TimeoutSeconds: 1, + PeriodSeconds: 10, + SuccessThreshold: 1, + FailureThreshold: 3, + }, + TerminationMessagePath: "/dev/termination-log", + TerminationMessagePolicy: "File", + ImagePullPolicy: "IfNotPresent", + }, + }, + SchedulerName: "default-scheduler", + RestartPolicy: "Always", + TerminationGracePeriodSeconds: &gracePeriod, + DNSPolicy: "ClusterFirst", + SecurityContext: &corev1.PodSecurityContext{ + SELinuxOptions: nil, + WindowsOptions: nil, + RunAsUser: nil, + RunAsGroup: nil, + RunAsNonRoot: nil, + SupplementalGroups: nil, + FSGroup: nil, + Sysctls: nil, + FSGroupChangePolicy: nil, + SeccompProfile: nil, + }, + AutomountServiceAccountToken: proto.Bool(false), + }, + }, + Strategy: appsv1.DeploymentStrategy{ + Type: "RollingUpdate", + RollingUpdate: &appsv1.RollingUpdateDeployment{ + MaxUnavailable: &intstr.IntOrString{Type: 1, IntVal: 0, StrVal: "25%"}, + MaxSurge: &intstr.IntOrString{Type: 1, IntVal: 0, StrVal: "25%"}, + }, + }, + RevisionHistoryLimit: &revisionHistory, + ProgressDeadlineSeconds: &progressDeadlineSeconds, + }, + } + Expect(actualExplainerDeployment.Spec).To(BeComparableTo(expectedExplainerDeployment.Spec)) + + // check predictor service + actualPredictorService := &corev1.Service{} + Eventually(func() error { return k8sClient.Get(context.TODO(), predictorServiceKey, actualPredictorService) }, timeout). + Should(Succeed()) + expectedPredictorService := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: predictorServiceKey.Name, + Namespace: predictorServiceKey.Namespace, + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Name: predictorServiceKey.Name, + Protocol: "TCP", + Port: 80, + TargetPort: intstr.IntOrString{Type: 0, IntVal: 8080, StrVal: ""}, + }, + }, + Type: "ClusterIP", + SessionAffinity: "None", + Selector: map[string]string{ + "app": "isvc." + predictorServiceKey.Name, + }, + }, + } + actualPredictorService.Spec.ClusterIP = "" + actualPredictorService.Spec.ClusterIPs = nil + actualPredictorService.Spec.IPFamilies = nil + actualPredictorService.Spec.IPFamilyPolicy = nil + actualPredictorService.Spec.InternalTrafficPolicy = nil + Expect(actualPredictorService.Spec).To(BeComparableTo(expectedPredictorService.Spec)) + + // check Explainer service + actualExplainerService := &corev1.Service{} + Eventually(func() error { return k8sClient.Get(context.TODO(), explainerServiceKey, actualExplainerService) }, timeout). + Should(Succeed()) + expectedExplainerService := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: explainerServiceKey.Name, + Namespace: explainerServiceKey.Namespace, + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Name: explainerServiceKey.Name, + Protocol: "TCP", + Port: 80, + TargetPort: intstr.IntOrString{Type: 0, IntVal: 8080, StrVal: ""}, + }, + }, + Type: "ClusterIP", + SessionAffinity: "None", + Selector: map[string]string{ + "app": "isvc." + explainerServiceKey.Name, + }, + }, + } + actualExplainerService.Spec.ClusterIP = "" + actualExplainerService.Spec.ClusterIPs = nil + actualExplainerService.Spec.IPFamilies = nil + actualExplainerService.Spec.IPFamilyPolicy = nil + actualExplainerService.Spec.InternalTrafficPolicy = nil + Expect(actualExplainerService.Spec).To(BeComparableTo(expectedExplainerService.Spec)) + + // update deployment status to make isvc ready + updatedPredictorDeployment := actualPredictorDeployment.DeepCopy() + updatedPredictorDeployment.Status.Conditions = []appsv1.DeploymentCondition{ + { + Type: appsv1.DeploymentAvailable, + Status: corev1.ConditionTrue, + }, + } + Expect(k8sClient.Status().Update(context.TODO(), updatedPredictorDeployment)).NotTo(HaveOccurred()) + updatedExplainerDeployment := actualExplainerDeployment.DeepCopy() + updatedExplainerDeployment.Status.Conditions = []appsv1.DeploymentCondition{ + { + Type: appsv1.DeploymentAvailable, + Status: corev1.ConditionTrue, + }, + } + Expect(k8sClient.Status().Update(context.TODO(), updatedExplainerDeployment)).NotTo(HaveOccurred()) + + // check http route + actualToplevelHttpRoute := &gatewayapiv1.HTTPRoute{} + Eventually(func() error { + return k8sClient.Get(context.TODO(), types.NamespacedName{ + Name: serviceKey.Name, + Namespace: serviceKey.Namespace, + }, actualToplevelHttpRoute) + }, timeout). + Should(Succeed()) + topLevelHost := fmt.Sprintf("%s-%s.%s", serviceKey.Name, serviceKey.Namespace, "example.com") + prefixUrlPath := fmt.Sprintf("/serving/%s/%s", serviceKey.Namespace, serviceKey.Name) + expectedToplevelHttpRoute := gatewayapiv1.HTTPRoute{ + Spec: gatewayapiv1.HTTPRouteSpec{ + Hostnames: []gatewayapiv1.Hostname{gatewayapiv1.Hostname(topLevelHost), "additional.example.com", "example.com"}, + Rules: []gatewayapiv1.HTTPRouteRule{ + { + Matches: []gatewayapiv1.HTTPRouteMatch{ + { + Path: &gatewayapiv1.HTTPPathMatch{ + Type: ptr.To(gatewayapiv1.PathMatchRegularExpression), + Value: ptr.To(constants.ExplainPrefix()), + }, + }, + }, + Filters: []gatewayapiv1.HTTPRouteFilter{ + { + Type: gatewayapiv1.HTTPRouteFilterRequestHeaderModifier, + RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ + Set: []gatewayapiv1.HTTPHeader{ + { + Name: constants.IsvcNameHeader, + Value: serviceKey.Name, + }, + { + Name: constants.IsvcNamespaceHeader, + Value: serviceKey.Namespace, + }, + }, + }, + }, + }, + BackendRefs: []gatewayapiv1.HTTPBackendRef{ + { + BackendRef: gatewayapiv1.BackendRef{ + BackendObjectReference: gatewayapiv1.BackendObjectReference{ + Group: (*gatewayapiv1.Group)(ptr.To("")), + Kind: ptr.To(gatewayapiv1.Kind(constants.ServiceKind)), + Name: gatewayapiv1.ObjectName(explainerServiceKey.Name), + Namespace: (*gatewayapiv1.Namespace)(ptr.To(serviceKey.Namespace)), + Port: (*gatewayapiv1.PortNumber)(ptr.To(int32(constants.CommonDefaultHttpPort))), + }, + Weight: ptr.To(int32(1)), + }, + }, + }, + Timeouts: &gatewayapiv1.HTTPRouteTimeouts{ + Request: ptr.To(gatewayapiv1.Duration("30s")), + }, + }, + { + Matches: []gatewayapiv1.HTTPRouteMatch{ + { + Path: &gatewayapiv1.HTTPPathMatch{ + Type: ptr.To(gatewayapiv1.PathMatchRegularExpression), + Value: ptr.To(constants.FallbackPrefix()), + }, + }, + }, + Filters: []gatewayapiv1.HTTPRouteFilter{ + { + Type: gatewayapiv1.HTTPRouteFilterRequestHeaderModifier, + RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ + Set: []gatewayapiv1.HTTPHeader{ + { + Name: constants.IsvcNameHeader, + Value: serviceKey.Name, + }, + { + Name: constants.IsvcNamespaceHeader, + Value: serviceKey.Namespace, + }, + }, + }, + }, + }, + BackendRefs: []gatewayapiv1.HTTPBackendRef{ + { + BackendRef: gatewayapiv1.BackendRef{ + BackendObjectReference: gatewayapiv1.BackendObjectReference{ + Group: (*gatewayapiv1.Group)(ptr.To("")), + Kind: ptr.To(gatewayapiv1.Kind(constants.ServiceKind)), + Name: gatewayapiv1.ObjectName(predictorServiceKey.Name), + Namespace: (*gatewayapiv1.Namespace)(ptr.To(serviceKey.Namespace)), + Port: (*gatewayapiv1.PortNumber)(ptr.To(int32(constants.CommonDefaultHttpPort))), + }, + Weight: ptr.To(int32(1)), + }, + }, + }, + Timeouts: &gatewayapiv1.HTTPRouteTimeouts{ + Request: ptr.To(gatewayapiv1.Duration("60s")), + }, + }, + { + Matches: []gatewayapiv1.HTTPRouteMatch{ + { + Path: &gatewayapiv1.HTTPPathMatch{ + Type: ptr.To(gatewayapiv1.PathMatchRegularExpression), + Value: ptr.To(prefixUrlPath + constants.PathBasedExplainPrefix()), + }, + }, + }, + Filters: []gatewayapiv1.HTTPRouteFilter{ + { + Type: gatewayapiv1.HTTPRouteFilterRequestHeaderModifier, + RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ + Set: []gatewayapiv1.HTTPHeader{ + { + Name: constants.IsvcNameHeader, + Value: serviceKey.Name, + }, + { + Name: constants.IsvcNamespaceHeader, + Value: serviceKey.Namespace, + }, + }, + }, + }, + }, + BackendRefs: []gatewayapiv1.HTTPBackendRef{ + { + BackendRef: gatewayapiv1.BackendRef{ + BackendObjectReference: gatewayapiv1.BackendObjectReference{ + Group: (*gatewayapiv1.Group)(ptr.To("")), + Kind: ptr.To(gatewayapiv1.Kind(constants.ServiceKind)), + Name: gatewayapiv1.ObjectName(explainerServiceKey.Name), + Namespace: (*gatewayapiv1.Namespace)(ptr.To(serviceKey.Namespace)), + Port: (*gatewayapiv1.PortNumber)(ptr.To(int32(constants.CommonDefaultHttpPort))), + }, + Weight: ptr.To(int32(1)), + }, + }, + }, + Timeouts: &gatewayapiv1.HTTPRouteTimeouts{ + Request: ptr.To(gatewayapiv1.Duration("30s")), + }, + }, + { + Matches: []gatewayapiv1.HTTPRouteMatch{ + { + Path: &gatewayapiv1.HTTPPathMatch{ + Type: ptr.To(gatewayapiv1.PathMatchRegularExpression), + Value: ptr.To(prefixUrlPath + "/"), + }, + }, + }, + Filters: []gatewayapiv1.HTTPRouteFilter{ + { + Type: gatewayapiv1.HTTPRouteFilterRequestHeaderModifier, + RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ + Set: []gatewayapiv1.HTTPHeader{ + { + Name: constants.IsvcNameHeader, + Value: serviceKey.Name, + }, + { + Name: constants.IsvcNamespaceHeader, + Value: serviceKey.Namespace, + }, + }, + }, + }, + }, + BackendRefs: []gatewayapiv1.HTTPBackendRef{ + { + BackendRef: gatewayapiv1.BackendRef{ + BackendObjectReference: gatewayapiv1.BackendObjectReference{ + Group: (*gatewayapiv1.Group)(ptr.To("")), + Kind: ptr.To(gatewayapiv1.Kind(constants.ServiceKind)), + Name: gatewayapiv1.ObjectName(predictorServiceKey.Name), + Namespace: (*gatewayapiv1.Namespace)(ptr.To(serviceKey.Namespace)), + Port: (*gatewayapiv1.PortNumber)(ptr.To(int32(constants.CommonDefaultHttpPort))), + }, + Weight: ptr.To(int32(1)), + }, + }, + }, + Timeouts: &gatewayapiv1.HTTPRouteTimeouts{ + Request: ptr.To(gatewayapiv1.Duration("60s")), + }, + }, + }, + CommonRouteSpec: gatewayapiv1.CommonRouteSpec{ + ParentRefs: []gatewayapiv1.ParentReference{ + { + Name: gatewayapiv1.ObjectName(kserveGateway.Name), + Kind: ptr.To(gatewayapiv1.Kind(constants.GatewayKind)), + Group: (*gatewayapiv1.Group)(&gatewayapiv1.GroupVersion.Group), + Namespace: ptr.To(gatewayapiv1.Namespace(kserveGateway.Namespace)), + }, + }, + }, }, } - Expect(k8sClient.Status().Update(context.TODO(), updatedDeployment)).NotTo(HaveOccurred()) + Expect(actualToplevelHttpRoute.Spec).To(BeComparableTo(expectedToplevelHttpRoute.Spec)) - //check ingress - pathType := netv1.PathTypePrefix - actualIngress := &netv1.Ingress{} - predictorIngressKey := types.NamespacedName{Name: serviceKey.Name, - Namespace: serviceKey.Namespace} - Eventually(func() error { return k8sClient.Get(context.TODO(), predictorIngressKey, actualIngress) }, timeout). + actualPredictorHttpRoute := &gatewayapiv1.HTTPRoute{} + Eventually(func() error { + return k8sClient.Get(context.TODO(), types.NamespacedName{ + Name: predictorServiceKey.Name, + Namespace: serviceKey.Namespace, + }, actualPredictorHttpRoute) + }, timeout). Should(Succeed()) - expectedIngress := netv1.Ingress{ - Spec: netv1.IngressSpec{ - Rules: []netv1.IngressRule{ + predictorHost := fmt.Sprintf("%s-%s.%s", predictorServiceKey.Name, serviceKey.Namespace, "example.com") + expectedPredictorHttpRoute := gatewayapiv1.HTTPRoute{ + Spec: gatewayapiv1.HTTPRouteSpec{ + Hostnames: []gatewayapiv1.Hostname{gatewayapiv1.Hostname(predictorHost)}, + Rules: []gatewayapiv1.HTTPRouteRule{ { - Host: fmt.Sprintf("%s-default.example.com", serviceName), - IngressRuleValue: netv1.IngressRuleValue{ - HTTP: &netv1.HTTPIngressRuleValue{ - Paths: []netv1.HTTPIngressPath{ - { - Path: "/", - PathType: &pathType, - Backend: netv1.IngressBackend{ - Service: &netv1.IngressServiceBackend{ - Name: fmt.Sprintf("%s-predictor", serviceName), - Port: netv1.ServiceBackendPort{ - Number: 80, - }, - }, + Matches: []gatewayapiv1.HTTPRouteMatch{ + { + Path: &gatewayapiv1.HTTPPathMatch{ + Type: ptr.To(gatewayapiv1.PathMatchRegularExpression), + Value: ptr.To(constants.FallbackPrefix()), + }, + }, + }, + Filters: []gatewayapiv1.HTTPRouteFilter{ + { + Type: gatewayapiv1.HTTPRouteFilterRequestHeaderModifier, + RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ + Set: []gatewayapiv1.HTTPHeader{ + { + Name: constants.IsvcNameHeader, + Value: serviceKey.Name, + }, + { + Name: constants.IsvcNamespaceHeader, + Value: serviceKey.Namespace, }, }, }, }, }, + BackendRefs: []gatewayapiv1.HTTPBackendRef{ + { + BackendRef: gatewayapiv1.BackendRef{ + BackendObjectReference: gatewayapiv1.BackendObjectReference{ + Group: (*gatewayapiv1.Group)(ptr.To("")), + Kind: ptr.To(gatewayapiv1.Kind(constants.ServiceKind)), + Name: gatewayapiv1.ObjectName(predictorServiceKey.Name), + Namespace: (*gatewayapiv1.Namespace)(ptr.To(serviceKey.Namespace)), + Port: (*gatewayapiv1.PortNumber)(ptr.To(int32(constants.CommonDefaultHttpPort))), + }, + Weight: ptr.To(int32(1)), + }, + }, + }, + Timeouts: &gatewayapiv1.HTTPRouteTimeouts{ + Request: ptr.To(gatewayapiv1.Duration("60s")), + }, + }, + }, + CommonRouteSpec: gatewayapiv1.CommonRouteSpec{ + ParentRefs: []gatewayapiv1.ParentReference{ + { + Name: gatewayapiv1.ObjectName(kserveGateway.Name), + Kind: ptr.To(gatewayapiv1.Kind(constants.GatewayKind)), + Group: (*gatewayapiv1.Group)(&gatewayapiv1.GroupVersion.Group), + Namespace: ptr.To(gatewayapiv1.Namespace(kserveGateway.Namespace)), + }, }, + }, + }, + } + Expect(actualPredictorHttpRoute.Spec).To(BeComparableTo(expectedPredictorHttpRoute.Spec)) + + actualExplainerHttpRoute := &gatewayapiv1.HTTPRoute{} + Eventually(func() error { + return k8sClient.Get(context.TODO(), types.NamespacedName{ + Name: explainerServiceKey.Name, + Namespace: serviceKey.Namespace, + }, actualExplainerHttpRoute) + }, timeout). + Should(Succeed()) + explainerHost := fmt.Sprintf("%s-%s.%s", explainerServiceKey.Name, serviceKey.Namespace, "example.com") + expectedExplainerHttpRoute := gatewayapiv1.HTTPRoute{ + Spec: gatewayapiv1.HTTPRouteSpec{ + Hostnames: []gatewayapiv1.Hostname{gatewayapiv1.Hostname(explainerHost)}, + Rules: []gatewayapiv1.HTTPRouteRule{ { - Host: fmt.Sprintf("%s-predictor-default.example.com", serviceName), - IngressRuleValue: netv1.IngressRuleValue{ - HTTP: &netv1.HTTPIngressRuleValue{ - Paths: []netv1.HTTPIngressPath{ - { - Path: "/", - PathType: &pathType, - Backend: netv1.IngressBackend{ - Service: &netv1.IngressServiceBackend{ - Name: fmt.Sprintf("%s-predictor", serviceName), - Port: netv1.ServiceBackendPort{ - Number: 80, - }, - }, + Matches: []gatewayapiv1.HTTPRouteMatch{ + { + Path: &gatewayapiv1.HTTPPathMatch{ + Type: ptr.To(gatewayapiv1.PathMatchRegularExpression), + Value: ptr.To(constants.FallbackPrefix()), + }, + }, + }, + Filters: []gatewayapiv1.HTTPRouteFilter{ + { + Type: gatewayapiv1.HTTPRouteFilterRequestHeaderModifier, + RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ + Set: []gatewayapiv1.HTTPHeader{ + { + Name: constants.IsvcNameHeader, + Value: serviceKey.Name, }, + { + Name: constants.IsvcNamespaceHeader, + Value: serviceKey.Namespace, + }, + }, + }, + }, + }, + BackendRefs: []gatewayapiv1.HTTPBackendRef{ + { + BackendRef: gatewayapiv1.BackendRef{ + BackendObjectReference: gatewayapiv1.BackendObjectReference{ + Group: (*gatewayapiv1.Group)(ptr.To("")), + Kind: ptr.To(gatewayapiv1.Kind(constants.ServiceKind)), + Name: gatewayapiv1.ObjectName(explainerServiceKey.Name), + Namespace: (*gatewayapiv1.Namespace)(ptr.To(serviceKey.Namespace)), + Port: (*gatewayapiv1.PortNumber)(ptr.To(int32(constants.CommonDefaultHttpPort))), }, + Weight: ptr.To(int32(1)), }, }, }, + Timeouts: &gatewayapiv1.HTTPRouteTimeouts{ + Request: ptr.To(gatewayapiv1.Duration("30s")), + }, + }, + }, + CommonRouteSpec: gatewayapiv1.CommonRouteSpec{ + ParentRefs: []gatewayapiv1.ParentReference{ + { + Name: gatewayapiv1.ObjectName(kserveGateway.Name), + Kind: ptr.To(gatewayapiv1.Kind(constants.GatewayKind)), + Group: (*gatewayapiv1.Group)(&gatewayapiv1.GroupVersion.Group), + Namespace: ptr.To(gatewayapiv1.Namespace(kserveGateway.Namespace)), + }, }, }, }, } - Expect(actualIngress.Spec).To(Equal(expectedIngress.Spec)) + Expect(actualExplainerHttpRoute.Spec).To(BeComparableTo(expectedExplainerHttpRoute.Spec)) + + // Mark the Ingress as accepted to make isvc ready + httpRouteStatus := gatewayapiv1.HTTPRouteStatus{ + RouteStatus: gatewayapiv1.RouteStatus{ + Parents: []gatewayapiv1.RouteParentStatus{ + { + ParentRef: gatewayapiv1.ParentReference{ + Name: gatewayapiv1.ObjectName(kserveGateway.Name), + Kind: ptr.To(gatewayapiv1.Kind(constants.GatewayKind)), + Group: (*gatewayapiv1.Group)(&gatewayapiv1.GroupVersion.Group), + Namespace: ptr.To(gatewayapiv1.Namespace(kserveGateway.Namespace)), + }, + ControllerName: "istio.io/gateway-controller", + Conditions: []metav1.Condition{ + { + Type: string(gatewayapiv1.ListenerConditionAccepted), + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "Route was valid", + LastTransitionTime: metav1.Now(), + }, + }, + }, + }, + }, + } + actualPredictorHttpRoute.Status = httpRouteStatus + Expect(k8sClient.Status().Update(context.Background(), actualPredictorHttpRoute)).NotTo(HaveOccurred()) + actualExplainerHttpRoute.Status = httpRouteStatus + Expect(k8sClient.Status().Update(context.Background(), actualExplainerHttpRoute)).NotTo(HaveOccurred()) + actualToplevelHttpRoute.Status = httpRouteStatus + Expect(k8sClient.Status().Update(context.Background(), actualToplevelHttpRoute)).NotTo(HaveOccurred()) + // verify if InferenceService status is updated expectedIsvcStatus := v1beta1.InferenceServiceStatus{ Status: duckv1.Status{ Conditions: duckv1.Conditions{ + { + Type: v1beta1.ExplainerReady, + Status: "True", + Severity: "Info", + }, { Type: v1beta1.IngressReady, Status: "True", @@ -1606,12 +6862,12 @@ var _ = Describe("v1beta1 inference service controller", func() { }, URL: &apis.URL{ Scheme: "http", - Host: fmt.Sprintf("%s-default.example.com", serviceName), + Host: fmt.Sprintf("%s-%s.example.com", serviceKey.Name, serviceKey.Namespace), }, Address: &duckv1.Addressable{ URL: &apis.URL{ Scheme: "http", - Host: fmt.Sprintf("%s-predictor.%s.svc.cluster.local", serviceKey.Name, serviceKey.Namespace), + Host: fmt.Sprintf("%s.%s.svc.cluster.local", predictorServiceKey.Name, serviceKey.Namespace), }, }, Components: map[v1beta1.ComponentType]v1beta1.ComponentStatusSpec{ @@ -1619,7 +6875,14 @@ var _ = Describe("v1beta1 inference service controller", func() { LatestCreatedRevision: "", URL: &apis.URL{ Scheme: "http", - Host: fmt.Sprintf("%s-predictor-default.example.com", serviceName), + Host: fmt.Sprintf("%s-%s.example.com", predictorServiceKey.Name, serviceKey.Namespace), + }, + }, + v1beta1.ExplainerComponent: { + LatestCreatedRevision: "", + URL: &apis.URL{ + Scheme: "http", + Host: fmt.Sprintf("%s-%s.example.com", explainerServiceKey.Name, serviceKey.Namespace), }, }, }, @@ -1636,23 +6899,23 @@ var _ = Describe("v1beta1 inference service controller", func() { return cmp.Diff(&expectedIsvcStatus, &isvc.Status, cmpopts.IgnoreTypes(apis.VolatileTime{})) }, timeout).Should(BeEmpty()) - //check HPA - var minReplicas int32 = 1 - var maxReplicas int32 = 3 + // check predictor HPA var cpuUtilization int32 = 75 var stabilizationWindowSeconds int32 = 0 selectPolicy := autoscalingv2.MaxChangePolicySelect - actualHPA := &autoscalingv2.HorizontalPodAutoscaler{} - predictorHPAKey := types.NamespacedName{Name: constants.PredictorServiceName(serviceKey.Name), - Namespace: serviceKey.Namespace} - Eventually(func() error { return k8sClient.Get(context.TODO(), predictorHPAKey, actualHPA) }, timeout). + actualPredictorHPA := &autoscalingv2.HorizontalPodAutoscaler{} + predictorHPAKey := types.NamespacedName{ + Name: predictorServiceKey.Name, + Namespace: serviceKey.Namespace, + } + Eventually(func() error { return k8sClient.Get(context.TODO(), predictorHPAKey, actualPredictorHPA) }, timeout). Should(Succeed()) - expectedHPA := &autoscalingv2.HorizontalPodAutoscaler{ + expectedPredictorHPA := &autoscalingv2.HorizontalPodAutoscaler{ Spec: autoscalingv2.HorizontalPodAutoscalerSpec{ ScaleTargetRef: autoscalingv2.CrossVersionObjectReference{ APIVersion: "apps/v1", Kind: "Deployment", - Name: constants.PredictorServiceName(serviceKey.Name), + Name: predictorServiceKey.Name, }, MinReplicas: &minReplicas, MaxReplicas: maxReplicas, @@ -1660,7 +6923,7 @@ var _ = Describe("v1beta1 inference service controller", func() { { Type: autoscalingv2.ResourceMetricSourceType, Resource: &autoscalingv2.ResourceMetricSource{ - Name: v1.ResourceCPU, + Name: corev1.ResourceCPU, Target: autoscalingv2.MetricTarget{ Type: "Utilization", AverageUtilization: &cpuUtilization, @@ -1699,30 +6962,101 @@ var _ = Describe("v1beta1 inference service controller", func() { }, }, } - Expect(actualHPA.Spec).To(Equal(expectedHPA.Spec)) + Expect(actualPredictorHPA.Spec).To(BeComparableTo(expectedPredictorHPA.Spec)) + + // check Explainer HPA + actualExplainerHPA := &autoscalingv2.HorizontalPodAutoscaler{} + explainerHPAKey := types.NamespacedName{ + Name: explainerServiceKey.Name, + Namespace: serviceKey.Namespace, + } + Eventually(func() error { return k8sClient.Get(context.TODO(), explainerHPAKey, actualExplainerHPA) }, timeout). + Should(Succeed()) + expectedExplainerHPA := &autoscalingv2.HorizontalPodAutoscaler{ + Spec: autoscalingv2.HorizontalPodAutoscalerSpec{ + ScaleTargetRef: autoscalingv2.CrossVersionObjectReference{ + APIVersion: "apps/v1", + Kind: "Deployment", + Name: explainerServiceKey.Name, + }, + MinReplicas: &explainerMinReplicas, + MaxReplicas: explainerMaxReplicas, + Metrics: []autoscalingv2.MetricSpec{ + { + Type: autoscalingv2.ResourceMetricSourceType, + Resource: &autoscalingv2.ResourceMetricSource{ + Name: corev1.ResourceCPU, + Target: autoscalingv2.MetricTarget{ + Type: "Utilization", + AverageUtilization: &explainerCpuUtilization, + }, + }, + }, + }, + Behavior: &autoscalingv2.HorizontalPodAutoscalerBehavior{ + ScaleUp: &autoscalingv2.HPAScalingRules{ + StabilizationWindowSeconds: &explainerStabilizationWindowSeconds, + SelectPolicy: &ExplainerSelectPolicy, + Policies: []autoscalingv2.HPAScalingPolicy{ + { + Type: "Pods", + Value: 4, + PeriodSeconds: 15, + }, + { + Type: "Percent", + Value: 100, + PeriodSeconds: 15, + }, + }, + }, + ScaleDown: &autoscalingv2.HPAScalingRules{ + StabilizationWindowSeconds: nil, + SelectPolicy: &ExplainerSelectPolicy, + Policies: []autoscalingv2.HPAScalingPolicy{ + { + Type: "Percent", + Value: 100, + PeriodSeconds: 15, + }, + }, + }, + }, + }, + } + Expect(actualExplainerHPA.Spec).To(BeComparableTo(expectedExplainerHPA.Spec)) }) }) - Context("When creating inference service with raw kube predictor with domain template", func() { + Context("When creating inference service with raw kube predictor with gateway api disabled", func() { configs := map[string]string{ "explainers": `{ - "alibi": { - "image": "kfserving/alibi-explainer", - "defaultImageVersion": "latest" - } - }`, + "alibi": { + "image": "kserve/alibi-explainer", + "defaultImageVersion": "latest" + } + }`, "ingress": `{ - "ingressGateway": "knative-serving/knative-ingress-gateway", - "localGateway": "knative-serving/knative-local-gateway", - "localGatewayService": "knative-local-gateway.istio-system.svc.cluster.local", - "ingressDomain": "example.com", - "domainTemplate": "{{ .Name }}.{{ .Namespace }}.{{ .IngressDomain }}" - }`, + "enableGatewayAPI": false, + "ingressGateway": "knative-serving/knative-ingress-gateway", + "localGateway": "knative-serving/knative-local-gateway", + "localGatewayService": "knative-local-gateway.istio-system.svc.cluster.local" + }`, + "storageInitializer": `{ + "image" : "kserve/storage-initializer:latest", + "memoryRequest": "100Mi", + "memoryLimit": "1Gi", + "cpuRequest": "100m", + "cpuLimit": "1", + "CaBundleConfigMapName": "", + "caBundleVolumeMountPath": "/etc/ssl/custom-certs", + "enableDirectPvcVolumeMount": false + }`, } It("Should have ingress/service/deployment/hpa created", func() { By("By creating a new InferenceService") // Create configmap - var configMap = &v1.ConfigMap{ + configMap := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: constants.InferenceServiceConfigMapName, Namespace: constants.KServeNamespace, @@ -1746,7 +7080,7 @@ var _ = Describe("v1beta1 inference service controller", func() { }, }, ServingRuntimePodSpec: v1alpha1.ServingRuntimePodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Name: "kserve-container", Image: "tensorflow/serving:1.14.0", @@ -1766,11 +7100,10 @@ var _ = Describe("v1beta1 inference service controller", func() { } k8sClient.Create(context.TODO(), servingRuntime) defer k8sClient.Delete(context.TODO(), servingRuntime) - // Create InferenceService - serviceName := "model" - var expectedRequest = reconcile.Request{NamespacedName: types.NamespacedName{Name: serviceName, Namespace: "default"}} - var serviceKey = expectedRequest.NamespacedName - var storageUri = "s3://test/mnist/export" + serviceName := "raw-foo" + expectedRequest := reconcile.Request{NamespacedName: types.NamespacedName{Name: serviceName, Namespace: "default"}} + serviceKey := expectedRequest.NamespacedName + storageUri := "s3://test/mnist/export" ctx := context.Background() isvc := &v1beta1.InferenceService{ ObjectMeta: metav1.ObjectMeta{ @@ -1786,14 +7119,14 @@ var _ = Describe("v1beta1 inference service controller", func() { Spec: v1beta1.InferenceServiceSpec{ Predictor: v1beta1.PredictorSpec{ ComponentExtensionSpec: v1beta1.ComponentExtensionSpec{ - MinReplicas: v1beta1.GetIntReference(1), + MinReplicas: ptr.To(int32(1)), MaxReplicas: 3, }, Tensorflow: &v1beta1.TFServingSpec{ PredictorExtensionSpec: v1beta1.PredictorExtensionSpec{ StorageURI: &storageUri, RuntimeVersion: proto.String("1.14.0"), - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, Resources: defaultResource, }, @@ -1809,15 +7142,14 @@ var _ = Describe("v1beta1 inference service controller", func() { Eventually(func() bool { err := k8sClient.Get(ctx, serviceKey, inferenceService) - if err != nil { - return false - } - return true + return err == nil }, timeout, interval).Should(BeTrue()) actualDeployment := &appsv1.Deployment{} - predictorDeploymentKey := types.NamespacedName{Name: constants.PredictorServiceName(serviceKey.Name), - Namespace: serviceKey.Namespace} + predictorDeploymentKey := types.NamespacedName{ + Name: constants.PredictorServiceName(serviceKey.Name), + Namespace: serviceKey.Namespace, + } Eventually(func() error { return k8sClient.Get(context.TODO(), predictorDeploymentKey, actualDeployment) }, timeout). Should(Succeed()) var replicas int32 = 1 @@ -1836,7 +7168,7 @@ var _ = Describe("v1beta1 inference service controller", func() { "app": "isvc." + predictorDeploymentKey.Name, }, }, - Template: v1.PodTemplateSpec{ + Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Name: predictorDeploymentKey.Name, Namespace: "default", @@ -1853,8 +7185,8 @@ var _ = Describe("v1beta1 inference service controller", func() { "serving.kserve.io/targetUtilizationPercentage": "75", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Image: "tensorflow/serving:" + *isvc.Spec.Predictor.Model.RuntimeVersion, @@ -1867,9 +7199,9 @@ var _ = Describe("v1beta1 inference service controller", func() { "--rest_api_timeout_in_ms=60000", }, Resources: defaultResource, - ReadinessProbe: &v1.Probe{ - ProbeHandler: v1.ProbeHandler{ - TCPSocket: &v1.TCPSocketAction{ + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + TCPSocket: &corev1.TCPSocketAction{ Port: intstr.IntOrString{ IntVal: 8080, }, @@ -1890,7 +7222,7 @@ var _ = Describe("v1beta1 inference service controller", func() { RestartPolicy: "Always", TerminationGracePeriodSeconds: &gracePeriod, DNSPolicy: "ClusterFirst", - SecurityContext: &v1.PodSecurityContext{ + SecurityContext: &corev1.PodSecurityContext{ SELinuxOptions: nil, WindowsOptions: nil, RunAsUser: nil, @@ -1918,20 +7250,22 @@ var _ = Describe("v1beta1 inference service controller", func() { } Expect(actualDeployment.Spec).To(Equal(expectedDeployment.Spec)) - //check service - actualService := &v1.Service{} - predictorServiceKey := types.NamespacedName{Name: constants.PredictorServiceName(serviceKey.Name), - Namespace: serviceKey.Namespace} + // check service + actualService := &corev1.Service{} + predictorServiceKey := types.NamespacedName{ + Name: constants.PredictorServiceName(serviceKey.Name), + Namespace: serviceKey.Namespace, + } Eventually(func() error { return k8sClient.Get(context.TODO(), predictorServiceKey, actualService) }, timeout). Should(Succeed()) - expectedService := &v1.Service{ + expectedService := &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: predictorServiceKey.Name, Namespace: predictorServiceKey.Namespace, }, - Spec: v1.ServiceSpec{ - Ports: []v1.ServicePort{ + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ { Name: constants.PredictorServiceName(serviceName), Protocol: "TCP", @@ -1942,7 +7276,7 @@ var _ = Describe("v1beta1 inference service controller", func() { Type: "ClusterIP", SessionAffinity: "None", Selector: map[string]string{ - "app": fmt.Sprintf("isvc.%s", constants.PredictorServiceName(serviceName)), + "app": "isvc." + constants.PredictorServiceName(serviceName), }, }, } @@ -1953,28 +7287,30 @@ var _ = Describe("v1beta1 inference service controller", func() { actualService.Spec.InternalTrafficPolicy = nil Expect(actualService.Spec).To(Equal(expectedService.Spec)) - //check isvc status + // check isvc status updatedDeployment := actualDeployment.DeepCopy() updatedDeployment.Status.Conditions = []appsv1.DeploymentCondition{ { Type: appsv1.DeploymentAvailable, - Status: v1.ConditionTrue, + Status: corev1.ConditionTrue, }, } Expect(k8sClient.Status().Update(context.TODO(), updatedDeployment)).NotTo(HaveOccurred()) - //check ingress + // check ingress pathType := netv1.PathTypePrefix actualIngress := &netv1.Ingress{} - predictorIngressKey := types.NamespacedName{Name: serviceKey.Name, - Namespace: serviceKey.Namespace} + predictorIngressKey := types.NamespacedName{ + Name: serviceKey.Name, + Namespace: serviceKey.Namespace, + } Eventually(func() error { return k8sClient.Get(context.TODO(), predictorIngressKey, actualIngress) }, timeout). Should(Succeed()) expectedIngress := netv1.Ingress{ Spec: netv1.IngressSpec{ Rules: []netv1.IngressRule{ { - Host: fmt.Sprintf("%s.%s.%s", serviceName, serviceKey.Namespace, domain), + Host: "raw-foo-default.example.com", IngressRuleValue: netv1.IngressRuleValue{ HTTP: &netv1.HTTPIngressRuleValue{ Paths: []netv1.HTTPIngressPath{ @@ -1983,7 +7319,7 @@ var _ = Describe("v1beta1 inference service controller", func() { PathType: &pathType, Backend: netv1.IngressBackend{ Service: &netv1.IngressServiceBackend{ - Name: fmt.Sprintf("%s-predictor", serviceName), + Name: serviceName + "-predictor", Port: netv1.ServiceBackendPort{ Number: 80, }, @@ -1995,7 +7331,7 @@ var _ = Describe("v1beta1 inference service controller", func() { }, }, { - Host: fmt.Sprintf("%s-predictor.%s.%s", serviceName, serviceKey.Namespace, domain), + Host: "raw-foo-predictor-default.example.com", IngressRuleValue: netv1.IngressRuleValue{ HTTP: &netv1.HTTPIngressRuleValue{ Paths: []netv1.HTTPIngressPath{ @@ -2004,7 +7340,7 @@ var _ = Describe("v1beta1 inference service controller", func() { PathType: &pathType, Backend: netv1.IngressBackend{ Service: &netv1.IngressServiceBackend{ - Name: fmt.Sprintf("%s-predictor", serviceName), + Name: serviceName + "-predictor", Port: netv1.ServiceBackendPort{ Number: 80, }, @@ -2039,7 +7375,7 @@ var _ = Describe("v1beta1 inference service controller", func() { }, URL: &apis.URL{ Scheme: "http", - Host: fmt.Sprintf("%s.%s.%s", serviceName, serviceKey.Namespace, domain), + Host: "raw-foo-default.example.com", }, Address: &duckv1.Addressable{ URL: &apis.URL{ @@ -2052,7 +7388,7 @@ var _ = Describe("v1beta1 inference service controller", func() { LatestCreatedRevision: "", URL: &apis.URL{ Scheme: "http", - Host: fmt.Sprintf("%s-predictor.%s.%s", serviceName, serviceKey.Namespace, domain), + Host: "raw-foo-predictor-default.example.com", }, }, }, @@ -2069,15 +7405,17 @@ var _ = Describe("v1beta1 inference service controller", func() { return cmp.Diff(&expectedIsvcStatus, &isvc.Status, cmpopts.IgnoreTypes(apis.VolatileTime{})) }, timeout).Should(BeEmpty()) - //check HPA + // check HPA var minReplicas int32 = 1 var maxReplicas int32 = 3 var cpuUtilization int32 = 75 var stabilizationWindowSeconds int32 = 0 selectPolicy := autoscalingv2.MaxChangePolicySelect actualHPA := &autoscalingv2.HorizontalPodAutoscaler{} - predictorHPAKey := types.NamespacedName{Name: constants.PredictorServiceName(serviceKey.Name), - Namespace: serviceKey.Namespace} + predictorHPAKey := types.NamespacedName{ + Name: constants.PredictorServiceName(serviceKey.Name), + Namespace: serviceKey.Namespace, + } Eventually(func() error { return k8sClient.Get(context.TODO(), predictorHPAKey, actualHPA) }, timeout). Should(Succeed()) expectedHPA := &autoscalingv2.HorizontalPodAutoscaler{ @@ -2093,7 +7431,7 @@ var _ = Describe("v1beta1 inference service controller", func() { { Type: autoscalingv2.ResourceMetricSourceType, Resource: &autoscalingv2.ResourceMetricSource{ - Name: v1.ResourceCPU, + Name: corev1.ResourceCPU, Target: autoscalingv2.MetricTarget{ Type: "Utilization", AverageUtilization: &cpuUtilization, @@ -2137,7 +7475,6 @@ var _ = Describe("v1beta1 inference service controller", func() { }) Context("When creating inference service with raw kube predictor with workerSpec", func() { var ( - ctx context.Context serviceKey types.NamespacedName storageUri string isvc *v1beta1.InferenceService @@ -2148,28 +7485,28 @@ var _ = Describe("v1beta1 inference service controller", func() { actualWorkerDeployment := &appsv1.Deployment{} BeforeEach(func() { - ctx = context.Background() + ctx := context.Background() storageUri = "pvc://llama-3-8b-pvc/hf/8b_instruction_tuned" // Create a ConfigMap configs := map[string]string{ "ingress": `{ - "ingressGateway": "knative-serving/knative-ingress-gateway", - "localGateway": "knative-serving/knative-local-gateway", - "localGatewayService": "knative-local-gateway.istio-system.svc.cluster.local" - }`, + "ingressGateway": "knative-serving/knative-ingress-gateway", + "localGateway": "knative-serving/knative-local-gateway", + "localGatewayService": "knative-local-gateway.istio-system.svc.cluster.local" + }`, "storageInitializer": `{ - "image" : "kserve/storage-initializer:latest", - "memoryRequest": "100Mi", - "memoryLimit": "1Gi", - "cpuRequest": "100m", - "cpuLimit": "1", - "CaBundleConfigMapName": "", - "caBundleVolumeMountPath": "/etc/ssl/custom-certs", - "enableDirectPvcVolumeMount": false - }`, - } - configMap := &v1.ConfigMap{ + "image" : "kserve/storage-initializer:latest", + "memoryRequest": "100Mi", + "memoryLimit": "1Gi", + "cpuRequest": "100m", + "cpuLimit": "1", + "CaBundleConfigMapName": "", + "caBundleVolumeMountPath": "/etc/ssl/custom-certs", + "enableDirectPvcVolumeMount": false + }`, + } + configMap := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: constants.InferenceServiceConfigMapName, Namespace: constants.KServeNamespace, @@ -2196,7 +7533,7 @@ var _ = Describe("v1beta1 inference service controller", func() { }, }, ServingRuntimePodSpec: v1alpha1.ServingRuntimePodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, Image: "kserve/huggingfaceserver:latest", @@ -2209,10 +7546,10 @@ var _ = Describe("v1beta1 inference service controller", func() { }, }, WorkerSpec: &v1alpha1.WorkerSpec{ - PipelineParallelSize: intPtr(2), - TensorParallelSize: intPtr(1), + PipelineParallelSize: ptr.To(2), + TensorParallelSize: ptr.To(1), ServingRuntimePodSpec: v1alpha1.ServingRuntimePodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Name: constants.WorkerContainerName, Image: "kserve/huggingfaceserver:latest", @@ -2234,6 +7571,8 @@ var _ = Describe("v1beta1 inference service controller", func() { }) }) It("Should have services/deployments for head/worker without an autoscaler when workerSpec is set in isvc", func() { + ctx, cancel := context.WithCancel(context.Background()) + DeferCleanup(cancel) By("creating a new InferenceService") isvcName := "raw-huggingface-multinode-1" predictorDeploymentName := constants.PredictorServiceName(isvcName) @@ -2285,10 +7624,10 @@ var _ = Describe("v1beta1 inference service controller", func() { }, timeout, interval).Should(BeTrue()) // Verify deployments details - verifyPipelineParallelSizeDeployments(actualDefaultDeployment, actualWorkerDeployment, "2", int32Ptr(1)) + verifyPipelineParallelSizeDeployments(actualDefaultDeployment, actualWorkerDeployment, "2", ptr.To(int32(1))) // Check Services - actualService := &v1.Service{} + actualService := &corev1.Service{} headServiceName := constants.GeHeadServiceName(isvcName+"-predictor", "1") defaultServiceName := isvcName + "-predictor" expectedHeadServiceName := types.NamespacedName{Name: headServiceName, Namespace: isvcNamespace} @@ -2315,18 +7654,22 @@ var _ = Describe("v1beta1 inference service controller", func() { // Verify there if the default autoscaler(HPA) is not created. actualHPA := &autoscalingv2.HorizontalPodAutoscaler{} - predictorHPAKey := types.NamespacedName{Name: constants.PredictorServiceName(isvcName), - Namespace: isvcNamespace} + predictorHPAKey := types.NamespacedName{ + Name: constants.PredictorServiceName(isvcName), + Namespace: isvcNamespace, + } Eventually(func() error { err := k8sClient.Get(context.TODO(), predictorHPAKey, actualHPA) if err != nil && apierr.IsNotFound(err) { return nil } - return fmt.Errorf("expected IsNotFound error, but got %v", err) + return fmt.Errorf("expected IsNotFound error, but got %w", err) }, timeout).Should(Succeed()) }) It("Should use WorkerSpec.PipelineParallelSize value in isvc when it is set", func() { + ctx, cancel := context.WithCancel(context.Background()) + DeferCleanup(cancel) By("By creating a new InferenceService") isvcName := "raw-huggingface-multinode-4" predictorDeploymentName := constants.PredictorServiceName(isvcName) @@ -2353,7 +7696,7 @@ var _ = Describe("v1beta1 inference service controller", func() { }, }, WorkerSpec: &v1beta1.WorkerSpec{ - PipelineParallelSize: intPtr(3), + PipelineParallelSize: ptr.To(3), }, }, }, @@ -2380,9 +7723,11 @@ var _ = Describe("v1beta1 inference service controller", func() { }, timeout, interval).Should(BeTrue()) // Verify deployments details - verifyPipelineParallelSizeDeployments(actualDefaultDeployment, actualWorkerDeployment, "3", int32Ptr(2)) + verifyPipelineParallelSizeDeployments(actualDefaultDeployment, actualWorkerDeployment, "3", ptr.To(int32(2))) }) It("Should use WorkerSpec.TensorParallelSize value in isvc when it is set", func() { + ctx, cancel := context.WithCancel(context.Background()) + DeferCleanup(cancel) By("creating a new InferenceService") isvcName := "raw-huggingface-multinode-5" predictorDeploymentName := constants.PredictorServiceName(isvcName) @@ -2410,7 +7755,7 @@ var _ = Describe("v1beta1 inference service controller", func() { }, }, WorkerSpec: &v1beta1.WorkerSpec{ - TensorParallelSize: intPtr(3), + TensorParallelSize: ptr.To(3), }, }, }, @@ -2433,6 +7778,88 @@ var _ = Describe("v1beta1 inference service controller", func() { // Verify deployments details verifyTensorParallelSizeDeployments(actualDefaultDeployment, actualWorkerDeployment, "3", constants.NvidiaGPUResourceType) }) + It("Should not set nil to replicas when multinode isvc(external autoscaler) is updated", func() { + ctx, cancel := context.WithCancel(context.Background()) + DeferCleanup(cancel) + By("creating a new InferenceService") + isvcName := "raw-huggingface-multinode-6" + predictorDeploymentName := constants.PredictorServiceName(isvcName) + workerDeploymentName := constants.PredictorWorkerServiceName(isvcName) + serviceKey = types.NamespacedName{Name: isvcName, Namespace: isvcNamespace} + + // Create a infereceService + isvc = &v1beta1.InferenceService{ + ObjectMeta: metav1.ObjectMeta{ + Name: isvcName, + Namespace: isvcNamespace, + Annotations: map[string]string{ + "serving.kserve.io/deploymentMode": "RawDeployment", + "serving.kserve.io/autoscalerClass": "external", + }, + }, + Spec: v1beta1.InferenceServiceSpec{ + Predictor: v1beta1.PredictorSpec{ + Model: &v1beta1.ModelSpec{ + ModelFormat: v1beta1.ModelFormat{ + Name: "huggingface", + }, + PredictorExtensionSpec: v1beta1.PredictorExtensionSpec{ + StorageURI: &storageUri, + }, + }, + WorkerSpec: &v1beta1.WorkerSpec{ + PipelineParallelSize: ptr.To(3), + }, + }, + }, + } + Expect(k8sClient.Create(ctx, isvc)).Should(Succeed()) + DeferCleanup(func() { + k8sClient.Delete(ctx, isvc) + }) + + // Verify if predictor deployment (default deployment) is created + Eventually(func() bool { + return k8sClient.Get(ctx, types.NamespacedName{Name: predictorDeploymentName, Namespace: isvcNamespace}, actualDefaultDeployment) == nil + }, timeout, interval).Should(BeTrue()) + + // Verify if worker node deployment is created. + Eventually(func() bool { + return k8sClient.Get(ctx, types.NamespacedName{Name: workerDeploymentName, Namespace: isvcNamespace}, actualWorkerDeployment) == nil + }, timeout, interval).Should(BeTrue()) + + // Verify deployments details + verifyPipelineParallelSizeDeployments(actualDefaultDeployment, actualWorkerDeployment, "3", ptr.To(int32(2))) + + // Update a infereceService + By("updating the InferenceService") + updatedIsvc := &v1beta1.InferenceService{} + k8sClient.Get(ctx, types.NamespacedName{Name: isvc.Name, Namespace: isvcNamespace}, updatedIsvc) + // Add label to isvc to create a new rs + if updatedIsvc.Labels == nil { + updatedIsvc.Labels = make(map[string]string) + } + updatedIsvc.Labels["newLabel"] = "test" + + err := k8sClient.Update(ctx, updatedIsvc) + Expect(err).ShouldNot(HaveOccurred(), "Failed to update InferenceService with new label") + + // Verify if predictor deployment (default deployment) has replicas + Eventually(func() bool { + if err := k8sClient.Get(ctx, types.NamespacedName{Name: predictorDeploymentName, Namespace: isvcNamespace}, actualDefaultDeployment); err == nil { + return actualDefaultDeployment.Spec.Replicas != nil && *actualDefaultDeployment.Spec.Replicas == 1 + } + return false + }, timeout, interval).Should(BeTrue()) + + // Verify if worker node deployment has replicas + Eventually(func() bool { + if err := k8sClient.Get(ctx, types.NamespacedName{Name: workerDeploymentName, Namespace: isvcNamespace}, actualWorkerDeployment); err == nil { + return actualWorkerDeployment.Spec.Replicas != nil && *actualWorkerDeployment.Spec.Replicas == 2 + } + return false + }, timeout, interval).Should(BeTrue()) + }) }) }) @@ -2453,7 +7880,7 @@ func verifyPipelineParallelSizeDeployments(actualDefaultDeployment *appsv1.Deplo Expect(actualWorkerDeployment.Spec.Replicas).Should(Equal(replicas)) } -func verifyTensorParallelSizeDeployments(actualDefaultDeployment *appsv1.Deployment, actualWorkerDeployment *appsv1.Deployment, tensorParallelSize string, gpuResourceType v1.ResourceName) { +func verifyTensorParallelSizeDeployments(actualDefaultDeployment *appsv1.Deployment, actualWorkerDeployment *appsv1.Deployment, tensorParallelSize string, gpuResourceType corev1.ResourceName) { gpuResourceQuantity := resource.MustParse(tensorParallelSize) // default deployment if tensorParallelSizeEnvValue, exists := utils.GetEnvVarValue(actualDefaultDeployment.Spec.Template.Spec.Containers[0].Env, constants.TensorParallelSizeEnvName); exists { @@ -2464,14 +7891,7 @@ func verifyTensorParallelSizeDeployments(actualDefaultDeployment *appsv1.Deploym Expect(actualDefaultDeployment.Spec.Template.Spec.Containers[0].Resources.Limits[gpuResourceType]).Should(Equal(gpuResourceQuantity)) Expect(actualDefaultDeployment.Spec.Template.Spec.Containers[0].Resources.Requests[gpuResourceType]).Should(Equal(gpuResourceQuantity)) - //worker node deployment + // worker node deployment Expect(actualWorkerDeployment.Spec.Template.Spec.Containers[0].Resources.Limits[gpuResourceType]).Should(Equal(gpuResourceQuantity)) Expect(actualWorkerDeployment.Spec.Template.Spec.Containers[0].Resources.Requests[gpuResourceType]).Should(Equal(gpuResourceQuantity)) } -func int32Ptr(i int32) *int32 { - return &i -} - -func intPtr(i int) *int { - return &i -} diff --git a/pkg/controller/v1beta1/inferenceservice/reconcilers/autoscaler/autoscaler_reconciler.go b/pkg/controller/v1beta1/inferenceservice/reconcilers/autoscaler/autoscaler_reconciler.go index 5a3a86d0b6c..14fa76f3a73 100644 --- a/pkg/controller/v1beta1/inferenceservice/reconcilers/autoscaler/autoscaler_reconciler.go +++ b/pkg/controller/v1beta1/inferenceservice/reconcilers/autoscaler/autoscaler_reconciler.go @@ -17,27 +17,29 @@ limitations under the License. package autoscaler import ( + "context" "fmt" - "github.com/kserve/kserve/pkg/apis/serving/v1beta1" - "github.com/kserve/kserve/pkg/constants" - hpa "github.com/kserve/kserve/pkg/controller/v1beta1/inferenceservice/reconcilers/hpa" autoscalingv2 "k8s.io/api/autoscaling/v2" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/kserve/kserve/pkg/apis/serving/v1beta1" + "github.com/kserve/kserve/pkg/constants" + hpa "github.com/kserve/kserve/pkg/controller/v1beta1/inferenceservice/reconcilers/hpa" ) // Autoscaler Interface implemented by all autoscalers type Autoscaler interface { - Reconcile() (*autoscalingv2.HorizontalPodAutoscaler, error) + Reconcile(ctx context.Context) (*autoscalingv2.HorizontalPodAutoscaler, error) SetControllerReferences(owner metav1.Object, scheme *runtime.Scheme) error } // NoOpAutoscaler Autoscaler that does nothing. Can be used to disable creation of autoscaler resources. type NoOpAutoscaler struct{} -func (*NoOpAutoscaler) Reconcile() (*autoscalingv2.HorizontalPodAutoscaler, error) { +func (*NoOpAutoscaler) Reconcile(ctx context.Context) (*autoscalingv2.HorizontalPodAutoscaler, error) { return nil, nil } @@ -56,7 +58,8 @@ type AutoscalerReconciler struct { func NewAutoscalerReconciler(client client.Client, scheme *runtime.Scheme, componentMeta metav1.ObjectMeta, - componentExt *v1beta1.ComponentExtensionSpec) (*AutoscalerReconciler, error) { + componentExt *v1beta1.ComponentExtensionSpec, +) (*AutoscalerReconciler, error) { as, err := createAutoscaler(client, scheme, componentMeta, componentExt) if err != nil { return nil, err @@ -80,20 +83,21 @@ func getAutoscalerClass(metadata metav1.ObjectMeta) constants.AutoscalerClassTyp func createAutoscaler(client client.Client, scheme *runtime.Scheme, componentMeta metav1.ObjectMeta, - componentExt *v1beta1.ComponentExtensionSpec) (Autoscaler, error) { + componentExt *v1beta1.ComponentExtensionSpec, +) (Autoscaler, error) { ac := getAutoscalerClass(componentMeta) switch ac { case constants.AutoscalerClassHPA, constants.AutoscalerClassExternal: - return hpa.NewHPAReconciler(client, scheme, componentMeta, componentExt), nil + return hpa.NewHPAReconciler(client, scheme, componentMeta, componentExt) default: return nil, fmt.Errorf("unknown autoscaler class type: %v", ac) } } // Reconcile ... -func (r *AutoscalerReconciler) Reconcile() error { +func (r *AutoscalerReconciler) Reconcile(ctx context.Context) error { // reconcile Autoscaler - _, err := r.Autoscaler.Reconcile() + _, err := r.Autoscaler.Reconcile(ctx) if err != nil { return err } diff --git a/pkg/controller/v1beta1/inferenceservice/reconcilers/autoscaler/autoscaler_reconciler_test.go b/pkg/controller/v1beta1/inferenceservice/reconcilers/autoscaler/autoscaler_reconciler_test.go index ccc41fd1563..f5ff5b999e7 100644 --- a/pkg/controller/v1beta1/inferenceservice/reconcilers/autoscaler/autoscaler_reconciler_test.go +++ b/pkg/controller/v1beta1/inferenceservice/reconcilers/autoscaler/autoscaler_reconciler_test.go @@ -17,11 +17,12 @@ limitations under the License. package autoscaler import ( + "testing" + "github.com/google/go-cmp/cmp" - "github.com/kserve/kserve/pkg/constants" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "testing" + "github.com/kserve/kserve/pkg/constants" ) func TestGetAutoscalerClass(t *testing.T) { diff --git a/pkg/controller/v1beta1/inferenceservice/reconcilers/cabundleconfigmap/cabundle_configmap_reconciler.go b/pkg/controller/v1beta1/inferenceservice/reconcilers/cabundleconfigmap/cabundle_configmap_reconciler.go index 4108e02d3b3..d34bc74a5e6 100644 --- a/pkg/controller/v1beta1/inferenceservice/reconcilers/cabundleconfigmap/cabundle_configmap_reconciler.go +++ b/pkg/controller/v1beta1/inferenceservice/reconcilers/cabundleconfigmap/cabundle_configmap_reconciler.go @@ -32,7 +32,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" logf "sigs.k8s.io/controller-runtime/pkg/log" - kservev1beta1 "github.com/kserve/kserve/pkg/apis/serving/v1beta1" + "github.com/kserve/kserve/pkg/apis/serving/v1beta1" "github.com/kserve/kserve/pkg/constants" "github.com/kserve/kserve/pkg/webhook/admission/pod" ) @@ -53,11 +53,11 @@ func NewCaBundleConfigMapReconciler(client client.Client, clientset kubernetes.I } } -func (c *CaBundleConfigMapReconciler) Reconcile(isvc *kservev1beta1.InferenceService) error { +func (c *CaBundleConfigMapReconciler) Reconcile(ctx context.Context, isvc *v1beta1.InferenceService) error { log.Info("Reconciling CaBundleConfigMap", "namespace", isvc.Namespace) - isvcConfigMap, err := c.clientset.CoreV1().ConfigMaps(constants.KServeNamespace).Get(context.TODO(), constants.InferenceServiceConfigMapName, metav1.GetOptions{}) + isvcConfigMap, err := v1beta1.GetInferenceServiceConfigMap(ctx, c.clientset) if err != nil { - log.Error(err, "failed to find config map", "name", constants.InferenceServiceConfigMapName) + log.Error(err, "unable to get configmap", "name", constants.InferenceServiceConfigMapName, "namespace", constants.KServeNamespace) return err } @@ -73,25 +73,25 @@ func (c *CaBundleConfigMapReconciler) Reconcile(isvc *kservev1beta1.InferenceSer if storageInitializerConfig.CaBundleConfigMapName == "" { return nil } else { - newCaBundleConfigMap, err = c.getCabundleConfigMapForUserNS(storageInitializerConfig.CaBundleConfigMapName, constants.KServeNamespace, isvc.Namespace) + newCaBundleConfigMap, err = c.getCabundleConfigMapForUserNS(ctx, storageInitializerConfig.CaBundleConfigMapName, constants.KServeNamespace, isvc.Namespace) if err != nil { return fmt.Errorf("fails to get cabundle configmap for creating to user namespace: %w", err) } } - if err := c.ReconcileCaBundleConfigMap(newCaBundleConfigMap); err != nil { + if err := c.ReconcileCaBundleConfigMap(ctx, newCaBundleConfigMap); err != nil { return fmt.Errorf("fails to reconcile cabundle configmap: %w", err) } return nil } -func (c *CaBundleConfigMapReconciler) getCabundleConfigMapForUserNS(caBundleNameInConfig string, kserveNamespace string, isvcNamespace string) (*corev1.ConfigMap, error) { +func (c *CaBundleConfigMapReconciler) getCabundleConfigMapForUserNS(ctx context.Context, caBundleNameInConfig string, kserveNamespace string, isvcNamespace string) (*corev1.ConfigMap, error) { var newCaBundleConfigMap *corev1.ConfigMap // Check if cabundle configmap exist & the cabundle.crt exist in the data in controller namespace // If it does not exist, return error - caBundleConfigMap, err := c.clientset.CoreV1().ConfigMaps(kserveNamespace).Get(context.TODO(), caBundleNameInConfig, metav1.GetOptions{}) + caBundleConfigMap, err := c.clientset.CoreV1().ConfigMaps(kserveNamespace).Get(ctx, caBundleNameInConfig, metav1.GetOptions{}) if err == nil { if caBundleConfigMapData := caBundleConfigMap.Data[constants.DefaultCaBundleFileName]; caBundleConfigMapData == "" { @@ -123,13 +123,13 @@ func getDesiredCaBundleConfigMapForUserNS(configmapName string, namespace string } // ReconcileCaBundleConfigMap will manage the creation, update and deletion of the ca bundle ConfigMap -func (c *CaBundleConfigMapReconciler) ReconcileCaBundleConfigMap(desiredConfigMap *corev1.ConfigMap) error { +func (c *CaBundleConfigMapReconciler) ReconcileCaBundleConfigMap(ctx context.Context, desiredConfigMap *corev1.ConfigMap) error { // Create ConfigMap if does not exist - existingConfigMap, err := c.clientset.CoreV1().ConfigMaps(desiredConfigMap.Namespace).Get(context.TODO(), desiredConfigMap.Name, metav1.GetOptions{}) + existingConfigMap, err := c.clientset.CoreV1().ConfigMaps(desiredConfigMap.Namespace).Get(ctx, desiredConfigMap.Name, metav1.GetOptions{}) if err != nil { if apierr.IsNotFound(err) { log.Info("Creating cabundle configmap", "namespace", desiredConfigMap.Namespace, "name", desiredConfigMap.Name) - err = c.client.Create(context.TODO(), desiredConfigMap) + err = c.client.Create(ctx, desiredConfigMap) } return err } @@ -147,7 +147,7 @@ func (c *CaBundleConfigMapReconciler) ReconcileCaBundleConfigMap(desiredConfigMa log.V(1).Info("Reconciling cabundle configmap diff (-desired, +observed):", "diff", diff) log.Info("Updating cabundle configmap", "namespace", existingConfigMap.Namespace, "name", existingConfigMap.Name) existingConfigMap.Data = desiredConfigMap.Data - err = c.client.Update(context.TODO(), existingConfigMap) + err = c.client.Update(ctx, existingConfigMap) if err != nil { return fmt.Errorf("fails to update cabundle configmap: %w", err) } diff --git a/pkg/controller/v1beta1/inferenceservice/reconcilers/cabundleconfigmap/cabundle_configmap_reconciler_test.go b/pkg/controller/v1beta1/inferenceservice/reconcilers/cabundleconfigmap/cabundle_configmap_reconciler_test.go index 52b3252e912..0dffd12a03d 100644 --- a/pkg/controller/v1beta1/inferenceservice/reconcilers/cabundleconfigmap/cabundle_configmap_reconciler_test.go +++ b/pkg/controller/v1beta1/inferenceservice/reconcilers/cabundleconfigmap/cabundle_configmap_reconciler_test.go @@ -20,9 +20,10 @@ import ( "testing" "github.com/google/go-cmp/cmp" - "github.com/kserve/kserve/pkg/constants" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/kserve/kserve/pkg/constants" ) func TestGetDesiredCaBundleConfigMapForUserNS(t *testing.T) { diff --git a/pkg/controller/v1beta1/inferenceservice/reconcilers/deployment/deployment_reconciler.go b/pkg/controller/v1beta1/inferenceservice/reconcilers/deployment/deployment_reconciler.go index 4433c3998df..7f7dc23b37b 100644 --- a/pkg/controller/v1beta1/inferenceservice/reconcilers/deployment/deployment_reconciler.go +++ b/pkg/controller/v1beta1/inferenceservice/reconcilers/deployment/deployment_reconciler.go @@ -20,13 +20,10 @@ import ( "context" "encoding/json" "fmt" - "strconv" "strings" + "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" - "github.com/kserve/kserve/pkg/apis/serving/v1beta1" - "github.com/kserve/kserve/pkg/constants" - "github.com/kserve/kserve/pkg/utils" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" apierr "k8s.io/apimachinery/pkg/api/errors" @@ -36,10 +33,15 @@ import ( "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/strategicpatch" + "k8s.io/utils/ptr" "knative.dev/pkg/kmp" "sigs.k8s.io/controller-runtime/pkg/client" kclient "sigs.k8s.io/controller-runtime/pkg/client" logf "sigs.k8s.io/controller-runtime/pkg/log" + + "github.com/kserve/kserve/pkg/apis/serving/v1beta1" + "github.com/kserve/kserve/pkg/constants" + "github.com/kserve/kserve/pkg/utils" ) var log = logf.Log.WithName("DeploymentReconciler") @@ -57,7 +59,8 @@ func NewDeploymentReconciler(client kclient.Client, componentMeta metav1.ObjectMeta, workerComponentMeta metav1.ObjectMeta, componentExt *v1beta1.ComponentExtensionSpec, - podSpec *corev1.PodSpec, workerPodSpec *corev1.PodSpec) *DeploymentReconciler { + podSpec *corev1.PodSpec, workerPodSpec *corev1.PodSpec, +) *DeploymentReconciler { return &DeploymentReconciler{ client: client, scheme: scheme, @@ -65,9 +68,11 @@ func NewDeploymentReconciler(client kclient.Client, componentExt: componentExt, } } + func createRawDeployment(componentMeta metav1.ObjectMeta, workerComponentMeta metav1.ObjectMeta, componentExt *v1beta1.ComponentExtensionSpec, - podSpec *corev1.PodSpec, workerPodSpec *corev1.PodSpec) []*appsv1.Deployment { + podSpec *corev1.PodSpec, workerPodSpec *corev1.PodSpec, +) []*appsv1.Deployment { var deploymentList []*appsv1.Deployment var workerNodeReplicas int32 var tensorParallelSize string @@ -79,9 +84,9 @@ func createRawDeployment(componentMeta metav1.ObjectMeta, workerComponentMeta me for _, container := range podSpec.Containers { if container.Name == constants.InferenceServiceContainerName { if value, exists := utils.GetEnvVarValue(container.Env, constants.PipelineParallelSizeEnvName); exists { - if parsedValue, err := strconv.Atoi(value); err == nil { + if parsedValue, err := utils.StringToInt32(value); err == nil { // Set pipelineParallelSize to workerNodeSize + 1 (head) - workerNodeReplicas = int32(parsedValue - 1) // nolint #nosec G109 + workerNodeReplicas = parsedValue - 1 } else { log.Error(err, "Failed to convert pipelineParallelSize to int") } @@ -126,7 +131,8 @@ func createRawDeployment(componentMeta metav1.ObjectMeta, workerComponentMeta me func createRawDefaultDeployment(componentMeta metav1.ObjectMeta, componentExt *v1beta1.ComponentExtensionSpec, - podSpec *corev1.PodSpec) *appsv1.Deployment { + podSpec *corev1.PodSpec, +) *appsv1.Deployment { podMetadata := componentMeta podMetadata.Labels["app"] = constants.GetRawServiceLabel(componentMeta.Name) setDefaultPodSpec(podSpec) @@ -148,11 +154,17 @@ func createRawDefaultDeployment(componentMeta metav1.ObjectMeta, deployment.Spec.Strategy = *componentExt.DeploymentStrategy } setDefaultDeploymentSpec(&deployment.Spec) + if componentExt.MinReplicas != nil && deployment.Annotations[constants.AutoscalerClass] == string(constants.AutoscalerClassExternal) { + deployment.Spec.Replicas = ptr.To(*componentExt.MinReplicas) + } + return deployment } + func createRawWorkerDeployment(componentMeta metav1.ObjectMeta, componentExt *v1beta1.ComponentExtensionSpec, - podSpec *corev1.PodSpec, predictorName string, replicas int32) *appsv1.Deployment { + podSpec *corev1.PodSpec, predictorName string, replicas int32, +) *appsv1.Deployment { podMetadata := componentMeta workerPredictorName := constants.GetRawWorkerServiceLabel(predictorName) podMetadata.Labels["app"] = workerPredictorName @@ -176,15 +188,23 @@ func createRawWorkerDeployment(componentMeta metav1.ObjectMeta, } setDefaultDeploymentSpec(&deployment.Spec) + // For multinode, it needs to keep original pods until new pods are ready with rollingUpdate strategy + if deployment.Spec.Strategy.Type == appsv1.RollingUpdateDeploymentStrategyType { + deployment.Spec.Strategy.RollingUpdate = &appsv1.RollingUpdateDeployment{ + MaxUnavailable: &intstr.IntOrString{Type: intstr.String, StrVal: "0%"}, + MaxSurge: &intstr.IntOrString{Type: intstr.String, StrVal: "100%"}, + } + } + deployment.Spec.Replicas = &replicas return deployment } // checkDeploymentExist checks if the deployment exists? -func (r *DeploymentReconciler) checkDeploymentExist(client kclient.Client, deployment *appsv1.Deployment) (constants.CheckResultType, *appsv1.Deployment, error) { +func (r *DeploymentReconciler) checkDeploymentExist(ctx context.Context, client kclient.Client, deployment *appsv1.Deployment) (constants.CheckResultType, *appsv1.Deployment, error) { // get deployment existingDeployment := &appsv1.Deployment{} - err := client.Get(context.TODO(), types.NamespacedName{ + err := client.Get(ctx, types.NamespacedName{ Namespace: deployment.ObjectMeta.Namespace, Name: deployment.ObjectMeta.Name, }, existingDeployment) @@ -196,10 +216,17 @@ func (r *DeploymentReconciler) checkDeploymentExist(client kclient.Client, deplo } // existed, check equivalence // for HPA scaling, we should ignore Replicas of Deployment - ignoreFields := cmpopts.IgnoreFields(appsv1.DeploymentSpec{}, "Replicas") + // for external scaler, we should not ignore Replicas. + var ignoreFields cmp.Option = nil // Initialize to nil by default + + // Set ignoreFields if the condition is met + if existingDeployment.Annotations[constants.AutoscalerClass] != string(constants.AutoscalerClassExternal) { + ignoreFields = cmpopts.IgnoreFields(appsv1.DeploymentSpec{}, "Replicas") + } + // Do a dry-run update. This will populate our local deployment object with any default values // that are present on the remote version. - if err := client.Update(context.TODO(), deployment, kclient.DryRunAll); err != nil { + if err := client.Update(ctx, deployment, kclient.DryRunAll); err != nil { log.Error(err, "Failed to perform dry-run update of deployment", "Deployment", deployment.Name) return constants.CheckResultUnknown, nil, err } @@ -340,10 +367,10 @@ func addGPUResourceToDeployment(deployment *appsv1.Deployment, targetContainerNa } // Reconcile ... -func (r *DeploymentReconciler) Reconcile() ([]*appsv1.Deployment, error) { +func (r *DeploymentReconciler) Reconcile(ctx context.Context) ([]*appsv1.Deployment, error) { for _, deployment := range r.DeploymentList { // Reconcile Deployment - checkResult, _, err := r.checkDeploymentExist(r.client, deployment) + checkResult, _, err := r.checkDeploymentExist(ctx, r.client, deployment) if err != nil { return nil, err } @@ -352,7 +379,7 @@ func (r *DeploymentReconciler) Reconcile() ([]*appsv1.Deployment, error) { var opErr error switch checkResult { case constants.CheckResultCreate: - opErr = r.client.Create(context.TODO(), deployment) + opErr = r.client.Create(ctx, deployment) case constants.CheckResultUpdate: curJson, err := json.Marshal(deployment) if err != nil { @@ -361,8 +388,11 @@ func (r *DeploymentReconciler) Reconcile() ([]*appsv1.Deployment, error) { // To avoid the conflict between HPA and Deployment, // we need to remove the Replicas field from the deployment spec + // For external autoscaler, it should not remove replicas modDeployment := deployment.DeepCopy() - modDeployment.Spec.Replicas = nil + if modDeployment.Annotations[constants.AutoscalerClass] != string(constants.AutoscalerClassExternal) { + modDeployment.Spec.Replicas = nil + } modJson, err := json.Marshal(modDeployment) if err != nil { @@ -375,7 +405,7 @@ func (r *DeploymentReconciler) Reconcile() ([]*appsv1.Deployment, error) { } // Patch the deployment object with the strategic merge patch - opErr = r.client.Patch(context.TODO(), deployment, client.RawPatch(types.StrategicMergePatchType, patchByte)) + opErr = r.client.Patch(ctx, deployment, client.RawPatch(types.StrategicMergePatchType, patchByte)) } if opErr != nil { diff --git a/pkg/controller/v1beta1/inferenceservice/reconcilers/deployment/deployment_reconciler_test.go b/pkg/controller/v1beta1/inferenceservice/reconcilers/deployment/deployment_reconciler_test.go index 0336bafbab6..22d3c5bc825 100644 --- a/pkg/controller/v1beta1/inferenceservice/reconcilers/deployment/deployment_reconciler_test.go +++ b/pkg/controller/v1beta1/inferenceservice/reconcilers/deployment/deployment_reconciler_test.go @@ -21,19 +21,19 @@ import ( "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" - "github.com/kserve/kserve/pkg/apis/serving/v1beta1" - "github.com/kserve/kserve/pkg/constants" - isvcutils "github.com/kserve/kserve/pkg/controller/v1beta1/inferenceservice/utils" - "github.com/kserve/kserve/pkg/utils" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" + + "github.com/kserve/kserve/pkg/apis/serving/v1beta1" + "github.com/kserve/kserve/pkg/constants" + isvcutils "github.com/kserve/kserve/pkg/controller/v1beta1/inferenceservice/utils" + "github.com/kserve/kserve/pkg/utils" ) func TestCreateDefaultDeployment(t *testing.T) { - type args struct { objectMeta metav1.ObjectMeta workerObjectMeta metav1.ObjectMeta @@ -174,6 +174,13 @@ func TestCreateDefaultDeployment(t *testing.T) { constants.RawDeploymentAppLabel: "isvc.default-predictor", }, }, + Strategy: appsv1.DeploymentStrategy{ + Type: appsv1.RollingUpdateDeploymentStrategyType, + RollingUpdate: &appsv1.RollingUpdateDeployment{ + MaxUnavailable: intStrPtr("25%"), + MaxSurge: intStrPtr("25%"), + }, + }, Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Name: "default-predictor", @@ -240,6 +247,13 @@ func TestCreateDefaultDeployment(t *testing.T) { "app": "isvc.default-predictor", }, }, + Strategy: appsv1.DeploymentStrategy{ + Type: appsv1.RollingUpdateDeploymentStrategyType, + RollingUpdate: &appsv1.RollingUpdateDeployment{ + MaxUnavailable: intStrPtr("25%"), + MaxSurge: intStrPtr("25%"), + }, + }, Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Name: "default-predictor", @@ -314,6 +328,13 @@ func TestCreateDefaultDeployment(t *testing.T) { constants.RawDeploymentAppLabel: "isvc.default-predictor-worker", }, }, + Strategy: appsv1.DeploymentStrategy{ + Type: appsv1.RollingUpdateDeploymentStrategyType, + RollingUpdate: &appsv1.RollingUpdateDeployment{ + MaxUnavailable: intStrPtr("0%"), + MaxSurge: intStrPtr("100%"), + }, + }, Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Name: "worker-predictor", @@ -397,13 +418,10 @@ func TestCreateDefaultDeployment(t *testing.T) { cmpopts.IgnoreFields(appsv1.Deployment{}, "Spec.Template.Spec.DNSPolicy"), cmpopts.IgnoreFields(appsv1.Deployment{}, "Spec.Template.Spec.AutomountServiceAccountToken"), cmpopts.IgnoreFields(appsv1.Deployment{}, "Spec.Template.Spec.SchedulerName"), - cmpopts.IgnoreFields(appsv1.Deployment{}, "Spec.Strategy.Type"), - cmpopts.IgnoreFields(appsv1.Deployment{}, "Spec.Strategy.RollingUpdate"), cmpopts.IgnoreFields(appsv1.Deployment{}, "Spec.RevisionHistoryLimit"), cmpopts.IgnoreFields(appsv1.Deployment{}, "Spec.ProgressDeadlineSeconds")); diff != "" { t.Errorf("Test %q unexpected deployment (-want +got): %v", tt.name, diff) } - } }) } @@ -441,7 +459,7 @@ func TestCreateDefaultDeployment(t *testing.T) { return updatedArgs }, modifyExpected: func(updatedExpected []*appsv1.Deployment) []*appsv1.Deployment { - //e[0] is default deployment, e[1] is worker node deployment + // e[0] is default deployment, e[1] is worker node deployment addEnvVarToDeploymentSpec(&updatedExpected[0].Spec, constants.InferenceServiceContainerName, "PIPELINE_PARALLEL_SIZE", "3") addEnvVarToDeploymentSpec(&updatedExpected[1].Spec, constants.WorkerContainerName, "PIPELINE_PARALLEL_SIZE", "3") updatedExpected[1].Spec.Replicas = int32Ptr(2) @@ -469,8 +487,6 @@ func TestCreateDefaultDeployment(t *testing.T) { cmpopts.IgnoreFields(appsv1.Deployment{}, "Spec.Template.Spec.DNSPolicy"), cmpopts.IgnoreFields(appsv1.Deployment{}, "Spec.Template.Spec.AutomountServiceAccountToken"), cmpopts.IgnoreFields(appsv1.Deployment{}, "Spec.Template.Spec.SchedulerName"), - cmpopts.IgnoreFields(appsv1.Deployment{}, "Spec.Strategy.Type"), - cmpopts.IgnoreFields(appsv1.Deployment{}, "Spec.Strategy.RollingUpdate"), cmpopts.IgnoreFields(appsv1.Deployment{}, "Spec.RevisionHistoryLimit"), cmpopts.IgnoreFields(appsv1.Deployment{}, "Spec.ProgressDeadlineSeconds")); diff != "" { t.Errorf("Test %q unexpected deployment (-want +got): %v", tt.name, diff) @@ -772,8 +788,6 @@ func TestCreateDefaultDeployment(t *testing.T) { cmpopts.IgnoreFields(appsv1.Deployment{}, "Spec.Template.Spec.DNSPolicy"), cmpopts.IgnoreFields(appsv1.Deployment{}, "Spec.Template.Spec.AutomountServiceAccountToken"), cmpopts.IgnoreFields(appsv1.Deployment{}, "Spec.Template.Spec.SchedulerName"), - cmpopts.IgnoreFields(appsv1.Deployment{}, "Spec.Strategy.Type"), - cmpopts.IgnoreFields(appsv1.Deployment{}, "Spec.Strategy.RollingUpdate"), cmpopts.IgnoreFields(appsv1.Deployment{}, "Spec.RevisionHistoryLimit"), cmpopts.IgnoreFields(appsv1.Deployment{}, "Spec.ProgressDeadlineSeconds")); diff != "" { t.Errorf("Test %q unexpected deployment (-want +got): %v", tt.name, diff) @@ -783,10 +797,16 @@ func TestCreateDefaultDeployment(t *testing.T) { } } +func intStrPtr(s string) *intstr.IntOrString { + v := intstr.FromString(s) + return &v +} + func int32Ptr(i int32) *int32 { val := i return &val } + func BoolPtr(b bool) *bool { val := b return &val diff --git a/pkg/controller/v1beta1/inferenceservice/reconcilers/hpa/hpa_reconciler.go b/pkg/controller/v1beta1/inferenceservice/reconcilers/hpa/hpa_reconciler.go index 1b25db4f705..349d4bf6c60 100644 --- a/pkg/controller/v1beta1/inferenceservice/reconcilers/hpa/hpa_reconciler.go +++ b/pkg/controller/v1beta1/inferenceservice/reconcilers/hpa/hpa_reconciler.go @@ -15,10 +15,7 @@ package hpa import ( "context" - "strconv" - "github.com/kserve/kserve/pkg/apis/serving/v1beta1" - "github.com/kserve/kserve/pkg/constants" autoscalingv2 "k8s.io/api/autoscaling/v2" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/equality" @@ -29,6 +26,10 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" logf "sigs.k8s.io/controller-runtime/pkg/log" + + "github.com/kserve/kserve/pkg/apis/serving/v1beta1" + "github.com/kserve/kserve/pkg/constants" + "github.com/kserve/kserve/pkg/utils" ) var log = logf.Log.WithName("HPAReconciler") @@ -44,30 +45,38 @@ type HPAReconciler struct { func NewHPAReconciler(client client.Client, scheme *runtime.Scheme, componentMeta metav1.ObjectMeta, - componentExt *v1beta1.ComponentExtensionSpec) *HPAReconciler { + componentExt *v1beta1.ComponentExtensionSpec, +) (*HPAReconciler, error) { + hpa, err := createHPA(componentMeta, componentExt) + if err != nil { + return nil, err + } return &HPAReconciler{ client: client, scheme: scheme, - HPA: createHPA(componentMeta, componentExt), + HPA: hpa, componentExt: componentExt, - } + }, nil } -func getHPAMetrics(metadata metav1.ObjectMeta, componentExt *v1beta1.ComponentExtensionSpec) []autoscalingv2.MetricSpec { +func getHPAMetrics(metadata metav1.ObjectMeta, componentExt *v1beta1.ComponentExtensionSpec) ([]autoscalingv2.MetricSpec, error) { var metrics []autoscalingv2.MetricSpec var utilization int32 + var err error annotations := metadata.Annotations resourceName := corev1.ResourceCPU if value, ok := annotations[constants.TargetUtilizationPercentage]; ok { - utilizationInt, _ := strconv.Atoi(value) - utilization = int32(utilizationInt) // #nosec G109 + utilization, err = utils.StringToInt32(value) + if err != nil { + return metrics, err + } } else { utilization = constants.DefaultCPUUtilization } if componentExt.ScaleTarget != nil { - utilization = int32(*componentExt.ScaleTarget) + utilization = *componentExt.ScaleTarget } if componentExt.ScaleMetric != nil { @@ -87,23 +96,27 @@ func getHPAMetrics(metadata metav1.ObjectMeta, componentExt *v1beta1.ComponentEx }, } metrics = append(metrics, ms) - return metrics + return metrics, nil } func createHPA(componentMeta metav1.ObjectMeta, - componentExt *v1beta1.ComponentExtensionSpec) *autoscalingv2.HorizontalPodAutoscaler { + componentExt *v1beta1.ComponentExtensionSpec, +) (*autoscalingv2.HorizontalPodAutoscaler, error) { var minReplicas int32 if componentExt.MinReplicas == nil || (*componentExt.MinReplicas) < constants.DefaultMinReplicas { - minReplicas = int32(constants.DefaultMinReplicas) + minReplicas = constants.DefaultMinReplicas } else { - minReplicas = int32(*componentExt.MinReplicas) + minReplicas = *componentExt.MinReplicas } - maxReplicas := int32(componentExt.MaxReplicas) + maxReplicas := componentExt.MaxReplicas if maxReplicas < minReplicas { maxReplicas = minReplicas } - metrics := getHPAMetrics(componentMeta, componentExt) + metrics, err := getHPAMetrics(componentMeta, componentExt) + if err != nil { + return nil, err + } hpa := &autoscalingv2.HorizontalPodAutoscaler{ ObjectMeta: componentMeta, Spec: autoscalingv2.HorizontalPodAutoscalerSpec{ @@ -118,14 +131,14 @@ func createHPA(componentMeta metav1.ObjectMeta, Behavior: &autoscalingv2.HorizontalPodAutoscalerBehavior{}, }, } - return hpa + return hpa, nil } // checkHPAExist checks if the hpa exists? -func (r *HPAReconciler) checkHPAExist(client client.Client) (constants.CheckResultType, *autoscalingv2.HorizontalPodAutoscaler, error) { +func (r *HPAReconciler) checkHPAExist(ctx context.Context, client client.Client) (constants.CheckResultType, *autoscalingv2.HorizontalPodAutoscaler, error) { // get hpa existingHPA := &autoscalingv2.HorizontalPodAutoscaler{} - err := client.Get(context.TODO(), types.NamespacedName{ + err := client.Get(ctx, types.NamespacedName{ Namespace: r.HPA.ObjectMeta.Namespace, Name: r.HPA.ObjectMeta.Name, }, existingHPA) @@ -173,9 +186,9 @@ func shouldCreateHPA(desired *autoscalingv2.HorizontalPodAutoscaler) bool { } // Reconcile ... -func (r *HPAReconciler) Reconcile() (*autoscalingv2.HorizontalPodAutoscaler, error) { +func (r *HPAReconciler) Reconcile(ctx context.Context) (*autoscalingv2.HorizontalPodAutoscaler, error) { // reconcile HorizontalPodAutoscaler - checkResult, existingHPA, err := r.checkHPAExist(r.client) + checkResult, existingHPA, err := r.checkHPAExist(ctx, r.client) log.Info("HorizontalPodAutoscaler reconcile", "checkResult", checkResult, "err", err) if err != nil { return nil, err @@ -184,11 +197,11 @@ func (r *HPAReconciler) Reconcile() (*autoscalingv2.HorizontalPodAutoscaler, err var opErr error switch checkResult { case constants.CheckResultCreate: - opErr = r.client.Create(context.TODO(), r.HPA) + opErr = r.client.Create(ctx, r.HPA) case constants.CheckResultUpdate: - opErr = r.client.Update(context.TODO(), r.HPA) + opErr = r.client.Update(ctx, r.HPA) case constants.CheckResultDelete: - opErr = r.client.Delete(context.TODO(), r.HPA) + opErr = r.client.Delete(ctx, r.HPA) default: return existingHPA, nil } @@ -199,6 +212,7 @@ func (r *HPAReconciler) Reconcile() (*autoscalingv2.HorizontalPodAutoscaler, err return r.HPA, nil } + func (r *HPAReconciler) SetControllerReferences(owner metav1.Object, scheme *runtime.Scheme) error { return controllerutil.SetControllerReference(owner, r.HPA, scheme) } diff --git a/pkg/controller/v1beta1/inferenceservice/reconcilers/hpa/hpa_reconciler_test.go b/pkg/controller/v1beta1/inferenceservice/reconcilers/hpa/hpa_reconciler_test.go index 46563bdb012..e0a740c1645 100644 --- a/pkg/controller/v1beta1/inferenceservice/reconcilers/hpa/hpa_reconciler_test.go +++ b/pkg/controller/v1beta1/inferenceservice/reconcilers/hpa/hpa_reconciler_test.go @@ -19,17 +19,17 @@ import ( "testing" "github.com/google/go-cmp/cmp" - "github.com/kserve/kserve/pkg/apis/serving/v1beta1" - "github.com/kserve/kserve/pkg/constants" "github.com/stretchr/testify/assert" autoscalingv2 "k8s.io/api/autoscaling/v2" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "knative.dev/pkg/ptr" + "k8s.io/utils/ptr" + + "github.com/kserve/kserve/pkg/apis/serving/v1beta1" + "github.com/kserve/kserve/pkg/constants" ) func TestCreateHPA(t *testing.T) { - type args struct { objectMeta metav1.ObjectMeta componentExt *v1beta1.ComponentExtensionSpec @@ -66,9 +66,9 @@ func TestCreateHPA(t *testing.T) { }, }, componentExt: &v1beta1.ComponentExtensionSpec{ - MinReplicas: v1beta1.GetIntReference(2), + MinReplicas: ptr.To(int32(2)), MaxReplicas: 5, - ScaleTarget: v1beta1.GetIntReference(30), + ScaleTarget: ptr.To(int32(30)), ScaleMetric: &cpuResource, }, }, @@ -84,16 +84,16 @@ func TestCreateHPA(t *testing.T) { "predictorspecifiedhpa": { objectMeta: metav1.ObjectMeta{}, componentExt: &v1beta1.ComponentExtensionSpec{ - MinReplicas: v1beta1.GetIntReference(5), + MinReplicas: ptr.To(int32(5)), MaxReplicas: 10, - ScaleTarget: v1beta1.GetIntReference(50), + ScaleTarget: ptr.To(int32(50)), ScaleMetric: &cpuResource, }, }, "invalidinputhpa": { objectMeta: metav1.ObjectMeta{}, componentExt: &v1beta1.ComponentExtensionSpec{ - MinReplicas: v1beta1.GetIntReference(0), + MinReplicas: ptr.To(int32(0)), MaxReplicas: -10, ScaleTarget: nil, ScaleMetric: &memoryResource, @@ -123,7 +123,7 @@ func TestCreateHPA(t *testing.T) { { Type: autoscalingv2.ResourceMetricSourceType, Resource: &autoscalingv2.ResourceMetricSource{ - Name: v1.ResourceName("cpu"), + Name: corev1.ResourceName("cpu"), Target: autoscalingv2.MetricTarget{ Type: "Utilization", AverageUtilization: &defaultutilization, @@ -148,7 +148,7 @@ func TestCreateHPA(t *testing.T) { { Type: autoscalingv2.ResourceMetricSourceType, Resource: &autoscalingv2.ResourceMetricSource{ - Name: v1.ResourceName("cpu"), + Name: corev1.ResourceName("cpu"), Target: autoscalingv2.MetricTarget{ Type: "Utilization", AverageUtilization: &igutilization, @@ -172,7 +172,7 @@ func TestCreateHPA(t *testing.T) { { Type: autoscalingv2.ResourceMetricSourceType, Resource: &autoscalingv2.ResourceMetricSource{ - Name: v1.ResourceName("memory"), + Name: corev1.ResourceName("memory"), Target: autoscalingv2.MetricTarget{ Type: "Utilization", AverageUtilization: &defaultutilization, @@ -196,7 +196,7 @@ func TestCreateHPA(t *testing.T) { { Type: autoscalingv2.ResourceMetricSourceType, Resource: &autoscalingv2.ResourceMetricSource{ - Name: v1.ResourceName("cpu"), + Name: corev1.ResourceName("cpu"), Target: autoscalingv2.MetricTarget{ Type: "Utilization", AverageUtilization: &predictorutilization, @@ -213,6 +213,7 @@ func TestCreateHPA(t *testing.T) { name string args args expected *autoscalingv2.HorizontalPodAutoscaler + err error }{ { name: "inference graph default hpa", @@ -221,6 +222,7 @@ func TestCreateHPA(t *testing.T) { componentExt: testInput["igdefaulthpa"].componentExt, }, expected: expectedHPASpecs["igdefaulthpa"], + err: nil, }, { name: "inference graph specified hpa", @@ -237,6 +239,7 @@ func TestCreateHPA(t *testing.T) { componentExt: testInput["predictordefaulthpa"].componentExt, }, expected: expectedHPASpecs["predictordefaulthpa"], + err: nil, }, { name: "predictor specified hpa", @@ -245,6 +248,7 @@ func TestCreateHPA(t *testing.T) { componentExt: testInput["predictorspecifiedhpa"].componentExt, }, expected: expectedHPASpecs["predictorspecifiedhpa"], + err: nil, }, { name: "invalid input for hpa", @@ -253,14 +257,16 @@ func TestCreateHPA(t *testing.T) { componentExt: testInput["invalidinputhpa"].componentExt, }, expected: expectedHPASpecs["predictordefaulthpa"], + err: nil, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got := createHPA(tt.args.objectMeta, tt.args.componentExt) + got, err := createHPA(tt.args.objectMeta, tt.args.componentExt) if diff := cmp.Diff(tt.expected, got); diff != "" { t.Errorf("Test %q unexpected hpa (-want +got): %v", tt.name, diff) } + assert.Equal(t, tt.err, err) }) } } @@ -276,59 +282,59 @@ func TestSemanticHPAEquals(t *testing.T) { assert.False(t, semanticHPAEquals( &autoscalingv2.HorizontalPodAutoscaler{ - Spec: autoscalingv2.HorizontalPodAutoscalerSpec{MinReplicas: ptr.Int32(3)}, + Spec: autoscalingv2.HorizontalPodAutoscalerSpec{MinReplicas: ptr.To(int32(3))}, }, &autoscalingv2.HorizontalPodAutoscaler{ - Spec: autoscalingv2.HorizontalPodAutoscalerSpec{MinReplicas: ptr.Int32(4)}, + Spec: autoscalingv2.HorizontalPodAutoscalerSpec{MinReplicas: ptr.To(int32(4))}, })) assert.False(t, semanticHPAEquals( &autoscalingv2.HorizontalPodAutoscaler{ ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{constants.AutoscalerClass: "hpa"}}, - Spec: autoscalingv2.HorizontalPodAutoscalerSpec{MinReplicas: ptr.Int32(3)}, + Spec: autoscalingv2.HorizontalPodAutoscalerSpec{MinReplicas: ptr.To(int32(3))}, }, &autoscalingv2.HorizontalPodAutoscaler{ ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{constants.AutoscalerClass: "external"}}, - Spec: autoscalingv2.HorizontalPodAutoscalerSpec{MinReplicas: ptr.Int32(3)}, + Spec: autoscalingv2.HorizontalPodAutoscalerSpec{MinReplicas: ptr.To(int32(3))}, })) assert.False(t, semanticHPAEquals( &autoscalingv2.HorizontalPodAutoscaler{ ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{}}, - Spec: autoscalingv2.HorizontalPodAutoscalerSpec{MinReplicas: ptr.Int32(3)}, + Spec: autoscalingv2.HorizontalPodAutoscalerSpec{MinReplicas: ptr.To(int32(3))}, }, &autoscalingv2.HorizontalPodAutoscaler{ ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{constants.AutoscalerClass: "external"}}, - Spec: autoscalingv2.HorizontalPodAutoscalerSpec{MinReplicas: ptr.Int32(3)}, + Spec: autoscalingv2.HorizontalPodAutoscalerSpec{MinReplicas: ptr.To(int32(3))}, })) assert.True(t, semanticHPAEquals( &autoscalingv2.HorizontalPodAutoscaler{ ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{constants.AutoscalerClass: "hpa"}}, - Spec: autoscalingv2.HorizontalPodAutoscalerSpec{MinReplicas: ptr.Int32(3)}, + Spec: autoscalingv2.HorizontalPodAutoscalerSpec{MinReplicas: ptr.To(int32(3))}, }, &autoscalingv2.HorizontalPodAutoscaler{ ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{constants.AutoscalerClass: "hpa"}}, - Spec: autoscalingv2.HorizontalPodAutoscalerSpec{MinReplicas: ptr.Int32(3)}, + Spec: autoscalingv2.HorizontalPodAutoscalerSpec{MinReplicas: ptr.To(int32(3))}, })) assert.True(t, semanticHPAEquals( &autoscalingv2.HorizontalPodAutoscaler{ ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{}}, - Spec: autoscalingv2.HorizontalPodAutoscalerSpec{MinReplicas: ptr.Int32(3)}, + Spec: autoscalingv2.HorizontalPodAutoscalerSpec{MinReplicas: ptr.To(int32(3))}, }, &autoscalingv2.HorizontalPodAutoscaler{ ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{}}, - Spec: autoscalingv2.HorizontalPodAutoscalerSpec{MinReplicas: ptr.Int32(3)}, + Spec: autoscalingv2.HorizontalPodAutoscalerSpec{MinReplicas: ptr.To(int32(3))}, })) assert.True(t, semanticHPAEquals( &autoscalingv2.HorizontalPodAutoscaler{ ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{"unrelated": "true"}}, - Spec: autoscalingv2.HorizontalPodAutoscalerSpec{MinReplicas: ptr.Int32(3)}, + Spec: autoscalingv2.HorizontalPodAutoscalerSpec{MinReplicas: ptr.To(int32(3))}, }, &autoscalingv2.HorizontalPodAutoscaler{ ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{"unrelated": "false"}}, - Spec: autoscalingv2.HorizontalPodAutoscalerSpec{MinReplicas: ptr.Int32(3)}, + Spec: autoscalingv2.HorizontalPodAutoscalerSpec{MinReplicas: ptr.To(int32(3))}, })) } diff --git a/pkg/controller/v1beta1/inferenceservice/reconcilers/ingress/domain.go b/pkg/controller/v1beta1/inferenceservice/reconcilers/ingress/domain.go index 17a2671c2b5..0eeaec77c23 100644 --- a/pkg/controller/v1beta1/inferenceservice/reconcilers/ingress/domain.go +++ b/pkg/controller/v1beta1/inferenceservice/reconcilers/ingress/domain.go @@ -21,10 +21,12 @@ import ( "fmt" "text/template" - "github.com/kserve/kserve/pkg/apis/serving/v1beta1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/validation" "k8s.io/apimachinery/pkg/util/validation/field" + "knative.dev/pkg/network" + + "github.com/kserve/kserve/pkg/apis/serving/v1beta1" ) type DomainTemplateValues struct { @@ -62,3 +64,30 @@ func GenerateDomainName(name string, obj metav1.ObjectMeta, ingressConfig *v1bet return buf.String(), nil } + +func GenerateInternalDomainName(name string, obj metav1.ObjectMeta, ingressConfig *v1beta1.IngressConfig) (string, error) { + values := DomainTemplateValues{ + Name: name, + Namespace: obj.Namespace, + IngressDomain: network.GetClusterDomainName(), + Annotations: obj.Annotations, + Labels: obj.Labels, + } + + tpl, err := template.New("domain-template").Parse(ingressConfig.DomainTemplate) + if err != nil { + return "", err + } + + buf := bytes.Buffer{} + if err := tpl.Execute(&buf, values); err != nil { + return "", fmt.Errorf("error rendering the domain template: %w", err) + } + + urlErrs := validation.IsFullyQualifiedDomainName(field.NewPath("url"), buf.String()) + if urlErrs != nil { + return "", fmt.Errorf("invalid domain name %q: %w", buf.String(), urlErrs.ToAggregate()) + } + + return buf.String(), nil +} diff --git a/pkg/controller/v1beta1/inferenceservice/reconcilers/ingress/domain_test.go b/pkg/controller/v1beta1/inferenceservice/reconcilers/ingress/domain_test.go index 576c121b1a6..4341724afa8 100644 --- a/pkg/controller/v1beta1/inferenceservice/reconcilers/ingress/domain_test.go +++ b/pkg/controller/v1beta1/inferenceservice/reconcilers/ingress/domain_test.go @@ -20,18 +20,19 @@ import ( "testing" "github.com/google/go-cmp/cmp" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "github.com/kserve/kserve/pkg/apis/serving/v1beta1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) func TestGenerateDomainName(t *testing.T) { type args struct { name string - obj v1.ObjectMeta + obj metav1.ObjectMeta ingressConfig *v1beta1.IngressConfig } - obj := v1.ObjectMeta{ + obj := metav1.ObjectMeta{ Name: "model", Namespace: "test", Annotations: map[string]string{ diff --git a/pkg/controller/v1beta1/inferenceservice/reconcilers/ingress/httproute_reconciler.go b/pkg/controller/v1beta1/inferenceservice/reconcilers/ingress/httproute_reconciler.go new file mode 100644 index 00000000000..102f18074c5 --- /dev/null +++ b/pkg/controller/v1beta1/inferenceservice/reconcilers/ingress/httproute_reconciler.go @@ -0,0 +1,872 @@ +/* +Copyright 2024 The KServe Authors. + +Licensed 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. +*/ + +package ingress + +import ( + "context" + "fmt" + "strings" + + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/equality" + apierr "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "k8s.io/utils/ptr" + "knative.dev/pkg/apis" + knapis "knative.dev/pkg/apis" + duckv1 "knative.dev/pkg/apis/duck/v1" + "knative.dev/pkg/network" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + gatewayapiv1 "sigs.k8s.io/gateway-api/apis/v1" + + "github.com/kserve/kserve/pkg/apis/serving/v1beta1" + "github.com/kserve/kserve/pkg/constants" + "github.com/kserve/kserve/pkg/utils" +) + +const ( + HTTPRouteNotReady = "HttpRouteNotReady" + HTTPRouteParentStatusNotAvailable = "ParentStatusNotAvailable" +) + +var DefaultTimeout = toGatewayAPIDuration(60) + +type RawHTTPRouteReconciler struct { + client client.Client + scheme *runtime.Scheme + ingressConfig *v1beta1.IngressConfig + isvcConfig *v1beta1.InferenceServicesConfig +} + +func NewRawHTTPRouteReconciler(client client.Client, scheme *runtime.Scheme, ingressConfig *v1beta1.IngressConfig, + isvcConfig *v1beta1.InferenceServicesConfig, +) *RawHTTPRouteReconciler { + return &RawHTTPRouteReconciler{ + client: client, + scheme: scheme, + ingressConfig: ingressConfig, + isvcConfig: isvcConfig, + } +} + +// toGatewayAPIDuration converts seconds to gatewayapiv1.Duration +func toGatewayAPIDuration(seconds int64) *gatewayapiv1.Duration { + duration := gatewayapiv1.Duration(fmt.Sprintf("%ds", seconds)) + return &duration +} + +func createRawURL(isvc *v1beta1.InferenceService, + ingressConfig *v1beta1.IngressConfig, +) (*knapis.URL, error) { + var err error + url := &knapis.URL{} + url.Scheme = ingressConfig.UrlScheme + url.Host, err = GenerateDomainName(isvc.Name, isvc.ObjectMeta, ingressConfig) + if err != nil { + return nil, err + } + + return url, nil +} + +func getRawServiceHost(ctx context.Context, isvc *v1beta1.InferenceService, client client.Client) string { + existingService := &corev1.Service{} + if isvc.Spec.Transformer != nil { + transformerName := constants.TransformerServiceName(isvc.Name) + + // Check if existing transformer service name has default suffix + err := client.Get(ctx, types.NamespacedName{Name: constants.DefaultTransformerServiceName(isvc.Name), Namespace: isvc.Namespace}, existingService) + if err == nil { + transformerName = constants.DefaultTransformerServiceName(isvc.Name) + } + return network.GetServiceHostname(transformerName, isvc.Namespace) + } + + predictorName := constants.PredictorServiceName(isvc.Name) + + // Check if existing predictor service name has default suffix + err := client.Get(ctx, types.NamespacedName{Name: constants.DefaultPredictorServiceName(isvc.Name), Namespace: isvc.Namespace}, existingService) + if err == nil { + predictorName = constants.DefaultPredictorServiceName(isvc.Name) + } + return network.GetServiceHostname(predictorName, isvc.Namespace) +} + +func createHTTPRouteMatch(prefix string) gatewayapiv1.HTTPRouteMatch { + return gatewayapiv1.HTTPRouteMatch{ + Path: &gatewayapiv1.HTTPPathMatch{ + Type: ptr.To(gatewayapiv1.PathMatchRegularExpression), + Value: ptr.To(prefix), + }, + } +} + +func addIsvcHeaders(name string, namespace string) gatewayapiv1.HTTPRouteFilter { + return gatewayapiv1.HTTPRouteFilter{ + Type: gatewayapiv1.HTTPRouteFilterRequestHeaderModifier, + RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ + Set: []gatewayapiv1.HTTPHeader{ + { + Name: constants.IsvcNameHeader, + Value: name, + }, + { + Name: constants.IsvcNamespaceHeader, + Value: namespace, + }, + }, + }, + } +} + +func createHTTPRouteRule(routeMatches []gatewayapiv1.HTTPRouteMatch, filters []gatewayapiv1.HTTPRouteFilter, + serviceName, namespace string, port int32, timeout *gatewayapiv1.Duration, +) gatewayapiv1.HTTPRouteRule { + var backendRefs []gatewayapiv1.HTTPBackendRef + if serviceName != "" { + backendRefs = []gatewayapiv1.HTTPBackendRef{ + { + BackendRef: gatewayapiv1.BackendRef{ + BackendObjectReference: gatewayapiv1.BackendObjectReference{ + Kind: ptr.To(gatewayapiv1.Kind(constants.ServiceKind)), + Name: gatewayapiv1.ObjectName(serviceName), + Namespace: (*gatewayapiv1.Namespace)(&namespace), + Port: (*gatewayapiv1.PortNumber)(&port), + }, + }, + }, + } + } + return gatewayapiv1.HTTPRouteRule{ + Matches: routeMatches, + Filters: filters, + BackendRefs: backendRefs, + Timeouts: &gatewayapiv1.HTTPRouteTimeouts{ + Request: timeout, + }, + } +} + +func createRawPredictorHTTPRoute(ctx context.Context, isvc *v1beta1.InferenceService, ingressConfig *v1beta1.IngressConfig, isvcConfig *v1beta1.InferenceServicesConfig, + client client.Client, +) (*gatewayapiv1.HTTPRoute, error) { + var httpRouteRules []gatewayapiv1.HTTPRouteRule + var allowedHosts []gatewayapiv1.Hostname + + if !isvc.Status.IsConditionReady(v1beta1.PredictorReady) { + isvc.Status.SetCondition(v1beta1.IngressReady, &apis.Condition{ + Type: v1beta1.IngressReady, + Status: corev1.ConditionFalse, + Reason: "Predictor ingress not created", + }) + return nil, nil + } + + existing := &corev1.Service{} + predictorName := constants.PredictorServiceName(isvc.Name) + + // Check if existing predictor service name has default suffix + err := client.Get(ctx, types.NamespacedName{Name: constants.DefaultPredictorServiceName(isvc.Name), Namespace: isvc.Namespace}, existing) + if err == nil { + // Found existing predictor service with default suffix + predictorName = constants.DefaultPredictorServiceName(isvc.Name) + } + + // Add isvc name and namespace headers + filters := []gatewayapiv1.HTTPRouteFilter{addIsvcHeaders(isvc.Name, isvc.Namespace)} + + // Add predictor host rules + predictorHost, err := GenerateDomainName(predictorName, isvc.ObjectMeta, ingressConfig) + if err != nil { + return nil, fmt.Errorf("failed to generate predictor ingress host: %w", err) + } + allowedHosts = append(allowedHosts, gatewayapiv1.Hostname(predictorHost)) + routeMatch := []gatewayapiv1.HTTPRouteMatch{createHTTPRouteMatch(constants.FallbackPrefix())} + timeout := DefaultTimeout + if isvc.Spec.Predictor.TimeoutSeconds != nil { + timeout = toGatewayAPIDuration(*isvc.Spec.Predictor.TimeoutSeconds) + } + httpRouteRules = append(httpRouteRules, createHTTPRouteRule(routeMatch, filters, predictorName, isvc.Namespace, constants.CommonDefaultHttpPort, timeout)) + + annotations := utils.Filter(isvc.Annotations, func(key string) bool { + return !utils.Includes(isvcConfig.ServiceAnnotationDisallowedList, key) + }) + labels := utils.Filter(isvc.Labels, func(key string) bool { + return !utils.Includes(isvcConfig.ServiceLabelDisallowedList, key) + }) + gatewaySlice := strings.Split(ingressConfig.KserveIngressGateway, "/") + httpRoute := gatewayapiv1.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: constants.PredictorServiceName(isvc.Name), + Namespace: isvc.Namespace, + Annotations: annotations, + Labels: labels, + }, + Spec: gatewayapiv1.HTTPRouteSpec{ + Hostnames: allowedHosts, + Rules: httpRouteRules, + CommonRouteSpec: gatewayapiv1.CommonRouteSpec{ + ParentRefs: []gatewayapiv1.ParentReference{ + { + Group: (*gatewayapiv1.Group)(&gatewayapiv1.GroupVersion.Group), + Kind: (*gatewayapiv1.Kind)(ptr.To(constants.GatewayKind)), + Namespace: (*gatewayapiv1.Namespace)(&gatewaySlice[0]), + Name: gatewayapiv1.ObjectName(gatewaySlice[1]), + }, + }, + }, + }, + } + return &httpRoute, nil +} + +func createRawTransformerHTTPRoute(ctx context.Context, isvc *v1beta1.InferenceService, ingressConfig *v1beta1.IngressConfig, isvcConfig *v1beta1.InferenceServicesConfig, + client client.Client, +) (*gatewayapiv1.HTTPRoute, error) { + var httpRouteRules []gatewayapiv1.HTTPRouteRule + var allowedHosts []gatewayapiv1.Hostname + + if !isvc.Status.IsConditionReady(v1beta1.TransformerReady) { + isvc.Status.SetCondition(v1beta1.IngressReady, &apis.Condition{ + Type: v1beta1.IngressReady, + Status: corev1.ConditionFalse, + Reason: "Transformer ingress not created", + }) + return nil, nil + } + + existing := &corev1.Service{} + transformerName := constants.TransformerServiceName(isvc.Name) + + // Check if existing transformer service name has default suffix + err := client.Get(ctx, types.NamespacedName{Name: constants.DefaultTransformerServiceName(isvc.Name), Namespace: isvc.Namespace}, existing) + if err == nil { + // Found existing transformer service with default suffix + transformerName = constants.DefaultTransformerServiceName(isvc.Name) + } + + // Add isvc name and namespace headers + filters := []gatewayapiv1.HTTPRouteFilter{addIsvcHeaders(isvc.Name, isvc.Namespace)} + + transformerHost, err := GenerateDomainName(transformerName, isvc.ObjectMeta, ingressConfig) + if err != nil { + return nil, fmt.Errorf("failed to generate transformer ingress host: %w", err) + } + allowedHosts = append(allowedHosts, gatewayapiv1.Hostname(transformerHost)) + routeMatch := []gatewayapiv1.HTTPRouteMatch{createHTTPRouteMatch(constants.FallbackPrefix())} + timeout := DefaultTimeout + if isvc.Spec.Transformer.TimeoutSeconds != nil { + timeout = toGatewayAPIDuration(*isvc.Spec.Transformer.TimeoutSeconds) + } + httpRouteRules = append(httpRouteRules, createHTTPRouteRule(routeMatch, filters, transformerName, isvc.Namespace, + constants.CommonDefaultHttpPort, timeout)) + + annotations := utils.Filter(isvc.Annotations, func(key string) bool { + return !utils.Includes(isvcConfig.ServiceAnnotationDisallowedList, key) + }) + labels := utils.Filter(isvc.Labels, func(key string) bool { + return !utils.Includes(isvcConfig.ServiceLabelDisallowedList, key) + }) + gatewaySlice := strings.Split(ingressConfig.KserveIngressGateway, "/") + httpRoute := gatewayapiv1.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: constants.TransformerServiceName(isvc.Name), + Namespace: isvc.Namespace, + Annotations: annotations, + Labels: labels, + }, + Spec: gatewayapiv1.HTTPRouteSpec{ + Hostnames: allowedHosts, + Rules: httpRouteRules, + CommonRouteSpec: gatewayapiv1.CommonRouteSpec{ + ParentRefs: []gatewayapiv1.ParentReference{ + { + Group: (*gatewayapiv1.Group)(&gatewayapiv1.GroupVersion.Group), + Kind: (*gatewayapiv1.Kind)(ptr.To(constants.GatewayKind)), + Namespace: (*gatewayapiv1.Namespace)(&gatewaySlice[0]), + Name: gatewayapiv1.ObjectName(gatewaySlice[1]), + }, + }, + }, + }, + } + return &httpRoute, nil +} + +func createRawExplainerHTTPRoute(ctx context.Context, isvc *v1beta1.InferenceService, ingressConfig *v1beta1.IngressConfig, isvcConfig *v1beta1.InferenceServicesConfig, + client client.Client, +) (*gatewayapiv1.HTTPRoute, error) { + var httpRouteRules []gatewayapiv1.HTTPRouteRule + var allowedHosts []gatewayapiv1.Hostname + + if !isvc.Status.IsConditionReady(v1beta1.ExplainerReady) { + isvc.Status.SetCondition(v1beta1.IngressReady, &apis.Condition{ + Type: v1beta1.IngressReady, + Status: corev1.ConditionFalse, + Reason: "Explainer ingress not created", + }) + return nil, nil + } + + existing := &corev1.Service{} + explainerName := constants.ExplainerServiceName(isvc.Name) + + // Check if existing explainer service name has default suffix + err := client.Get(ctx, types.NamespacedName{Name: constants.DefaultExplainerServiceName(isvc.Name), Namespace: isvc.Namespace}, existing) + if err == nil { + // Found existing explainer service with default suffix + explainerName = constants.DefaultExplainerServiceName(isvc.Name) + } + + // Add isvc name and namespace headers + filters := []gatewayapiv1.HTTPRouteFilter{addIsvcHeaders(isvc.Name, isvc.Namespace)} + + explainerHost, err := GenerateDomainName(explainerName, isvc.ObjectMeta, ingressConfig) + if err != nil { + return nil, fmt.Errorf("failed to generate explainer ingress host: %w", err) + } + allowedHosts = append(allowedHosts, gatewayapiv1.Hostname(explainerHost)) + + // Add explainer host rules + routeMatch := []gatewayapiv1.HTTPRouteMatch{createHTTPRouteMatch(constants.FallbackPrefix())} + timeout := DefaultTimeout + if isvc.Spec.Explainer.TimeoutSeconds != nil { + timeout = toGatewayAPIDuration(*isvc.Spec.Explainer.TimeoutSeconds) + } + httpRouteRules = append(httpRouteRules, createHTTPRouteRule(routeMatch, filters, explainerName, isvc.Namespace, + constants.CommonDefaultHttpPort, timeout)) + + annotations := utils.Filter(isvc.Annotations, func(key string) bool { + return !utils.Includes(isvcConfig.ServiceAnnotationDisallowedList, key) + }) + labels := utils.Filter(isvc.Labels, func(key string) bool { + return !utils.Includes(isvcConfig.ServiceLabelDisallowedList, key) + }) + gatewaySlice := strings.Split(ingressConfig.KserveIngressGateway, "/") + httpRoute := gatewayapiv1.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: constants.ExplainerServiceName(isvc.Name), + Namespace: isvc.Namespace, + Annotations: annotations, + Labels: labels, + }, + Spec: gatewayapiv1.HTTPRouteSpec{ + Hostnames: allowedHosts, + Rules: httpRouteRules, + CommonRouteSpec: gatewayapiv1.CommonRouteSpec{ + ParentRefs: []gatewayapiv1.ParentReference{ + { + Group: (*gatewayapiv1.Group)(&gatewayapiv1.GroupVersion.Group), + Kind: (*gatewayapiv1.Kind)(ptr.To(constants.GatewayKind)), + Namespace: (*gatewayapiv1.Namespace)(&gatewaySlice[0]), + Name: gatewayapiv1.ObjectName(gatewaySlice[1]), + }, + }, + }, + }, + } + return &httpRoute, nil +} + +func createRawTopLevelHTTPRoute(ctx context.Context, isvc *v1beta1.InferenceService, ingressConfig *v1beta1.IngressConfig, isvcConfig *v1beta1.InferenceServicesConfig, + client client.Client, +) (*gatewayapiv1.HTTPRoute, error) { + var httpRouteRules []gatewayapiv1.HTTPRouteRule + var allowedHosts []gatewayapiv1.Hostname + additionalHosts := ingressConfig.AdditionalIngressDomains + + if !isvc.Status.IsConditionReady(v1beta1.PredictorReady) { + isvc.Status.SetCondition(v1beta1.IngressReady, &apis.Condition{ + Type: v1beta1.IngressReady, + Status: corev1.ConditionFalse, + Reason: "Predictor ingress not created", + }) + return nil, nil + } + existing := &corev1.Service{} + predictorName := constants.PredictorServiceName(isvc.Name) + transformerName := constants.TransformerServiceName(isvc.Name) + explainerName := constants.ExplainerServiceName(isvc.Name) + // Check if existing predictor service name has default suffix + err := client.Get(ctx, types.NamespacedName{Name: constants.DefaultPredictorServiceName(isvc.Name), Namespace: isvc.Namespace}, existing) + if err == nil { + // Found existing predictor service with default suffix + predictorName = constants.DefaultPredictorServiceName(isvc.Name) + transformerName = constants.DefaultTransformerServiceName(isvc.Name) + explainerName = constants.DefaultExplainerServiceName(isvc.Name) + } + topLevelHost, err := GenerateDomainName(isvc.Name, isvc.ObjectMeta, ingressConfig) + if err != nil { + return nil, fmt.Errorf("failed to generate top level ingress host: %w", err) + } + allowedHosts = append(allowedHosts, gatewayapiv1.Hostname(topLevelHost)) + // Add additional hosts to allowed hosts + if additionalHosts != nil { + hostMap := make(map[string]bool, len(*additionalHosts)) + for _, host := range *additionalHosts { + hostMap[host] = true + } + for additionalHost := range hostMap { + allowedHosts = append(allowedHosts, gatewayapiv1.Hostname(additionalHost)) + } + } + // Add isvc name and namespace headers + filters := []gatewayapiv1.HTTPRouteFilter{addIsvcHeaders(isvc.Name, isvc.Namespace)} + + if isvc.Spec.Explainer != nil { + // Scenario: When explainer present + if !isvc.Status.IsConditionReady(v1beta1.ExplainerReady) { + isvc.Status.SetCondition(v1beta1.IngressReady, &apis.Condition{ + Type: v1beta1.IngressReady, + Status: corev1.ConditionFalse, + Reason: "Explainer ingress not created", + }) + return nil, nil + } + timeout := DefaultTimeout + if isvc.Spec.Explainer.TimeoutSeconds != nil { + timeout = toGatewayAPIDuration(*isvc.Spec.Explainer.TimeoutSeconds) + } + // Add toplevel host :explain route + // :explain routes to the explainer when there is only explainer + explainRouteMatch := []gatewayapiv1.HTTPRouteMatch{createHTTPRouteMatch(constants.ExplainPrefix())} + httpRouteRules = append(httpRouteRules, createHTTPRouteRule(explainRouteMatch, filters, + explainerName, isvc.Namespace, constants.CommonDefaultHttpPort, timeout)) + } + if isvc.Spec.Transformer != nil { + // Scenario: When predictor with transformer and with/without explainer present + if !isvc.Status.IsConditionReady(v1beta1.TransformerReady) { + isvc.Status.SetCondition(v1beta1.IngressReady, &apis.Condition{ + Type: v1beta1.IngressReady, + Status: corev1.ConditionFalse, + Reason: "Transformer ingress not created", + }) + return nil, nil + } + timeout := DefaultTimeout + if isvc.Spec.Transformer.TimeoutSeconds != nil { + timeout = toGatewayAPIDuration(*isvc.Spec.Transformer.TimeoutSeconds) + } + // :predict routes to the transformer when there are both predictor and transformer + routeMatch := []gatewayapiv1.HTTPRouteMatch{createHTTPRouteMatch(constants.FallbackPrefix())} + httpRouteRules = append(httpRouteRules, createHTTPRouteRule(routeMatch, filters, transformerName, isvc.Namespace, constants.CommonDefaultHttpPort, timeout)) + } else { + // Scenario: When predictor without transformer and with/without explainer present + timeout := DefaultTimeout + if isvc.Spec.Predictor.TimeoutSeconds != nil { + timeout = toGatewayAPIDuration(*isvc.Spec.Predictor.TimeoutSeconds) + } + // Add toplevel host rules for predictor which routes all traffic to predictor + routeMatch := []gatewayapiv1.HTTPRouteMatch{createHTTPRouteMatch(constants.FallbackPrefix())} + httpRouteRules = append(httpRouteRules, createHTTPRouteRule(routeMatch, filters, predictorName, isvc.Namespace, constants.CommonDefaultHttpPort, timeout)) + } + + // Add path based routing rules + if ingressConfig.PathTemplate != "" { + path, err := GenerateUrlPath(isvc.Name, isvc.Namespace, ingressConfig) + if err != nil { + log.Error(err, "Failed to generate URL from pathTemplate") + return nil, fmt.Errorf("failed to generate URL from pathTemplate: %w", err) + } + path = strings.TrimSuffix(path, "/") // remove trailing "/" if present + // Include ingressDomain to the allowed hosts + allowedHosts = append(allowedHosts, gatewayapiv1.Hostname(ingressConfig.IngressDomain)) + + if isvc.Spec.Explainer != nil { + timeout := DefaultTimeout + if isvc.Spec.Explainer.TimeoutSeconds != nil { + timeout = toGatewayAPIDuration(*isvc.Spec.Explainer.TimeoutSeconds) + } + // Add path based routing rule for :explain endpoint + explainerPathRouteMatch := []gatewayapiv1.HTTPRouteMatch{createHTTPRouteMatch(path + constants.PathBasedExplainPrefix())} + httpRouteRules = append(httpRouteRules, createHTTPRouteRule(explainerPathRouteMatch, filters, explainerName, isvc.Namespace, + constants.CommonDefaultHttpPort, timeout)) + } + // Add path based routing rule for :predict endpoint + if isvc.Spec.Transformer != nil { + timeout := DefaultTimeout + if isvc.Spec.Transformer.TimeoutSeconds != nil { + timeout = toGatewayAPIDuration(*isvc.Spec.Transformer.TimeoutSeconds) + } + // :predict routes to the transformer when there are both predictor and transformer + pathRouteMatch := []gatewayapiv1.HTTPRouteMatch{createHTTPRouteMatch(path + "/")} + httpRouteRules = append(httpRouteRules, createHTTPRouteRule(pathRouteMatch, filters, transformerName, isvc.Namespace, + constants.CommonDefaultHttpPort, timeout)) + } else { + timeout := DefaultTimeout + if isvc.Spec.Predictor.TimeoutSeconds != nil { + timeout = toGatewayAPIDuration(*isvc.Spec.Predictor.TimeoutSeconds) + } + // :predict routes to the predictor when there is only predictor + pathRouteMatch := []gatewayapiv1.HTTPRouteMatch{createHTTPRouteMatch(path + "/")} + httpRouteRules = append(httpRouteRules, createHTTPRouteRule(pathRouteMatch, filters, predictorName, isvc.Namespace, + constants.CommonDefaultHttpPort, timeout)) + } + } + + annotations := utils.Filter(isvc.Annotations, func(key string) bool { + return !utils.Includes(isvcConfig.ServiceAnnotationDisallowedList, key) + }) + labels := utils.Filter(isvc.Labels, func(key string) bool { + return !utils.Includes(isvcConfig.ServiceLabelDisallowedList, key) + }) + gatewaySlice := strings.Split(ingressConfig.KserveIngressGateway, "/") + httpRoute := gatewayapiv1.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: isvc.Name, + Namespace: isvc.Namespace, + Annotations: annotations, + Labels: labels, + }, + Spec: gatewayapiv1.HTTPRouteSpec{ + Hostnames: allowedHosts, + Rules: httpRouteRules, + CommonRouteSpec: gatewayapiv1.CommonRouteSpec{ + ParentRefs: []gatewayapiv1.ParentReference{ + { + Group: (*gatewayapiv1.Group)(&gatewayapiv1.GroupVersion.Group), + Kind: (*gatewayapiv1.Kind)(ptr.To(constants.GatewayKind)), + Namespace: (*gatewayapiv1.Namespace)(&gatewaySlice[0]), + Name: gatewayapiv1.ObjectName(gatewaySlice[1]), + }, + }, + }, + }, + } + return &httpRoute, nil +} + +func semanticHttpRouteEquals(desired, existing *gatewayapiv1.HTTPRoute) bool { + return equality.Semantic.DeepEqual(desired.Spec, existing.Spec) +} + +// isHTTPRouteReady checks if the HTTPRoute is ready. If not, returns the reason and message. +func isHTTPRouteReady(httpRouteStatus gatewayapiv1.HTTPRouteStatus) (bool, *string, *string) { + if len(httpRouteStatus.Parents) == 0 { + return false, ptr.To(HTTPRouteParentStatusNotAvailable), ptr.To(HTTPRouteNotReady) + } + for _, parent := range httpRouteStatus.Parents { + for _, condition := range parent.Conditions { + if condition.Status == metav1.ConditionFalse { + return false, &condition.Reason, &condition.Message + } + } + } + return true, nil, nil +} + +func (r *RawHTTPRouteReconciler) reconcilePredictorHTTPRoute(ctx context.Context, isvc *v1beta1.InferenceService) error { + desired, err := createRawPredictorHTTPRoute(ctx, isvc, r.ingressConfig, r.isvcConfig, r.client) + if err != nil { + return err + } + if desired == nil { + return nil + } + if err := controllerutil.SetControllerReference(isvc, desired, r.scheme); err != nil { + log.Error(err, "Failed to set controller reference for predictor HttpRoute", "name", desired.Name) + } + existing := &gatewayapiv1.HTTPRoute{} + err = r.client.Get(ctx, types.NamespacedName{Name: desired.Name, Namespace: isvc.Namespace}, existing) + if err != nil { + if apierr.IsNotFound(err) { + log.Info("Creating Predictor HttpRoute resource", "name", desired.Name) + if err := r.client.Create(ctx, desired); err != nil { + log.Error(err, "Failed to create predictor HttpRoute", "name", desired.Name) + return err + } + } else { + return err + } + } else { + // Set ResourceVersion which is required for update operation. + desired.ResourceVersion = existing.ResourceVersion + // Do a dry-run update to avoid diffs generated by default values. + // This will populate our local httpRoute with any default values that are present on the remote version. + if err := r.client.Update(ctx, desired, client.DryRunAll); err != nil { + log.Error(err, "Failed to perform dry-run update for predictor HttpRoute", "name", desired.Name) + return err + } + if !semanticHttpRouteEquals(desired, existing) { + if err := r.client.Update(ctx, desired); err != nil { + log.Error(err, "Failed to update predictor HttpRoute", "name", desired.Name) + } + } + } + return nil +} + +func (r *RawHTTPRouteReconciler) reconcileTransformerHTTPRoute(ctx context.Context, isvc *v1beta1.InferenceService) error { + desired, err := createRawTransformerHTTPRoute(ctx, isvc, r.ingressConfig, r.isvcConfig, r.client) + if err != nil { + return err + } + if desired == nil { + return nil + } + if err := controllerutil.SetControllerReference(isvc, desired, r.scheme); err != nil { + log.Error(err, "Failed to set controller reference for transformer HttpRoute", "name", desired.Name) + } + existing := &gatewayapiv1.HTTPRoute{} + err = r.client.Get(ctx, types.NamespacedName{Name: desired.Name, Namespace: isvc.Namespace}, existing) + if err != nil { + if apierr.IsNotFound(err) { + log.Info("Creating transformer HttpRoute resource", "name", desired.Name) + if err := r.client.Create(ctx, desired); err != nil { + log.Error(err, "Failed to create transformer HttpRoute", "name", desired.Name) + return err + } + } else { + return err + } + } else { + // Set ResourceVersion which is required for update operation. + desired.ResourceVersion = existing.ResourceVersion + // Do a dry-run update to avoid diffs generated by default values. + // This will populate our local httpRoute with any default values that are present on the remote version. + if err := r.client.Update(ctx, desired, client.DryRunAll); err != nil { + log.Error(err, "Failed to perform dry-run update for transformer HttpRoute", "name", desired.Name) + return err + } + if !semanticHttpRouteEquals(desired, existing) { + if err := r.client.Update(ctx, desired); err != nil { + log.Error(err, "Failed to update transformer HttpRoute", "name", desired.Name) + } + } + } + return nil +} + +func (r *RawHTTPRouteReconciler) reconcileExplainerHTTPRoute(ctx context.Context, isvc *v1beta1.InferenceService) error { + desired, err := createRawExplainerHTTPRoute(ctx, isvc, r.ingressConfig, r.isvcConfig, r.client) + if err != nil { + return err + } + if desired == nil { + return nil + } + if err := controllerutil.SetControllerReference(isvc, desired, r.scheme); err != nil { + log.Error(err, "Failed to set controller reference for explainer HttpRoute", "name", desired.Name) + } + existing := &gatewayapiv1.HTTPRoute{} + err = r.client.Get(ctx, types.NamespacedName{Name: desired.Name, Namespace: isvc.Namespace}, existing) + if err != nil { + if apierr.IsNotFound(err) { + log.Info("Creating explainer HttpRoute resource", "name", desired.Name) + if err := r.client.Create(ctx, desired); err != nil { + log.Error(err, "Failed to create explainer HttpRoute", "name", desired.Name) + return err + } + } else { + return err + } + } else { + // Set ResourceVersion which is required for update operation. + desired.ResourceVersion = existing.ResourceVersion + // Do a dry-run update to avoid diffs generated by default values. + // This will populate our local httpRoute with any default values that are present on the remote version. + if err := r.client.Update(ctx, desired, client.DryRunAll); err != nil { + log.Error(err, "Failed to perform dry-run update for explainer HttpRoute", "name", desired.Name) + return err + } + if !semanticHttpRouteEquals(desired, existing) { + if err := r.client.Update(ctx, desired); err != nil { + log.Error(err, "Failed to update explainer HttpRoute", "name", desired.Name) + } + } + } + return nil +} + +func (r *RawHTTPRouteReconciler) reconcileTopLevelHTTPRoute(ctx context.Context, isvc *v1beta1.InferenceService) error { + desired, err := createRawTopLevelHTTPRoute(ctx, isvc, r.ingressConfig, r.isvcConfig, r.client) + if err != nil { + return err + } + if desired == nil { + return nil + } + if err := controllerutil.SetControllerReference(isvc, desired, r.scheme); err != nil { + log.Error(err, "Failed to set controller reference for top level HttpRoute", "name", isvc.Name) + return err + } + // reconcile httpRoute + existingHttpRoute := &gatewayapiv1.HTTPRoute{} + err = r.client.Get(ctx, types.NamespacedName{ + Namespace: isvc.Namespace, + Name: isvc.Name, + }, existingHttpRoute) + if err != nil { + if apierr.IsNotFound(err) { + log.Info("Creating top level HttpRoute resource", "name", isvc.Name) + if err := r.client.Create(ctx, desired); err != nil { + log.Error(err, "Failed to create top level HttpRoute", "name", desired.Name) + return err + } + } else { + return err + } + } else { + // Set ResourceVersion which is required for update operation. + desired.ResourceVersion = existingHttpRoute.ResourceVersion + // Do a dry-run update to avoid diffs generated by default values. + // This will populate our local httpRoute with any default values that are present on the remote version. + if err := r.client.Update(ctx, desired, client.DryRunAll); err != nil { + log.Error(err, "Failed to perform dry-run update on top level httpRoute", "name", desired.Name) + return err + } + if !semanticHttpRouteEquals(desired, existingHttpRoute) { + if err = r.client.Update(ctx, desired); err != nil { + log.Error(err, "Failed to update toplevel HttpRoute", "name", isvc.Name) + return err + } + } + } + return nil +} + +// ReconcileHTTPRoute reconciles the HTTPRoute resource +func (r *RawHTTPRouteReconciler) Reconcile(ctx context.Context, isvc *v1beta1.InferenceService) error { + var err error + isInternal := false + // disable ingress creation if service is labelled with cluster local or kserve domain is cluster local + if val, ok := isvc.Labels[constants.NetworkVisibility]; ok && val == constants.ClusterLocalVisibility { + isInternal = true + } + if r.ingressConfig.IngressDomain == constants.ClusterLocalDomain { + isInternal = true + } + if !isInternal && !r.ingressConfig.DisableIngressCreation { + if err := r.reconcilePredictorHTTPRoute(ctx, isvc); err != nil { + return err + } + if isvc.Spec.Transformer != nil { + if err := r.reconcileTransformerHTTPRoute(ctx, isvc); err != nil { + return err + } + } + if isvc.Spec.Explainer != nil { + if err := r.reconcileExplainerHTTPRoute(ctx, isvc); err != nil { + return err + } + } + if err := r.reconcileTopLevelHTTPRoute(ctx, isvc); err != nil { + return err + } + + // Check Predictor HTTPRoute status + httpRoute := &gatewayapiv1.HTTPRoute{} + if err := r.client.Get(ctx, types.NamespacedName{ + Name: constants.PredictorServiceName(isvc.Name), + Namespace: isvc.Namespace, + }, httpRoute); err != nil { + return err + } + if ready, reason, message := isHTTPRouteReady(httpRoute.Status); !ready { + log.Info("Predictor HTTPRoute not ready", "reason", *reason, "message", *message) + isvc.Status.SetCondition(v1beta1.IngressReady, &apis.Condition{ + Type: v1beta1.IngressReady, + Status: corev1.ConditionFalse, + Reason: *reason, + Message: fmt.Sprintf("%s %s", "Predictor", *message), + }) + return nil + } + // Check Transformer HTTPRoute stauts + if isvc.Spec.Transformer != nil { + httpRoute = &gatewayapiv1.HTTPRoute{} + if err := r.client.Get(ctx, types.NamespacedName{ + Name: constants.TransformerServiceName(isvc.Name), + Namespace: isvc.Namespace, + }, httpRoute); err != nil { + return err + } + if ready, reason, message := isHTTPRouteReady(httpRoute.Status); !ready { + isvc.Status.SetCondition(v1beta1.IngressReady, &apis.Condition{ + Type: v1beta1.IngressReady, + Status: corev1.ConditionFalse, + Reason: *reason, + Message: fmt.Sprintf("%s %s", "Transformer", *message), + }) + return nil + } + } + // Check Explainer HTTPRoute stauts + if isvc.Spec.Explainer != nil { + httpRoute = &gatewayapiv1.HTTPRoute{} + if err := r.client.Get(ctx, types.NamespacedName{ + Name: constants.ExplainerServiceName(isvc.Name), + Namespace: isvc.Namespace, + }, httpRoute); err != nil { + return err + } + if ready, reason, message := isHTTPRouteReady(httpRoute.Status); !ready { + isvc.Status.SetCondition(v1beta1.IngressReady, &apis.Condition{ + Type: v1beta1.IngressReady, + Status: corev1.ConditionFalse, + Reason: *reason, + Message: fmt.Sprintf("%s %s", "Explainer", *message), + }) + return nil + } + } + // Check Top level HTTPRoute status + httpRoute = &gatewayapiv1.HTTPRoute{} + if err := r.client.Get(ctx, types.NamespacedName{ + Name: isvc.Name, + Namespace: isvc.Namespace, + }, httpRoute); err != nil { + return err + } + if ready, reason, message := isHTTPRouteReady(httpRoute.Status); !ready { + log.Info("Top level HTTPRoute not ready", "reason", *reason, "message", *message) + isvc.Status.SetCondition(v1beta1.IngressReady, &apis.Condition{ + Type: v1beta1.IngressReady, + Status: corev1.ConditionFalse, + Reason: *reason, + Message: fmt.Sprintf("%s %s", "TopLevel", *message), + }) + return nil + } + // If we are here, then all the HTTPRoutes are ready, Mark ingress as ready + isvc.Status.SetCondition(v1beta1.IngressReady, &apis.Condition{ + Type: v1beta1.IngressReady, + Status: corev1.ConditionTrue, + }) + } else { + // Ingress creation is disabled. We set it to true as the isvc condition depends on it. + isvc.Status.SetCondition(v1beta1.IngressReady, &apis.Condition{ + Type: v1beta1.IngressReady, + Status: corev1.ConditionTrue, + }) + } + isvc.Status.URL, err = createRawURL(isvc, r.ingressConfig) + if err != nil { + return err + } + isvc.Status.Address = &duckv1.Addressable{ + URL: &apis.URL{ + Host: getRawServiceHost(ctx, isvc, r.client), + Scheme: r.ingressConfig.UrlScheme, + Path: "", + }, + } + return nil +} diff --git a/pkg/controller/v1beta1/inferenceservice/reconcilers/ingress/httproute_reconciler_test.go b/pkg/controller/v1beta1/inferenceservice/reconcilers/ingress/httproute_reconciler_test.go new file mode 100644 index 00000000000..7debe8f4e32 --- /dev/null +++ b/pkg/controller/v1beta1/inferenceservice/reconcilers/ingress/httproute_reconciler_test.go @@ -0,0 +1,2092 @@ +/* +Copyright 2024 The KServe Authors. + +Licensed 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. +*/ + +package ingress + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp/cmpopts" + . "github.com/onsi/gomega" + "github.com/onsi/gomega/format" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/utils/ptr" + "knative.dev/pkg/apis" + duckv1 "knative.dev/pkg/apis/duck/v1" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + gatewayapiv1 "sigs.k8s.io/gateway-api/apis/v1" + + "github.com/kserve/kserve/pkg/apis/serving/v1beta1" + "github.com/kserve/kserve/pkg/constants" +) + +func TestCreateRawURL(t *testing.T) { + g := NewGomegaWithT(t) + + testCases := map[string]struct { + isvc *v1beta1.InferenceService + ingressConfig *v1beta1.IngressConfig + expectedURL string + isErrorExpected bool + }{ + "basic case": { + isvc: &v1beta1.InferenceService{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-isvc", + Namespace: "default", + }, + }, + ingressConfig: &v1beta1.IngressConfig{ + IngressDomain: "example.com", + UrlScheme: "http", + DomainTemplate: "{{.Name}}-{{.Namespace}}.{{.IngressDomain}}", + }, + isErrorExpected: false, + expectedURL: "http://test-isvc-default.example.com", + }, + "basic case with empty domain template": { + isvc: &v1beta1.InferenceService{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-isvc", + Namespace: "default", + }, + }, + ingressConfig: &v1beta1.IngressConfig{ + IngressDomain: "example.com", + UrlScheme: "http", + DomainTemplate: "", + }, + expectedURL: "", + isErrorExpected: true, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + url, err := createRawURL(tc.isvc, tc.ingressConfig) + if tc.isErrorExpected { + g.Expect(err).To(HaveOccurred()) + } else { + g.Expect(err).ToNot(HaveOccurred()) + } + g.Expect(tc.expectedURL).To(BeComparableTo(url.String())) + }) + } +} + +func TestGetRawServiceHost(t *testing.T) { + g := NewGomegaWithT(t) + testCases := map[string]struct { + isvc *v1beta1.InferenceService + expectedHost string + }{ + "basic case": { + isvc: &v1beta1.InferenceService{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-isvc", + Namespace: "default", + }, + Spec: v1beta1.InferenceServiceSpec{ + Predictor: v1beta1.PredictorSpec{}, + }, + }, + expectedHost: "test-isvc-predictor.default.svc.cluster.local", + }, + "basic case with transformer": { + isvc: &v1beta1.InferenceService{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-isvc", + Namespace: "default", + }, + Spec: v1beta1.InferenceServiceSpec{ + Predictor: v1beta1.PredictorSpec{}, + Transformer: &v1beta1.TransformerSpec{}, + }, + }, + expectedHost: "test-isvc-transformer.default.svc.cluster.local", + }, + "predictor with default suffix": { + isvc: &v1beta1.InferenceService{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-isvc-pred-default", + Namespace: "default", + }, + Spec: v1beta1.InferenceServiceSpec{ + Predictor: v1beta1.PredictorSpec{}, + }, + }, + expectedHost: "test-isvc-pred-default-predictor.default.svc.cluster.local", + }, + "transformer with default suffix": { + isvc: &v1beta1.InferenceService{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-isvc-pred-default", + Namespace: "default", + }, + Spec: v1beta1.InferenceServiceSpec{ + Predictor: v1beta1.PredictorSpec{}, + Transformer: &v1beta1.TransformerSpec{}, + }, + }, + expectedHost: "test-isvc-pred-default-transformer.default.svc.cluster.local", + }, + } + + s := scheme.Scheme + s.AddKnownTypes(v1beta1.SchemeGroupVersion, &v1beta1.InferenceService{}) + client := fake.NewClientBuilder().WithScheme(s).Build() + // Create a dummy service to test default suffix cases + client.Create(context.Background(), &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-isvc-pred-default", + Namespace: "default", + }, + Spec: corev1.ServiceSpec{}, + }) + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + host := getRawServiceHost(context.Background(), tc.isvc, client) + g.Expect(tc.expectedHost).To(BeComparableTo(host)) + }) + } +} + +func TestCreateHTTPRouteMatch(t *testing.T) { + g := NewGomegaWithT(t) + testCases := map[string]struct { + prefix string + expectedHTTPRoutes gatewayapiv1.HTTPRouteMatch + }{ + "basic case": { + prefix: "^.*$", + expectedHTTPRoutes: gatewayapiv1.HTTPRouteMatch{ + Path: &gatewayapiv1.HTTPPathMatch{ + Type: ptr.To(gatewayapiv1.PathMatchRegularExpression), + Value: ptr.To("^.*$"), + }, + }, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + matches := createHTTPRouteMatch(tc.prefix) + g.Expect(matches).To(BeComparableTo(tc.expectedHTTPRoutes)) + }) + } +} + +func TestAddIsvcHeaders(t *testing.T) { + g := NewGomegaWithT(t) + testCases := map[string]struct { + isvcName string + isvcNamespace string + }{ + "basic case": { + isvcName: "test-isvc", + isvcNamespace: "default", + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + headers := addIsvcHeaders(tc.isvcName, tc.isvcNamespace) + g.Expect(headers.Type).To(BeComparableTo(gatewayapiv1.HTTPRouteFilterRequestHeaderModifier)) + g.Expect(headers.RequestHeaderModifier.Set).To(HaveLen(2)) + g.Expect(string(headers.RequestHeaderModifier.Set[0].Name)).To(BeComparableTo(constants.IsvcNameHeader)) + g.Expect(headers.RequestHeaderModifier.Set[0].Value).To(BeComparableTo(tc.isvcName)) + g.Expect(string(headers.RequestHeaderModifier.Set[1].Name)).To(BeComparableTo(constants.IsvcNamespaceHeader)) + g.Expect(headers.RequestHeaderModifier.Set[1].Value).To(BeComparableTo(tc.isvcNamespace)) + }) + } +} + +func TestCreateHTTPRouteRule(t *testing.T) { + g := NewGomegaWithT(t) + testCases := map[string]struct { + matches []gatewayapiv1.HTTPRouteMatch + filters []gatewayapiv1.HTTPRouteFilter + serviceName string + servicePort int32 + expectedRules int + }{ + "basic case": { + matches: []gatewayapiv1.HTTPRouteMatch{ + { + Path: &gatewayapiv1.HTTPPathMatch{ + Type: ptr.To(gatewayapiv1.PathMatchRegularExpression), + Value: ptr.To("/predict"), + }, + }, + }, + filters: []gatewayapiv1.HTTPRouteFilter{ + addIsvcHeaders("test-isvc", "default"), + }, + serviceName: "test-service", + servicePort: 80, + expectedRules: 1, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + rule := createHTTPRouteRule(tc.matches, tc.filters, tc.serviceName, "default", tc.servicePort, DefaultTimeout) + g.Expect(rule.Matches).To(HaveLen(tc.expectedRules)) + g.Expect(rule.Filters).To(HaveLen(tc.expectedRules)) + g.Expect(rule.BackendRefs).To(HaveLen(tc.expectedRules)) + g.Expect(string(rule.BackendRefs[0].Name)).To(BeComparableTo(tc.serviceName)) + }) + } +} + +func TestSemanticHttpRouteEquals(t *testing.T) { + g := NewGomegaWithT(t) + testCases := map[string]struct { + desired *gatewayapiv1.HTTPRoute + existing *gatewayapiv1.HTTPRoute + expectedEqual bool + }{ + "equal routes": { + desired: &gatewayapiv1.HTTPRoute{ + Spec: gatewayapiv1.HTTPRouteSpec{ + Hostnames: []gatewayapiv1.Hostname{"example.com"}, + }, + }, + existing: &gatewayapiv1.HTTPRoute{ + Spec: gatewayapiv1.HTTPRouteSpec{ + Hostnames: []gatewayapiv1.Hostname{"example.com"}, + }, + }, + expectedEqual: true, + }, + "different routes": { + desired: &gatewayapiv1.HTTPRoute{ + Spec: gatewayapiv1.HTTPRouteSpec{ + Hostnames: []gatewayapiv1.Hostname{"example.com"}, + }, + }, + existing: &gatewayapiv1.HTTPRoute{ + Spec: gatewayapiv1.HTTPRouteSpec{ + Hostnames: []gatewayapiv1.Hostname{"different.com"}, + }, + }, + expectedEqual: false, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + g.Expect(semanticHttpRouteEquals(tc.desired, tc.existing)).To(BeComparableTo(tc.expectedEqual)) + }) + } +} + +func TestIsHTTPRouteReady(t *testing.T) { + g := NewGomegaWithT(t) + testCases := map[string]struct { + httpRouteStatus gatewayapiv1.HTTPRouteStatus + expectedReady bool + expectedReason *string + expectedMessage *string + }{ + "route accepted": { + httpRouteStatus: gatewayapiv1.HTTPRouteStatus{ + RouteStatus: gatewayapiv1.RouteStatus{ + Parents: []gatewayapiv1.RouteParentStatus{ + { + Conditions: []metav1.Condition{ + { + Type: string(gatewayapiv1.RouteConditionAccepted), + Status: metav1.ConditionTrue, + }, + { + Type: string(gatewayapiv1.RouteConditionResolvedRefs), + Status: metav1.ConditionTrue, + }, + }, + }, + }, + }, + }, + expectedReady: true, + expectedReason: nil, + expectedMessage: nil, + }, + "route not accepted": { + httpRouteStatus: gatewayapiv1.HTTPRouteStatus{ + RouteStatus: gatewayapiv1.RouteStatus{ + Parents: []gatewayapiv1.RouteParentStatus{ + { + Conditions: []metav1.Condition{ + { + Type: string(gatewayapiv1.RouteConditionAccepted), + Status: metav1.ConditionFalse, + Reason: "Route not accepted", + Message: "Route not accepted", + }, + { + Type: string(gatewayapiv1.RouteConditionResolvedRefs), + Status: metav1.ConditionTrue, + }, + }, + }, + }, + }, + }, + expectedReady: false, + expectedReason: ptr.To("Route not accepted"), + expectedMessage: ptr.To("Route not accepted"), + }, + "no parent status": { + httpRouteStatus: gatewayapiv1.HTTPRouteStatus{ + RouteStatus: gatewayapiv1.RouteStatus{ + Parents: []gatewayapiv1.RouteParentStatus{}, + }, + }, + expectedReady: false, + expectedReason: ptr.To(HTTPRouteParentStatusNotAvailable), + expectedMessage: ptr.To(HTTPRouteNotReady), + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + ready, reason, message := isHTTPRouteReady(tc.httpRouteStatus) + g.Expect(ready).To(BeComparableTo(tc.expectedReady)) + g.Expect(reason).To(BeComparableTo(tc.expectedReason)) + g.Expect(message).To(BeComparableTo(tc.expectedMessage)) + }) + } +} + +func TestCreateRawTopLevelHTTPRoute(t *testing.T) { + format.MaxLength = 0 + g := NewGomegaWithT(t) + testCases := map[string]struct { + isvc *v1beta1.InferenceService + ingressConfig *v1beta1.IngressConfig + expected *gatewayapiv1.HTTPRoute + }{ + "Predictor ready": { + isvc: &v1beta1.InferenceService{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-isvc", + Namespace: "default", + }, + Spec: v1beta1.InferenceServiceSpec{ + Predictor: v1beta1.PredictorSpec{}, + }, + Status: v1beta1.InferenceServiceStatus{ + Status: duckv1.Status{ + Conditions: []apis.Condition{ + { + Type: v1beta1.PredictorReady, + Status: corev1.ConditionTrue, + }, + }, + }, + }, + }, + ingressConfig: &v1beta1.IngressConfig{ + IngressDomain: "example.com", + UrlScheme: "http", + DomainTemplate: "{{.Name}}-{{.Namespace}}.{{.IngressDomain}}", + KserveIngressGateway: "kserve/kserve-gateway", + AdditionalIngressDomains: &[]string{"additional.example.com"}, + EnableGatewayAPI: true, + }, + expected: &gatewayapiv1.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-isvc", + Namespace: "default", + Annotations: map[string]string{}, + Labels: map[string]string{}, + }, + Spec: gatewayapiv1.HTTPRouteSpec{ + Hostnames: []gatewayapiv1.Hostname{"test-isvc-default.example.com", "additional.example.com"}, + Rules: []gatewayapiv1.HTTPRouteRule{ + { + Matches: []gatewayapiv1.HTTPRouteMatch{ + { + Path: &gatewayapiv1.HTTPPathMatch{ + Type: ptr.To(gatewayapiv1.PathMatchRegularExpression), + Value: ptr.To("^/.*$"), + }, + }, + }, + Filters: []gatewayapiv1.HTTPRouteFilter{ + { + Type: gatewayapiv1.HTTPRouteFilterRequestHeaderModifier, + RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ + Set: []gatewayapiv1.HTTPHeader{ + { + Name: constants.IsvcNameHeader, + Value: "test-isvc", + }, + { + Name: constants.IsvcNamespaceHeader, + Value: "default", + }, + }, + }, + }, + }, + BackendRefs: []gatewayapiv1.HTTPBackendRef{ + { + BackendRef: gatewayapiv1.BackendRef{ + BackendObjectReference: gatewayapiv1.BackendObjectReference{ + Kind: ptr.To(gatewayapiv1.Kind(constants.ServiceKind)), + Name: "test-isvc-predictor", + Namespace: (*gatewayapiv1.Namespace)(ptr.To("default")), + Port: (*gatewayapiv1.PortNumber)(ptr.To(int32(constants.CommonDefaultHttpPort))), + }, + }, + }, + }, + Timeouts: &gatewayapiv1.HTTPRouteTimeouts{ + Request: ptr.To(gatewayapiv1.Duration("60s")), + }, + }, + }, + CommonRouteSpec: gatewayapiv1.CommonRouteSpec{ + ParentRefs: []gatewayapiv1.ParentReference{ + { + Name: "kserve-gateway", + Kind: ptr.To(gatewayapiv1.Kind(constants.GatewayKind)), + Group: (*gatewayapiv1.Group)(&gatewayapiv1.GroupVersion.Group), + Namespace: ptr.To(gatewayapiv1.Namespace("kserve")), + }, + }, + }, + }, + }, + }, + "When predictor not ready, httproute should not be created": { + isvc: &v1beta1.InferenceService{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-isvc", + Namespace: "default", + }, + Spec: v1beta1.InferenceServiceSpec{ + Predictor: v1beta1.PredictorSpec{}, + }, + Status: v1beta1.InferenceServiceStatus{ + Status: duckv1.Status{ + Conditions: []apis.Condition{ + { + Type: v1beta1.PredictorReady, + Status: corev1.ConditionFalse, + }, + }, + }, + }, + }, + ingressConfig: &v1beta1.IngressConfig{ + IngressDomain: "example.com", + UrlScheme: "http", + DomainTemplate: "{{.Name}}-{{.Namespace}}.{{.IngressDomain}}", + KserveIngressGateway: "kserve/kserve-gateway", + AdditionalIngressDomains: &[]string{"additional.example.com"}, + EnableGatewayAPI: true, + }, + expected: nil, + }, + "With transformer ready": { + isvc: &v1beta1.InferenceService{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-isvc", + Namespace: "default", + }, + Spec: v1beta1.InferenceServiceSpec{ + Predictor: v1beta1.PredictorSpec{}, + Transformer: &v1beta1.TransformerSpec{}, + }, + Status: v1beta1.InferenceServiceStatus{ + Status: duckv1.Status{ + Conditions: []apis.Condition{ + { + Type: v1beta1.TransformerReady, + Status: corev1.ConditionTrue, + }, + { + Type: v1beta1.PredictorReady, + Status: corev1.ConditionTrue, + }, + }, + }, + }, + }, + ingressConfig: &v1beta1.IngressConfig{ + IngressDomain: "example.com", + UrlScheme: "http", + DomainTemplate: "{{.Name}}-{{.Namespace}}.{{.IngressDomain}}", + KserveIngressGateway: "kserve/kserve-gateway", + AdditionalIngressDomains: &[]string{"additional.example.com"}, + EnableGatewayAPI: true, + }, + expected: &gatewayapiv1.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-isvc", + Namespace: "default", + Annotations: map[string]string{}, + Labels: map[string]string{}, + }, + Spec: gatewayapiv1.HTTPRouteSpec{ + Hostnames: []gatewayapiv1.Hostname{"test-isvc-default.example.com", "additional.example.com"}, + Rules: []gatewayapiv1.HTTPRouteRule{ + { + Matches: []gatewayapiv1.HTTPRouteMatch{ + { + Path: &gatewayapiv1.HTTPPathMatch{ + Type: ptr.To(gatewayapiv1.PathMatchRegularExpression), + Value: ptr.To("^/.*$"), + }, + }, + }, + Filters: []gatewayapiv1.HTTPRouteFilter{ + { + Type: gatewayapiv1.HTTPRouteFilterRequestHeaderModifier, + RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ + Set: []gatewayapiv1.HTTPHeader{ + { + Name: constants.IsvcNameHeader, + Value: "test-isvc", + }, + { + Name: constants.IsvcNamespaceHeader, + Value: "default", + }, + }, + }, + }, + }, + BackendRefs: []gatewayapiv1.HTTPBackendRef{ + { + BackendRef: gatewayapiv1.BackendRef{ + BackendObjectReference: gatewayapiv1.BackendObjectReference{ + Kind: ptr.To(gatewayapiv1.Kind(constants.ServiceKind)), + Name: "test-isvc-transformer", + Namespace: (*gatewayapiv1.Namespace)(ptr.To("default")), + Port: (*gatewayapiv1.PortNumber)(ptr.To(int32(constants.CommonDefaultHttpPort))), + }, + }, + }, + }, + Timeouts: &gatewayapiv1.HTTPRouteTimeouts{ + Request: ptr.To(gatewayapiv1.Duration("60s")), + }, + }, + }, + CommonRouteSpec: gatewayapiv1.CommonRouteSpec{ + ParentRefs: []gatewayapiv1.ParentReference{ + { + Name: "kserve-gateway", + Kind: ptr.To(gatewayapiv1.Kind(constants.GatewayKind)), + Group: (*gatewayapiv1.Group)(&gatewayapiv1.GroupVersion.Group), + Namespace: ptr.To(gatewayapiv1.Namespace("kserve")), + }, + }, + }, + }, + }, + }, + "When transformer not ready, httproute should not be created": { + isvc: &v1beta1.InferenceService{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-isvc", + Namespace: "default", + }, + Spec: v1beta1.InferenceServiceSpec{ + Predictor: v1beta1.PredictorSpec{}, + Transformer: &v1beta1.TransformerSpec{}, + }, + Status: v1beta1.InferenceServiceStatus{ + Status: duckv1.Status{ + Conditions: []apis.Condition{ + { + Type: v1beta1.TransformerReady, + Status: corev1.ConditionFalse, + }, + { + Type: v1beta1.PredictorReady, + Status: corev1.ConditionTrue, + }, + }, + }, + }, + }, + ingressConfig: &v1beta1.IngressConfig{ + IngressDomain: "example.com", + UrlScheme: "http", + DomainTemplate: "{{.Name}}-{{.Namespace}}.{{.IngressDomain}}", + KserveIngressGateway: "kserve/kserve-gateway", + AdditionalIngressDomains: &[]string{"additional.example.com"}, + EnableGatewayAPI: true, + }, + expected: nil, + }, + "With explainer ready": { + isvc: &v1beta1.InferenceService{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-isvc", + Namespace: "default", + }, + Spec: v1beta1.InferenceServiceSpec{ + Predictor: v1beta1.PredictorSpec{}, + Explainer: &v1beta1.ExplainerSpec{}, + }, + Status: v1beta1.InferenceServiceStatus{ + Status: duckv1.Status{ + Conditions: []apis.Condition{ + { + Type: v1beta1.ExplainerReady, + Status: corev1.ConditionTrue, + }, + { + Type: v1beta1.PredictorReady, + Status: corev1.ConditionTrue, + }, + }, + }, + }, + }, + ingressConfig: &v1beta1.IngressConfig{ + IngressDomain: "example.com", + UrlScheme: "http", + DomainTemplate: "{{.Name}}-{{.Namespace}}.{{.IngressDomain}}", + KserveIngressGateway: "kserve/kserve-gateway", + AdditionalIngressDomains: &[]string{"additional.example.com"}, + EnableGatewayAPI: true, + }, + expected: &gatewayapiv1.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-isvc", + Namespace: "default", + Annotations: map[string]string{}, + Labels: map[string]string{}, + }, + Spec: gatewayapiv1.HTTPRouteSpec{ + Hostnames: []gatewayapiv1.Hostname{"test-isvc-default.example.com", "additional.example.com"}, + Rules: []gatewayapiv1.HTTPRouteRule{ + { + Matches: []gatewayapiv1.HTTPRouteMatch{ + { + Path: &gatewayapiv1.HTTPPathMatch{ + Type: ptr.To(gatewayapiv1.PathMatchRegularExpression), + Value: ptr.To(constants.ExplainPrefix()), + }, + }, + }, + Filters: []gatewayapiv1.HTTPRouteFilter{ + { + Type: gatewayapiv1.HTTPRouteFilterRequestHeaderModifier, + RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ + Set: []gatewayapiv1.HTTPHeader{ + { + Name: constants.IsvcNameHeader, + Value: "test-isvc", + }, + { + Name: constants.IsvcNamespaceHeader, + Value: "default", + }, + }, + }, + }, + }, + BackendRefs: []gatewayapiv1.HTTPBackendRef{ + { + BackendRef: gatewayapiv1.BackendRef{ + BackendObjectReference: gatewayapiv1.BackendObjectReference{ + Kind: ptr.To(gatewayapiv1.Kind(constants.ServiceKind)), + Name: "test-isvc-explainer", + Namespace: (*gatewayapiv1.Namespace)(ptr.To("default")), + Port: (*gatewayapiv1.PortNumber)(ptr.To(int32(constants.CommonDefaultHttpPort))), + }, + }, + }, + }, + Timeouts: &gatewayapiv1.HTTPRouteTimeouts{ + Request: ptr.To(gatewayapiv1.Duration("60s")), + }, + }, + { + Matches: []gatewayapiv1.HTTPRouteMatch{ + { + Path: &gatewayapiv1.HTTPPathMatch{ + Type: ptr.To(gatewayapiv1.PathMatchRegularExpression), + Value: ptr.To("^/.*$"), + }, + }, + }, + Filters: []gatewayapiv1.HTTPRouteFilter{ + { + Type: gatewayapiv1.HTTPRouteFilterRequestHeaderModifier, + RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ + Set: []gatewayapiv1.HTTPHeader{ + { + Name: constants.IsvcNameHeader, + Value: "test-isvc", + }, + { + Name: constants.IsvcNamespaceHeader, + Value: "default", + }, + }, + }, + }, + }, + BackendRefs: []gatewayapiv1.HTTPBackendRef{ + { + BackendRef: gatewayapiv1.BackendRef{ + BackendObjectReference: gatewayapiv1.BackendObjectReference{ + Kind: ptr.To(gatewayapiv1.Kind(constants.ServiceKind)), + Name: "test-isvc-predictor", + Namespace: (*gatewayapiv1.Namespace)(ptr.To("default")), + Port: (*gatewayapiv1.PortNumber)(ptr.To(int32(constants.CommonDefaultHttpPort))), + }, + }, + }, + }, + Timeouts: &gatewayapiv1.HTTPRouteTimeouts{ + Request: ptr.To(gatewayapiv1.Duration("60s")), + }, + }, + }, + CommonRouteSpec: gatewayapiv1.CommonRouteSpec{ + ParentRefs: []gatewayapiv1.ParentReference{ + { + Name: "kserve-gateway", + Kind: ptr.To(gatewayapiv1.Kind(constants.GatewayKind)), + Group: (*gatewayapiv1.Group)(&gatewayapiv1.GroupVersion.Group), + Namespace: ptr.To(gatewayapiv1.Namespace("kserve")), + }, + }, + }, + }, + }, + }, + "When explainer not ready, httproute should not be created": { + isvc: &v1beta1.InferenceService{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-isvc", + Namespace: "default", + }, + Spec: v1beta1.InferenceServiceSpec{ + Predictor: v1beta1.PredictorSpec{}, + Explainer: &v1beta1.ExplainerSpec{}, + }, + Status: v1beta1.InferenceServiceStatus{ + Status: duckv1.Status{ + Conditions: []apis.Condition{ + { + Type: v1beta1.ExplainerReady, + Status: corev1.ConditionFalse, + }, + { + Type: v1beta1.PredictorReady, + Status: corev1.ConditionTrue, + }, + }, + }, + }, + }, + ingressConfig: &v1beta1.IngressConfig{ + IngressDomain: "example.com", + UrlScheme: "http", + DomainTemplate: "{{.Name}}-{{.Namespace}}.{{.IngressDomain}}", + KserveIngressGateway: "kserve/kserve-gateway", + AdditionalIngressDomains: &[]string{"additional.example.com"}, + EnableGatewayAPI: true, + }, + expected: nil, + }, + "Path based routing with explainer": { + isvc: &v1beta1.InferenceService{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-isvc", + Namespace: "default", + }, + Spec: v1beta1.InferenceServiceSpec{ + Predictor: v1beta1.PredictorSpec{}, + Explainer: &v1beta1.ExplainerSpec{}, + }, + Status: v1beta1.InferenceServiceStatus{ + Status: duckv1.Status{ + Conditions: []apis.Condition{ + { + Type: v1beta1.ExplainerReady, + Status: corev1.ConditionTrue, + }, + { + Type: v1beta1.PredictorReady, + Status: corev1.ConditionTrue, + }, + }, + }, + }, + }, + ingressConfig: &v1beta1.IngressConfig{ + IngressDomain: "example.com", + UrlScheme: "http", + DomainTemplate: "{{.Name}}-{{.Namespace}}.{{.IngressDomain}}", + KserveIngressGateway: "kserve/kserve-gateway", + AdditionalIngressDomains: &[]string{"additional.example.com"}, + PathTemplate: "/serving/{{ .Namespace }}/{{ .Name }}", + EnableGatewayAPI: true, + }, + expected: &gatewayapiv1.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-isvc", + Namespace: "default", + Annotations: map[string]string{}, + Labels: map[string]string{}, + }, + Spec: gatewayapiv1.HTTPRouteSpec{ + Hostnames: []gatewayapiv1.Hostname{"test-isvc-default.example.com", "additional.example.com", "example.com"}, + Rules: []gatewayapiv1.HTTPRouteRule{ + { + Matches: []gatewayapiv1.HTTPRouteMatch{ + { + Path: &gatewayapiv1.HTTPPathMatch{ + Type: ptr.To(gatewayapiv1.PathMatchRegularExpression), + Value: ptr.To(constants.ExplainPrefix()), + }, + }, + }, + Filters: []gatewayapiv1.HTTPRouteFilter{ + { + Type: gatewayapiv1.HTTPRouteFilterRequestHeaderModifier, + RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ + Set: []gatewayapiv1.HTTPHeader{ + { + Name: constants.IsvcNameHeader, + Value: "test-isvc", + }, + { + Name: constants.IsvcNamespaceHeader, + Value: "default", + }, + }, + }, + }, + }, + BackendRefs: []gatewayapiv1.HTTPBackendRef{ + { + BackendRef: gatewayapiv1.BackendRef{ + BackendObjectReference: gatewayapiv1.BackendObjectReference{ + Kind: ptr.To(gatewayapiv1.Kind(constants.ServiceKind)), + Name: "test-isvc-explainer", + Namespace: (*gatewayapiv1.Namespace)(ptr.To("default")), + Port: (*gatewayapiv1.PortNumber)(ptr.To(int32(constants.CommonDefaultHttpPort))), + }, + }, + }, + }, + Timeouts: &gatewayapiv1.HTTPRouteTimeouts{ + Request: ptr.To(gatewayapiv1.Duration("60s")), + }, + }, + { + Matches: []gatewayapiv1.HTTPRouteMatch{ + { + Path: &gatewayapiv1.HTTPPathMatch{ + Type: ptr.To(gatewayapiv1.PathMatchRegularExpression), + Value: ptr.To("^/.*$"), + }, + }, + }, + Filters: []gatewayapiv1.HTTPRouteFilter{ + { + Type: gatewayapiv1.HTTPRouteFilterRequestHeaderModifier, + RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ + Set: []gatewayapiv1.HTTPHeader{ + { + Name: constants.IsvcNameHeader, + Value: "test-isvc", + }, + { + Name: constants.IsvcNamespaceHeader, + Value: "default", + }, + }, + }, + }, + }, + BackendRefs: []gatewayapiv1.HTTPBackendRef{ + { + BackendRef: gatewayapiv1.BackendRef{ + BackendObjectReference: gatewayapiv1.BackendObjectReference{ + Kind: ptr.To(gatewayapiv1.Kind(constants.ServiceKind)), + Name: "test-isvc-predictor", + Namespace: (*gatewayapiv1.Namespace)(ptr.To("default")), + Port: (*gatewayapiv1.PortNumber)(ptr.To(int32(constants.CommonDefaultHttpPort))), + }, + }, + }, + }, + Timeouts: &gatewayapiv1.HTTPRouteTimeouts{ + Request: ptr.To(gatewayapiv1.Duration("60s")), + }, + }, + { + Matches: []gatewayapiv1.HTTPRouteMatch{ + { + Path: &gatewayapiv1.HTTPPathMatch{ + Type: ptr.To(gatewayapiv1.PathMatchRegularExpression), + Value: ptr.To("/serving/default/test-isvc" + constants.PathBasedExplainPrefix()), + }, + }, + }, + Filters: []gatewayapiv1.HTTPRouteFilter{ + { + Type: gatewayapiv1.HTTPRouteFilterRequestHeaderModifier, + RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ + Set: []gatewayapiv1.HTTPHeader{ + { + Name: constants.IsvcNameHeader, + Value: "test-isvc", + }, + { + Name: constants.IsvcNamespaceHeader, + Value: "default", + }, + }, + }, + }, + }, + BackendRefs: []gatewayapiv1.HTTPBackendRef{ + { + BackendRef: gatewayapiv1.BackendRef{ + BackendObjectReference: gatewayapiv1.BackendObjectReference{ + Kind: ptr.To(gatewayapiv1.Kind(constants.ServiceKind)), + Name: "test-isvc-explainer", + Namespace: (*gatewayapiv1.Namespace)(ptr.To("default")), + Port: (*gatewayapiv1.PortNumber)(ptr.To(int32(constants.CommonDefaultHttpPort))), + }, + }, + }, + }, + Timeouts: &gatewayapiv1.HTTPRouteTimeouts{ + Request: ptr.To(gatewayapiv1.Duration("60s")), + }, + }, + { + Matches: []gatewayapiv1.HTTPRouteMatch{ + { + Path: &gatewayapiv1.HTTPPathMatch{ + Type: ptr.To(gatewayapiv1.PathMatchRegularExpression), + Value: ptr.To("/serving/default/test-isvc/"), + }, + }, + }, + Filters: []gatewayapiv1.HTTPRouteFilter{ + { + Type: gatewayapiv1.HTTPRouteFilterRequestHeaderModifier, + RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ + Set: []gatewayapiv1.HTTPHeader{ + { + Name: constants.IsvcNameHeader, + Value: "test-isvc", + }, + { + Name: constants.IsvcNamespaceHeader, + Value: "default", + }, + }, + }, + }, + }, + BackendRefs: []gatewayapiv1.HTTPBackendRef{ + { + BackendRef: gatewayapiv1.BackendRef{ + BackendObjectReference: gatewayapiv1.BackendObjectReference{ + Kind: ptr.To(gatewayapiv1.Kind(constants.ServiceKind)), + Name: "test-isvc-predictor", + Namespace: (*gatewayapiv1.Namespace)(ptr.To("default")), + Port: (*gatewayapiv1.PortNumber)(ptr.To(int32(constants.CommonDefaultHttpPort))), + }, + }, + }, + }, + Timeouts: &gatewayapiv1.HTTPRouteTimeouts{ + Request: ptr.To(gatewayapiv1.Duration("60s")), + }, + }, + }, + CommonRouteSpec: gatewayapiv1.CommonRouteSpec{ + ParentRefs: []gatewayapiv1.ParentReference{ + { + Name: "kserve-gateway", + Kind: ptr.To(gatewayapiv1.Kind(constants.GatewayKind)), + Group: (*gatewayapiv1.Group)(&gatewayapiv1.GroupVersion.Group), + Namespace: ptr.To(gatewayapiv1.Namespace("kserve")), + }, + }, + }, + }, + }, + }, + "Path based routing with transformer": { + isvc: &v1beta1.InferenceService{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-isvc", + Namespace: "default", + }, + Spec: v1beta1.InferenceServiceSpec{ + Predictor: v1beta1.PredictorSpec{}, + Transformer: &v1beta1.TransformerSpec{}, + }, + Status: v1beta1.InferenceServiceStatus{ + Status: duckv1.Status{ + Conditions: []apis.Condition{ + { + Type: v1beta1.TransformerReady, + Status: corev1.ConditionTrue, + }, + { + Type: v1beta1.PredictorReady, + Status: corev1.ConditionTrue, + }, + }, + }, + }, + }, + ingressConfig: &v1beta1.IngressConfig{ + IngressDomain: "example.com", + UrlScheme: "http", + DomainTemplate: "{{.Name}}-{{.Namespace}}.{{.IngressDomain}}", + KserveIngressGateway: "kserve/kserve-gateway", + AdditionalIngressDomains: &[]string{"additional.example.com"}, + PathTemplate: "/serving/{{ .Namespace }}/{{ .Name }}", + EnableGatewayAPI: true, + }, + expected: &gatewayapiv1.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-isvc", + Namespace: "default", + Annotations: map[string]string{}, + Labels: map[string]string{}, + }, + Spec: gatewayapiv1.HTTPRouteSpec{ + Hostnames: []gatewayapiv1.Hostname{"test-isvc-default.example.com", "additional.example.com", "example.com"}, + Rules: []gatewayapiv1.HTTPRouteRule{ + { + Matches: []gatewayapiv1.HTTPRouteMatch{ + { + Path: &gatewayapiv1.HTTPPathMatch{ + Type: ptr.To(gatewayapiv1.PathMatchRegularExpression), + Value: ptr.To("^/.*$"), + }, + }, + }, + Filters: []gatewayapiv1.HTTPRouteFilter{ + { + Type: gatewayapiv1.HTTPRouteFilterRequestHeaderModifier, + RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ + Set: []gatewayapiv1.HTTPHeader{ + { + Name: constants.IsvcNameHeader, + Value: "test-isvc", + }, + { + Name: constants.IsvcNamespaceHeader, + Value: "default", + }, + }, + }, + }, + }, + BackendRefs: []gatewayapiv1.HTTPBackendRef{ + { + BackendRef: gatewayapiv1.BackendRef{ + BackendObjectReference: gatewayapiv1.BackendObjectReference{ + Kind: ptr.To(gatewayapiv1.Kind(constants.ServiceKind)), + Name: "test-isvc-transformer", + Namespace: (*gatewayapiv1.Namespace)(ptr.To("default")), + Port: (*gatewayapiv1.PortNumber)(ptr.To(int32(constants.CommonDefaultHttpPort))), + }, + }, + }, + }, + Timeouts: &gatewayapiv1.HTTPRouteTimeouts{ + Request: ptr.To(gatewayapiv1.Duration("60s")), + }, + }, + { + Matches: []gatewayapiv1.HTTPRouteMatch{ + { + Path: &gatewayapiv1.HTTPPathMatch{ + Type: ptr.To(gatewayapiv1.PathMatchRegularExpression), + Value: ptr.To("/serving/default/test-isvc/"), + }, + }, + }, + Filters: []gatewayapiv1.HTTPRouteFilter{ + { + Type: gatewayapiv1.HTTPRouteFilterRequestHeaderModifier, + RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ + Set: []gatewayapiv1.HTTPHeader{ + { + Name: constants.IsvcNameHeader, + Value: "test-isvc", + }, + { + Name: constants.IsvcNamespaceHeader, + Value: "default", + }, + }, + }, + }, + }, + BackendRefs: []gatewayapiv1.HTTPBackendRef{ + { + BackendRef: gatewayapiv1.BackendRef{ + BackendObjectReference: gatewayapiv1.BackendObjectReference{ + Kind: ptr.To(gatewayapiv1.Kind(constants.ServiceKind)), + Name: "test-isvc-transformer", + Namespace: (*gatewayapiv1.Namespace)(ptr.To("default")), + Port: (*gatewayapiv1.PortNumber)(ptr.To(int32(constants.CommonDefaultHttpPort))), + }, + }, + }, + }, + Timeouts: &gatewayapiv1.HTTPRouteTimeouts{ + Request: ptr.To(gatewayapiv1.Duration("60s")), + }, + }, + }, + CommonRouteSpec: gatewayapiv1.CommonRouteSpec{ + ParentRefs: []gatewayapiv1.ParentReference{ + { + Name: "kserve-gateway", + Kind: ptr.To(gatewayapiv1.Kind(constants.GatewayKind)), + Group: (*gatewayapiv1.Group)(&gatewayapiv1.GroupVersion.Group), + Namespace: ptr.To(gatewayapiv1.Namespace("kserve")), + }, + }, + }, + }, + }, + }, + "Predictor with default suffix": { + isvc: &v1beta1.InferenceService{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-isvc-default", + Namespace: "default", + }, + Spec: v1beta1.InferenceServiceSpec{ + Predictor: v1beta1.PredictorSpec{}, + }, + Status: v1beta1.InferenceServiceStatus{ + Status: duckv1.Status{ + Conditions: []apis.Condition{ + { + Type: v1beta1.PredictorReady, + Status: corev1.ConditionTrue, + }, + }, + }, + }, + }, + ingressConfig: &v1beta1.IngressConfig{ + IngressDomain: "example.com", + UrlScheme: "http", + DomainTemplate: "{{.Name}}-{{.Namespace}}.{{.IngressDomain}}", + KserveIngressGateway: "kserve/kserve-gateway", + AdditionalIngressDomains: &[]string{"additional.example.com"}, + EnableGatewayAPI: true, + }, + expected: &gatewayapiv1.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-isvc-default", + Namespace: "default", + Annotations: map[string]string{}, + Labels: map[string]string{}, + }, + Spec: gatewayapiv1.HTTPRouteSpec{ + Hostnames: []gatewayapiv1.Hostname{"test-isvc-default-default.example.com", "additional.example.com"}, + Rules: []gatewayapiv1.HTTPRouteRule{ + { + Matches: []gatewayapiv1.HTTPRouteMatch{ + { + Path: &gatewayapiv1.HTTPPathMatch{ + Type: ptr.To(gatewayapiv1.PathMatchRegularExpression), + Value: ptr.To("^/.*$"), + }, + }, + }, + Filters: []gatewayapiv1.HTTPRouteFilter{ + { + Type: gatewayapiv1.HTTPRouteFilterRequestHeaderModifier, + RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ + Set: []gatewayapiv1.HTTPHeader{ + { + Name: constants.IsvcNameHeader, + Value: "test-isvc-default", + }, + { + Name: constants.IsvcNamespaceHeader, + Value: "default", + }, + }, + }, + }, + }, + BackendRefs: []gatewayapiv1.HTTPBackendRef{ + { + BackendRef: gatewayapiv1.BackendRef{ + BackendObjectReference: gatewayapiv1.BackendObjectReference{ + Kind: ptr.To(gatewayapiv1.Kind(constants.ServiceKind)), + Name: "test-isvc-default-predictor-default", + Namespace: (*gatewayapiv1.Namespace)(ptr.To("default")), + Port: (*gatewayapiv1.PortNumber)(ptr.To(int32(constants.CommonDefaultHttpPort))), + }, + }, + }, + }, + Timeouts: &gatewayapiv1.HTTPRouteTimeouts{ + Request: ptr.To(gatewayapiv1.Duration("60s")), + }, + }, + }, + CommonRouteSpec: gatewayapiv1.CommonRouteSpec{ + ParentRefs: []gatewayapiv1.ParentReference{ + { + Name: "kserve-gateway", + Kind: ptr.To(gatewayapiv1.Kind(constants.GatewayKind)), + Group: (*gatewayapiv1.Group)(&gatewayapiv1.GroupVersion.Group), + Namespace: ptr.To(gatewayapiv1.Namespace("kserve")), + }, + }, + }, + }, + }, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + s := scheme.Scheme + s.AddKnownTypes(v1beta1.SchemeGroupVersion, &v1beta1.InferenceService{}) + s.AddKnownTypes(schema.GroupVersion{Group: gatewayapiv1.GroupVersion.Group, Version: gatewayapiv1.GroupVersion.Version}, + &gatewayapiv1.HTTPRoute{}) + client := fake.NewClientBuilder().WithScheme(s).Build() + // Create a dummy service to test default suffix case + client.Create(context.Background(), &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-isvc-default-predictor-default", + Namespace: "default", + }, + Spec: corev1.ServiceSpec{}, + }) + isvcConfig := &v1beta1.InferenceServicesConfig{ + ServiceAnnotationDisallowedList: []string{}, + ServiceLabelDisallowedList: []string{}, + } + httpRoute, err := createRawTopLevelHTTPRoute(context.Background(), tc.isvc, tc.ingressConfig, isvcConfig, client) + + g.Expect(err).ToNot(HaveOccurred()) + if tc.expected != nil { + g.Expect(httpRoute.Spec).To(BeComparableTo(tc.expected.Spec)) + g.Expect(httpRoute.ObjectMeta).To(BeComparableTo(tc.expected.ObjectMeta, cmpopts.IgnoreFields(httpRoute.ObjectMeta, "CreationTimestamp"))) + } else { + g.Expect(httpRoute).To(BeNil()) + } + }) + } +} + +func TestCreateRawPredictorHTTPRoute(t *testing.T) { + format.MaxLength = 0 + g := NewGomegaWithT(t) + testCases := map[string]struct { + isvc *v1beta1.InferenceService + ingressConfig *v1beta1.IngressConfig + expected *gatewayapiv1.HTTPRoute + }{ + "Predictor ready": { + isvc: &v1beta1.InferenceService{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-isvc", + Namespace: "default", + }, + Spec: v1beta1.InferenceServiceSpec{ + Predictor: v1beta1.PredictorSpec{}, + }, + Status: v1beta1.InferenceServiceStatus{ + Status: duckv1.Status{ + Conditions: []apis.Condition{ + { + Type: v1beta1.PredictorReady, + Status: corev1.ConditionTrue, + }, + }, + }, + }, + }, + ingressConfig: &v1beta1.IngressConfig{ + IngressDomain: "example.com", + UrlScheme: "http", + DomainTemplate: "{{.Name}}-{{.Namespace}}.{{.IngressDomain}}", + KserveIngressGateway: "kserve/kserve-gateway", + EnableGatewayAPI: true, + }, + expected: &gatewayapiv1.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-isvc-predictor", + Namespace: "default", + Annotations: map[string]string{}, + Labels: map[string]string{}, + }, + Spec: gatewayapiv1.HTTPRouteSpec{ + Hostnames: []gatewayapiv1.Hostname{"test-isvc-predictor-default.example.com"}, + Rules: []gatewayapiv1.HTTPRouteRule{ + { + Matches: []gatewayapiv1.HTTPRouteMatch{ + { + Path: &gatewayapiv1.HTTPPathMatch{ + Type: ptr.To(gatewayapiv1.PathMatchRegularExpression), + Value: ptr.To("^/.*$"), + }, + }, + }, + Filters: []gatewayapiv1.HTTPRouteFilter{ + { + Type: gatewayapiv1.HTTPRouteFilterRequestHeaderModifier, + RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ + Set: []gatewayapiv1.HTTPHeader{ + { + Name: constants.IsvcNameHeader, + Value: "test-isvc", + }, + { + Name: constants.IsvcNamespaceHeader, + Value: "default", + }, + }, + }, + }, + }, + BackendRefs: []gatewayapiv1.HTTPBackendRef{ + { + BackendRef: gatewayapiv1.BackendRef{ + BackendObjectReference: gatewayapiv1.BackendObjectReference{ + Kind: ptr.To(gatewayapiv1.Kind(constants.ServiceKind)), + Name: "test-isvc-predictor", + Namespace: (*gatewayapiv1.Namespace)(ptr.To("default")), + Port: (*gatewayapiv1.PortNumber)(ptr.To(int32(constants.CommonDefaultHttpPort))), + }, + }, + }, + }, + Timeouts: &gatewayapiv1.HTTPRouteTimeouts{ + Request: ptr.To(gatewayapiv1.Duration("60s")), + }, + }, + }, + CommonRouteSpec: gatewayapiv1.CommonRouteSpec{ + ParentRefs: []gatewayapiv1.ParentReference{ + { + Group: (*gatewayapiv1.Group)(&gatewayapiv1.GroupVersion.Group), + Kind: (*gatewayapiv1.Kind)(ptr.To(constants.GatewayKind)), + Namespace: (*gatewayapiv1.Namespace)(ptr.To("kserve")), + Name: gatewayapiv1.ObjectName("kserve-gateway"), + }, + }, + }, + }, + }, + }, + "Predictor not ready": { + isvc: &v1beta1.InferenceService{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-isvc", + Namespace: "default", + }, + Spec: v1beta1.InferenceServiceSpec{ + Predictor: v1beta1.PredictorSpec{}, + }, + Status: v1beta1.InferenceServiceStatus{ + Status: duckv1.Status{ + Conditions: []apis.Condition{ + { + Type: v1beta1.PredictorReady, + Status: corev1.ConditionFalse, + }, + }, + }, + }, + }, + ingressConfig: &v1beta1.IngressConfig{ + IngressDomain: "example.com", + UrlScheme: "http", + DomainTemplate: "{{.Name}}-{{.Namespace}}.{{.IngressDomain}}", + KserveIngressGateway: "kserve/kserve-gateway", + EnableGatewayAPI: true, + }, + expected: nil, + }, + "Predictor with default suffix": { + isvc: &v1beta1.InferenceService{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-isvc-default", + Namespace: "default", + }, + Spec: v1beta1.InferenceServiceSpec{ + Predictor: v1beta1.PredictorSpec{}, + }, + Status: v1beta1.InferenceServiceStatus{ + Status: duckv1.Status{ + Conditions: []apis.Condition{ + { + Type: v1beta1.PredictorReady, + Status: corev1.ConditionTrue, + }, + }, + }, + }, + }, + ingressConfig: &v1beta1.IngressConfig{ + IngressDomain: "example.com", + UrlScheme: "http", + DomainTemplate: "{{.Name}}-{{.Namespace}}.{{.IngressDomain}}", + KserveIngressGateway: "kserve/kserve-gateway", + EnableGatewayAPI: true, + }, + expected: &gatewayapiv1.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-isvc-default-predictor", + Namespace: "default", + Annotations: map[string]string{}, + Labels: map[string]string{}, + }, + Spec: gatewayapiv1.HTTPRouteSpec{ + Hostnames: []gatewayapiv1.Hostname{"test-isvc-default-predictor-default-default.example.com"}, + Rules: []gatewayapiv1.HTTPRouteRule{ + { + Matches: []gatewayapiv1.HTTPRouteMatch{ + { + Path: &gatewayapiv1.HTTPPathMatch{ + Type: ptr.To(gatewayapiv1.PathMatchRegularExpression), + Value: ptr.To("^/.*$"), + }, + }, + }, + Filters: []gatewayapiv1.HTTPRouteFilter{ + { + Type: gatewayapiv1.HTTPRouteFilterRequestHeaderModifier, + RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ + Set: []gatewayapiv1.HTTPHeader{ + { + Name: constants.IsvcNameHeader, + Value: "test-isvc-default", + }, + { + Name: constants.IsvcNamespaceHeader, + Value: "default", + }, + }, + }, + }, + }, + BackendRefs: []gatewayapiv1.HTTPBackendRef{ + { + BackendRef: gatewayapiv1.BackendRef{ + BackendObjectReference: gatewayapiv1.BackendObjectReference{ + Kind: ptr.To(gatewayapiv1.Kind(constants.ServiceKind)), + Name: "test-isvc-default-predictor-default", + Namespace: (*gatewayapiv1.Namespace)(ptr.To("default")), + Port: (*gatewayapiv1.PortNumber)(ptr.To(int32(constants.CommonDefaultHttpPort))), + }, + }, + }, + }, + Timeouts: &gatewayapiv1.HTTPRouteTimeouts{ + Request: ptr.To(gatewayapiv1.Duration("60s")), + }, + }, + }, + CommonRouteSpec: gatewayapiv1.CommonRouteSpec{ + ParentRefs: []gatewayapiv1.ParentReference{ + { + Group: (*gatewayapiv1.Group)(&gatewayapiv1.GroupVersion.Group), + Kind: (*gatewayapiv1.Kind)(ptr.To(constants.GatewayKind)), + Namespace: (*gatewayapiv1.Namespace)(ptr.To("kserve")), + Name: gatewayapiv1.ObjectName("kserve-gateway"), + }, + }, + }, + }, + }, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + s := scheme.Scheme + s.AddKnownTypes(v1beta1.SchemeGroupVersion, &v1beta1.InferenceService{}) + s.AddKnownTypes(schema.GroupVersion{Group: gatewayapiv1.GroupVersion.Group, Version: gatewayapiv1.GroupVersion.Version}, + &gatewayapiv1.HTTPRoute{}) + client := fake.NewClientBuilder().WithScheme(s).Build() + // Create a dummy service to test default suffix case + client.Create(context.Background(), &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-isvc-default-predictor-default", + Namespace: "default", + }, + Spec: corev1.ServiceSpec{}, + }) + isvcConfig := &v1beta1.InferenceServicesConfig{ + ServiceAnnotationDisallowedList: []string{}, + ServiceLabelDisallowedList: []string{}, + } + httpRoute, err := createRawPredictorHTTPRoute(context.Background(), tc.isvc, tc.ingressConfig, isvcConfig, client) + + g.Expect(err).ToNot(HaveOccurred()) + if tc.expected != nil { + g.Expect(httpRoute.Spec).To(BeComparableTo(tc.expected.Spec)) + g.Expect(httpRoute.ObjectMeta).To(BeComparableTo(tc.expected.ObjectMeta, cmpopts.IgnoreFields(httpRoute.ObjectMeta, "CreationTimestamp"))) + } else { + g.Expect(httpRoute).To(BeNil()) + } + }) + } +} + +func TestCreateRawTransformerHTTPRoute(t *testing.T) { + format.MaxLength = 0 + g := NewGomegaWithT(t) + testCases := map[string]struct { + isvc *v1beta1.InferenceService + ingressConfig *v1beta1.IngressConfig + expected *gatewayapiv1.HTTPRoute + }{ + "Transformer ready": { + isvc: &v1beta1.InferenceService{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-isvc", + Namespace: "default", + }, + Spec: v1beta1.InferenceServiceSpec{ + Predictor: v1beta1.PredictorSpec{}, + Transformer: &v1beta1.TransformerSpec{}, + }, + Status: v1beta1.InferenceServiceStatus{ + Status: duckv1.Status{ + Conditions: []apis.Condition{ + { + Type: v1beta1.TransformerReady, + Status: corev1.ConditionTrue, + }, + }, + }, + }, + }, + ingressConfig: &v1beta1.IngressConfig{ + IngressDomain: "example.com", + UrlScheme: "http", + DomainTemplate: "{{.Name}}-{{.Namespace}}.{{.IngressDomain}}", + KserveIngressGateway: "kserve/kserve-gateway", + EnableGatewayAPI: true, + }, + expected: &gatewayapiv1.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-isvc-transformer", + Namespace: "default", + Annotations: map[string]string{}, + Labels: map[string]string{}, + }, + Spec: gatewayapiv1.HTTPRouteSpec{ + Hostnames: []gatewayapiv1.Hostname{"test-isvc-transformer-default.example.com"}, + Rules: []gatewayapiv1.HTTPRouteRule{ + { + Matches: []gatewayapiv1.HTTPRouteMatch{ + { + Path: &gatewayapiv1.HTTPPathMatch{ + Type: ptr.To(gatewayapiv1.PathMatchRegularExpression), + Value: ptr.To("^/.*$"), + }, + }, + }, + Filters: []gatewayapiv1.HTTPRouteFilter{ + { + Type: gatewayapiv1.HTTPRouteFilterRequestHeaderModifier, + RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ + Set: []gatewayapiv1.HTTPHeader{ + { + Name: constants.IsvcNameHeader, + Value: "test-isvc", + }, + { + Name: constants.IsvcNamespaceHeader, + Value: "default", + }, + }, + }, + }, + }, + BackendRefs: []gatewayapiv1.HTTPBackendRef{ + { + BackendRef: gatewayapiv1.BackendRef{ + BackendObjectReference: gatewayapiv1.BackendObjectReference{ + Kind: ptr.To(gatewayapiv1.Kind(constants.ServiceKind)), + Name: "test-isvc-transformer", + Namespace: (*gatewayapiv1.Namespace)(ptr.To("default")), + Port: (*gatewayapiv1.PortNumber)(ptr.To(int32(constants.CommonDefaultHttpPort))), + }, + }, + }, + }, + Timeouts: &gatewayapiv1.HTTPRouteTimeouts{ + Request: ptr.To(gatewayapiv1.Duration("60s")), + }, + }, + }, + CommonRouteSpec: gatewayapiv1.CommonRouteSpec{ + ParentRefs: []gatewayapiv1.ParentReference{ + { + Group: (*gatewayapiv1.Group)(&gatewayapiv1.GroupVersion.Group), + Kind: (*gatewayapiv1.Kind)(ptr.To(constants.GatewayKind)), + Namespace: (*gatewayapiv1.Namespace)(ptr.To("kserve")), + Name: gatewayapiv1.ObjectName("kserve-gateway"), + }, + }, + }, + }, + }, + }, + "Transformer not ready": { + isvc: &v1beta1.InferenceService{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-isvc", + Namespace: "default", + }, + Spec: v1beta1.InferenceServiceSpec{ + Predictor: v1beta1.PredictorSpec{}, + Transformer: &v1beta1.TransformerSpec{}, + }, + Status: v1beta1.InferenceServiceStatus{ + Status: duckv1.Status{ + Conditions: []apis.Condition{ + { + Type: v1beta1.TransformerReady, + Status: corev1.ConditionFalse, + }, + }, + }, + }, + }, + ingressConfig: &v1beta1.IngressConfig{ + IngressDomain: "example.com", + UrlScheme: "http", + DomainTemplate: "{{.Name}}-{{.Namespace}}.{{.IngressDomain}}", + KserveIngressGateway: "kserve/kserve-gateway", + EnableGatewayAPI: true, + }, + expected: nil, + }, + "Transformer with default suffix": { + isvc: &v1beta1.InferenceService{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-isvc-default", + Namespace: "default", + }, + Spec: v1beta1.InferenceServiceSpec{ + Predictor: v1beta1.PredictorSpec{}, + Transformer: &v1beta1.TransformerSpec{}, + }, + Status: v1beta1.InferenceServiceStatus{ + Status: duckv1.Status{ + Conditions: []apis.Condition{ + { + Type: v1beta1.TransformerReady, + Status: corev1.ConditionTrue, + }, + }, + }, + }, + }, + ingressConfig: &v1beta1.IngressConfig{ + IngressDomain: "example.com", + UrlScheme: "http", + DomainTemplate: "{{.Name}}-{{.Namespace}}.{{.IngressDomain}}", + KserveIngressGateway: "kserve/kserve-gateway", + EnableGatewayAPI: true, + }, + expected: &gatewayapiv1.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-isvc-default-transformer", + Namespace: "default", + Annotations: map[string]string{}, + Labels: map[string]string{}, + }, + Spec: gatewayapiv1.HTTPRouteSpec{ + Hostnames: []gatewayapiv1.Hostname{"test-isvc-default-transformer-default-default.example.com"}, + Rules: []gatewayapiv1.HTTPRouteRule{ + { + Matches: []gatewayapiv1.HTTPRouteMatch{ + { + Path: &gatewayapiv1.HTTPPathMatch{ + Type: ptr.To(gatewayapiv1.PathMatchRegularExpression), + Value: ptr.To("^/.*$"), + }, + }, + }, + Filters: []gatewayapiv1.HTTPRouteFilter{ + { + Type: gatewayapiv1.HTTPRouteFilterRequestHeaderModifier, + RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ + Set: []gatewayapiv1.HTTPHeader{ + { + Name: constants.IsvcNameHeader, + Value: "test-isvc-default", + }, + { + Name: constants.IsvcNamespaceHeader, + Value: "default", + }, + }, + }, + }, + }, + BackendRefs: []gatewayapiv1.HTTPBackendRef{ + { + BackendRef: gatewayapiv1.BackendRef{ + BackendObjectReference: gatewayapiv1.BackendObjectReference{ + Kind: ptr.To(gatewayapiv1.Kind(constants.ServiceKind)), + Name: "test-isvc-default-transformer-default", + Namespace: (*gatewayapiv1.Namespace)(ptr.To("default")), + Port: (*gatewayapiv1.PortNumber)(ptr.To(int32(constants.CommonDefaultHttpPort))), + }, + }, + }, + }, + Timeouts: &gatewayapiv1.HTTPRouteTimeouts{ + Request: ptr.To(gatewayapiv1.Duration("60s")), + }, + }, + }, + CommonRouteSpec: gatewayapiv1.CommonRouteSpec{ + ParentRefs: []gatewayapiv1.ParentReference{ + { + Group: (*gatewayapiv1.Group)(&gatewayapiv1.GroupVersion.Group), + Kind: (*gatewayapiv1.Kind)(ptr.To(constants.GatewayKind)), + Namespace: (*gatewayapiv1.Namespace)(ptr.To("kserve")), + Name: gatewayapiv1.ObjectName("kserve-gateway"), + }, + }, + }, + }, + }, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + s := scheme.Scheme + s.AddKnownTypes(v1beta1.SchemeGroupVersion, &v1beta1.InferenceService{}) + s.AddKnownTypes(schema.GroupVersion{Group: gatewayapiv1.GroupVersion.Group, Version: gatewayapiv1.GroupVersion.Version}, + &gatewayapiv1.HTTPRoute{}) + client := fake.NewClientBuilder().WithScheme(s).Build() + // Create a dummy service to test default suffix case + client.Create(context.Background(), &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-isvc-default-transformer-default", + Namespace: "default", + }, + Spec: corev1.ServiceSpec{}, + }) + isvcConfig := &v1beta1.InferenceServicesConfig{ + ServiceAnnotationDisallowedList: []string{}, + ServiceLabelDisallowedList: []string{}, + } + httpRoute, err := createRawTransformerHTTPRoute(context.Background(), tc.isvc, tc.ingressConfig, isvcConfig, client) + + g.Expect(err).ToNot(HaveOccurred()) + if tc.expected != nil { + g.Expect(httpRoute.Spec).To(BeComparableTo(tc.expected.Spec)) + g.Expect(httpRoute.ObjectMeta).To(BeComparableTo(tc.expected.ObjectMeta, cmpopts.IgnoreFields(httpRoute.ObjectMeta, "CreationTimestamp"))) + } else { + g.Expect(httpRoute).To(BeNil()) + } + }) + } +} + +func TestCreateRawExplainerHTTPRoute(t *testing.T) { + format.MaxLength = 0 + g := NewGomegaWithT(t) + testCases := map[string]struct { + isvc *v1beta1.InferenceService + ingressConfig *v1beta1.IngressConfig + expected *gatewayapiv1.HTTPRoute + }{ + "Explainer ready": { + isvc: &v1beta1.InferenceService{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-isvc", + Namespace: "default", + }, + Spec: v1beta1.InferenceServiceSpec{ + Predictor: v1beta1.PredictorSpec{}, + Explainer: &v1beta1.ExplainerSpec{}, + }, + Status: v1beta1.InferenceServiceStatus{ + Status: duckv1.Status{ + Conditions: []apis.Condition{ + { + Type: v1beta1.ExplainerReady, + Status: corev1.ConditionTrue, + }, + }, + }, + }, + }, + ingressConfig: &v1beta1.IngressConfig{ + IngressDomain: "example.com", + UrlScheme: "http", + DomainTemplate: "{{.Name}}-{{.Namespace}}.{{.IngressDomain}}", + KserveIngressGateway: "kserve/kserve-gateway", + EnableGatewayAPI: true, + }, + expected: &gatewayapiv1.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-isvc-explainer", + Namespace: "default", + Annotations: map[string]string{}, + Labels: map[string]string{}, + }, + Spec: gatewayapiv1.HTTPRouteSpec{ + Hostnames: []gatewayapiv1.Hostname{"test-isvc-explainer-default.example.com"}, + Rules: []gatewayapiv1.HTTPRouteRule{ + { + Matches: []gatewayapiv1.HTTPRouteMatch{ + { + Path: &gatewayapiv1.HTTPPathMatch{ + Type: ptr.To(gatewayapiv1.PathMatchRegularExpression), + Value: ptr.To("^/.*$"), + }, + }, + }, + Filters: []gatewayapiv1.HTTPRouteFilter{ + { + Type: gatewayapiv1.HTTPRouteFilterRequestHeaderModifier, + RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ + Set: []gatewayapiv1.HTTPHeader{ + { + Name: constants.IsvcNameHeader, + Value: "test-isvc", + }, + { + Name: constants.IsvcNamespaceHeader, + Value: "default", + }, + }, + }, + }, + }, + BackendRefs: []gatewayapiv1.HTTPBackendRef{ + { + BackendRef: gatewayapiv1.BackendRef{ + BackendObjectReference: gatewayapiv1.BackendObjectReference{ + Kind: ptr.To(gatewayapiv1.Kind(constants.ServiceKind)), + Name: "test-isvc-explainer", + Namespace: (*gatewayapiv1.Namespace)(ptr.To("default")), + Port: (*gatewayapiv1.PortNumber)(ptr.To(int32(constants.CommonDefaultHttpPort))), + }, + }, + }, + }, + Timeouts: &gatewayapiv1.HTTPRouteTimeouts{ + Request: ptr.To(gatewayapiv1.Duration("60s")), + }, + }, + }, + CommonRouteSpec: gatewayapiv1.CommonRouteSpec{ + ParentRefs: []gatewayapiv1.ParentReference{ + { + Group: (*gatewayapiv1.Group)(&gatewayapiv1.GroupVersion.Group), + Kind: (*gatewayapiv1.Kind)(ptr.To(constants.GatewayKind)), + Namespace: (*gatewayapiv1.Namespace)(ptr.To("kserve")), + Name: gatewayapiv1.ObjectName("kserve-gateway"), + }, + }, + }, + }, + }, + }, + "Explainer not ready": { + isvc: &v1beta1.InferenceService{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-isvc", + Namespace: "default", + }, + Spec: v1beta1.InferenceServiceSpec{ + Predictor: v1beta1.PredictorSpec{}, + Explainer: &v1beta1.ExplainerSpec{}, + }, + Status: v1beta1.InferenceServiceStatus{ + Status: duckv1.Status{ + Conditions: []apis.Condition{ + { + Type: v1beta1.ExplainerReady, + Status: corev1.ConditionFalse, + }, + }, + }, + }, + }, + ingressConfig: &v1beta1.IngressConfig{ + IngressDomain: "example.com", + UrlScheme: "http", + DomainTemplate: "{{.Name}}-{{.Namespace}}.{{.IngressDomain}}", + KserveIngressGateway: "kserve/kserve-gateway", + EnableGatewayAPI: true, + }, + expected: nil, + }, + "Explainer with default suffix": { + isvc: &v1beta1.InferenceService{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-isvc-default", + Namespace: "default", + }, + Spec: v1beta1.InferenceServiceSpec{ + Predictor: v1beta1.PredictorSpec{}, + Explainer: &v1beta1.ExplainerSpec{}, + }, + Status: v1beta1.InferenceServiceStatus{ + Status: duckv1.Status{ + Conditions: []apis.Condition{ + { + Type: v1beta1.ExplainerReady, + Status: corev1.ConditionTrue, + }, + }, + }, + }, + }, + ingressConfig: &v1beta1.IngressConfig{ + IngressDomain: "example.com", + UrlScheme: "http", + DomainTemplate: "{{.Name}}-{{.Namespace}}.{{.IngressDomain}}", + KserveIngressGateway: "kserve/kserve-gateway", + EnableGatewayAPI: true, + }, + expected: &gatewayapiv1.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-isvc-default-explainer", + Namespace: "default", + Annotations: map[string]string{}, + Labels: map[string]string{}, + }, + Spec: gatewayapiv1.HTTPRouteSpec{ + Hostnames: []gatewayapiv1.Hostname{"test-isvc-default-explainer-default-default.example.com"}, + Rules: []gatewayapiv1.HTTPRouteRule{ + { + Matches: []gatewayapiv1.HTTPRouteMatch{ + { + Path: &gatewayapiv1.HTTPPathMatch{ + Type: ptr.To(gatewayapiv1.PathMatchRegularExpression), + Value: ptr.To("^/.*$"), + }, + }, + }, + Filters: []gatewayapiv1.HTTPRouteFilter{ + { + Type: gatewayapiv1.HTTPRouteFilterRequestHeaderModifier, + RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ + Set: []gatewayapiv1.HTTPHeader{ + { + Name: constants.IsvcNameHeader, + Value: "test-isvc-default", + }, + { + Name: constants.IsvcNamespaceHeader, + Value: "default", + }, + }, + }, + }, + }, + BackendRefs: []gatewayapiv1.HTTPBackendRef{ + { + BackendRef: gatewayapiv1.BackendRef{ + BackendObjectReference: gatewayapiv1.BackendObjectReference{ + Kind: ptr.To(gatewayapiv1.Kind(constants.ServiceKind)), + Name: "test-isvc-default-explainer-default", + Namespace: (*gatewayapiv1.Namespace)(ptr.To("default")), + Port: (*gatewayapiv1.PortNumber)(ptr.To(int32(constants.CommonDefaultHttpPort))), + }, + }, + }, + }, + Timeouts: &gatewayapiv1.HTTPRouteTimeouts{ + Request: ptr.To(gatewayapiv1.Duration("60s")), + }, + }, + }, + CommonRouteSpec: gatewayapiv1.CommonRouteSpec{ + ParentRefs: []gatewayapiv1.ParentReference{ + { + Group: (*gatewayapiv1.Group)(&gatewayapiv1.GroupVersion.Group), + Kind: (*gatewayapiv1.Kind)(ptr.To(constants.GatewayKind)), + Namespace: (*gatewayapiv1.Namespace)(ptr.To("kserve")), + Name: gatewayapiv1.ObjectName("kserve-gateway"), + }, + }, + }, + }, + }, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + s := scheme.Scheme + s.AddKnownTypes(v1beta1.SchemeGroupVersion, &v1beta1.InferenceService{}) + s.AddKnownTypes(schema.GroupVersion{Group: gatewayapiv1.GroupVersion.Group, Version: gatewayapiv1.GroupVersion.Version}, + &gatewayapiv1.HTTPRoute{}) + client := fake.NewClientBuilder().WithScheme(s).Build() + // Create a dummy service to test default suffix case + client.Create(context.Background(), &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-isvc-default-explainer-default", + Namespace: "default", + }, + Spec: corev1.ServiceSpec{}, + }) + isvcConfig := &v1beta1.InferenceServicesConfig{ + ServiceAnnotationDisallowedList: []string{}, + ServiceLabelDisallowedList: []string{}, + } + httpRoute, err := createRawExplainerHTTPRoute(context.Background(), tc.isvc, tc.ingressConfig, isvcConfig, client) + + g.Expect(err).ToNot(HaveOccurred()) + if tc.expected != nil { + g.Expect(httpRoute.Spec).To(BeComparableTo(tc.expected.Spec)) + g.Expect(httpRoute.ObjectMeta).To(BeComparableTo(tc.expected.ObjectMeta, cmpopts.IgnoreFields(httpRoute.ObjectMeta, "CreationTimestamp"))) + } else { + g.Expect(httpRoute).To(BeNil()) + } + }) + } +} diff --git a/pkg/controller/v1beta1/inferenceservice/reconcilers/ingress/ingress_reconciler.go b/pkg/controller/v1beta1/inferenceservice/reconcilers/ingress/ingress_reconciler.go index 2f6222b355b..361234cbebc 100644 --- a/pkg/controller/v1beta1/inferenceservice/reconcilers/ingress/ingress_reconciler.go +++ b/pkg/controller/v1beta1/inferenceservice/reconcilers/ingress/ingress_reconciler.go @@ -22,6 +22,9 @@ import ( "os" "strings" + duckv1 "knative.dev/pkg/apis/duck/v1" + knservingv1 "knative.dev/serving/pkg/apis/serving/v1" + "github.com/google/go-cmp/cmp" "github.com/pkg/errors" "google.golang.org/protobuf/testing/protocmp" @@ -36,11 +39,9 @@ import ( "k8s.io/apimachinery/pkg/util/validation" "k8s.io/client-go/kubernetes" "knative.dev/pkg/apis" - duckv1 "knative.dev/pkg/apis/duck/v1" "knative.dev/pkg/kmp" "knative.dev/pkg/network" "knative.dev/pkg/system" - knservingv1 "knative.dev/serving/pkg/apis/serving/v1" "knative.dev/serving/pkg/reconciler/route/config" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" @@ -51,9 +52,7 @@ import ( "github.com/kserve/kserve/pkg/utils" ) -var ( - log = logf.Log.WithName("IngressReconciler") -) +var log = logf.Log.WithName("IngressReconciler") type IngressReconciler struct { // client is the client that is used to access the custom resources @@ -62,14 +61,104 @@ type IngressReconciler struct { clientset kubernetes.Interface scheme *runtime.Scheme ingressConfig *v1beta1.IngressConfig + isvcConfig *v1beta1.InferenceServicesConfig } -func NewIngressReconciler(client client.Client, clientset kubernetes.Interface, scheme *runtime.Scheme, ingressConfig *v1beta1.IngressConfig) *IngressReconciler { +func NewIngressReconciler(client client.Client, clientset kubernetes.Interface, scheme *runtime.Scheme, + ingressConfig *v1beta1.IngressConfig, isvcConfig *v1beta1.InferenceServicesConfig, +) *IngressReconciler { return &IngressReconciler{ client: client, clientset: clientset, scheme: scheme, ingressConfig: ingressConfig, + isvcConfig: isvcConfig, + } +} + +func (ir *IngressReconciler) Reconcile(ctx context.Context, isvc *v1beta1.InferenceService) error { + serviceHost := getServiceHost(isvc) + serviceUrl := getServiceUrl(isvc, ir.ingressConfig) + disableIstioVirtualHost := ir.ingressConfig.DisableIstioVirtualHost + if serviceHost == "" || serviceUrl == "" { + return nil + } + // When Istio virtual host is disabled, we return the underlying component url. + // When Istio virtual host is enabled. we return the url using inference service virtual host name and redirect to the corresponding transformer, predictor or explainer url. + if !disableIstioVirtualHost { + // Check if existing knative service name has default suffix + defaultNameExisting := &knservingv1.Service{} + useDefault := false + err := ir.client.Get(ctx, types.NamespacedName{Name: constants.DefaultPredictorServiceName(isvc.Name), Namespace: isvc.Namespace}, defaultNameExisting) + if err == nil { + useDefault = true + } + domainList := getDomainList(ctx, ir.clientset) + desiredIngress := createIngress(isvc, useDefault, ir.ingressConfig, domainList, ir.isvcConfig) + if desiredIngress == nil { + return nil + } + + // Create external service which points to local gateway + if err := ir.reconcileExternalService(ctx, isvc, ir.ingressConfig); err != nil { + return errors.Wrapf(err, "fails to reconcile external name service") + } + + if err := controllerutil.SetControllerReference(isvc, desiredIngress, ir.scheme); err != nil { + return errors.Wrapf(err, "fails to set owner reference for ingress") + } + + existing := &istioclientv1beta1.VirtualService{} + err = ir.client.Get(ctx, types.NamespacedName{Name: desiredIngress.Name, Namespace: desiredIngress.Namespace}, existing) + if err != nil { + if apierr.IsNotFound(err) { + log.Info("Creating Ingress for isvc", "namespace", desiredIngress.Namespace, "name", desiredIngress.Name) + err = ir.client.Create(ctx, desiredIngress) + } + } else { + if !routeSemanticEquals(desiredIngress, existing) { + deepCopy := existing.DeepCopy() + deepCopy.Spec = *desiredIngress.Spec.DeepCopy() + deepCopy.Annotations = desiredIngress.Annotations + deepCopy.Labels = desiredIngress.Labels + log.Info("Update Ingress for isvc", "namespace", desiredIngress.Namespace, "name", desiredIngress.Name) + err = ir.client.Update(ctx, deepCopy) + } + } + if err != nil { + return errors.Wrapf(err, "fails to create or update ingress") + } + } + + if url, err := apis.ParseURL(serviceUrl); err == nil { + isvc.Status.URL = url + var hostPrefix string + if disableIstioVirtualHost { + // Check if existing kubernetes service name has default suffix + existingServiceWithDefaultSuffix := &corev1.Service{} + useDefault := false + err := ir.client.Get(ctx, types.NamespacedName{Name: constants.DefaultPredictorServiceName(isvc.Name), Namespace: isvc.Namespace}, existingServiceWithDefaultSuffix) + if err == nil { + useDefault = true + } + hostPrefix = getHostPrefix(isvc, disableIstioVirtualHost, useDefault) + } else { + hostPrefix = getHostPrefix(isvc, disableIstioVirtualHost, false) + } + + isvc.Status.Address = &duckv1.Addressable{ + URL: &apis.URL{ + Host: network.GetServiceHostname(hostPrefix, isvc.Namespace), + Scheme: "http", + }, + } + isvc.Status.SetCondition(v1beta1.IngressReady, &apis.Condition{ + Type: v1beta1.IngressReady, + Status: corev1.ConditionTrue, + }) + return nil + } else { + return errors.Wrapf(err, "fails to parse service url") } } @@ -88,7 +177,7 @@ func getServiceHost(isvc *v1beta1.InferenceService) string { return strings.Replace(transformerStatus.URL.Host, fmt.Sprintf("-%s-default", string(constants.Transformer)), "", 1) } else { - return strings.Replace(transformerStatus.URL.Host, fmt.Sprintf("-%s", string(constants.Transformer)), "", + return strings.Replace(transformerStatus.URL.Host, "-"+string(constants.Transformer), "", 1) } } @@ -103,7 +192,7 @@ func getServiceHost(isvc *v1beta1.InferenceService) string { return strings.Replace(predictorStatus.URL.Host, fmt.Sprintf("-%s-default", string(constants.Predictor)), "", 1) } else { - return strings.Replace(predictorStatus.URL.Host, fmt.Sprintf("-%s", string(constants.Predictor)), "", + return strings.Replace(predictorStatus.URL.Host, "-"+string(constants.Predictor), "", 1) } } @@ -190,7 +279,7 @@ func getHostBasedServiceUrl(isvc *v1beta1.InferenceService, config *v1beta1.Ingr if strings.Contains(urlString, "-default") { return strings.Replace(urlString, fmt.Sprintf("-%s-default", string(constants.Transformer)), "", 1) } else { - return strings.Replace(urlString, fmt.Sprintf("-%s", string(constants.Transformer)), "", 1) + return strings.Replace(urlString, "-"+string(constants.Transformer), "", 1) } } return urlString @@ -209,14 +298,14 @@ func getHostBasedServiceUrl(isvc *v1beta1.InferenceService, config *v1beta1.Ingr if strings.Contains(urlString, "-default") { return strings.Replace(urlString, fmt.Sprintf("-%s-default", string(constants.Predictor)), "", 1) } else { - return strings.Replace(urlString, fmt.Sprintf("-%s", string(constants.Predictor)), "", 1) + return strings.Replace(urlString, "-"+string(constants.Predictor), "", 1) } } return urlString } } -func (r *IngressReconciler) reconcileExternalService(isvc *v1beta1.InferenceService, config *v1beta1.IngressConfig) error { +func (r *IngressReconciler) reconcileExternalService(ctx context.Context, isvc *v1beta1.InferenceService, config *v1beta1.IngressConfig) error { desired := &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: isvc.Name, @@ -234,11 +323,11 @@ func (r *IngressReconciler) reconcileExternalService(isvc *v1beta1.InferenceServ // Create service if does not exist existing := &corev1.Service{} - err := r.client.Get(context.TODO(), types.NamespacedName{Name: desired.Name, Namespace: desired.Namespace}, existing) + err := r.client.Get(ctx, types.NamespacedName{Name: desired.Name, Namespace: desired.Namespace}, existing) if err != nil { if apierr.IsNotFound(err) { log.Info("Creating external name service", "namespace", desired.Namespace, "name", desired.Name) - err = r.client.Create(context.TODO(), desired) + err = r.client.Create(ctx, desired) } return err } @@ -258,7 +347,7 @@ func (r *IngressReconciler) reconcileExternalService(isvc *v1beta1.InferenceServ existing.Spec = desired.Spec existing.ObjectMeta.Labels = desired.ObjectMeta.Labels existing.ObjectMeta.Annotations = desired.ObjectMeta.Annotations - err = r.client.Update(context.TODO(), existing) + err = r.client.Update(ctx, existing) if err != nil { return errors.Wrapf(err, "fails to update external name service") } @@ -335,8 +424,8 @@ func createHTTPMatchRequest(prefix, targetHost, internalHost string, additionalH func containsHTTPMatchRequest(matchRequest *istiov1beta1.HTTPMatchRequest, matchRequests []*istiov1beta1.HTTPMatchRequest) bool { for _, matchRequestEle := range matchRequests { // If authority, gateways and uri are all equal, two HTTPMatchRequests will be equal. - if stringMatchEqual(matchRequest.Authority, matchRequestEle.Authority) && gatewaysEqual(matchRequest, matchRequestEle) && - stringMatchEqual(matchRequest.Uri, matchRequestEle.Uri) { + if stringMatchEqual(matchRequest.GetAuthority(), matchRequestEle.GetAuthority()) && gatewaysEqual(matchRequest, matchRequestEle) && + stringMatchEqual(matchRequest.GetUri(), matchRequestEle.GetUri()) { return true } } @@ -345,7 +434,7 @@ func containsHTTPMatchRequest(matchRequest *istiov1beta1.HTTPMatchRequest, match func stringMatchEqual(stringMatch, stringMatchDest *istiov1beta1.StringMatch) bool { if stringMatch != nil && stringMatchDest != nil { - return equality.Semantic.DeepEqual(stringMatch.MatchType, stringMatchDest.MatchType) + return equality.Semantic.DeepEqual(stringMatch.GetMatchType(), stringMatchDest.GetMatchType()) } if stringMatch == nil && stringMatchDest == nil { return true @@ -354,10 +443,12 @@ func stringMatchEqual(stringMatch, stringMatchDest *istiov1beta1.StringMatch) bo } func gatewaysEqual(matchRequest, matchRequestDest *istiov1beta1.HTTPMatchRequest) bool { - return equality.Semantic.DeepEqual(matchRequest.Gateways, matchRequestDest.Gateways) + return equality.Semantic.DeepEqual(matchRequest.GetGateways(), matchRequestDest.GetGateways()) } -func createIngress(isvc *v1beta1.InferenceService, useDefault bool, config *v1beta1.IngressConfig, domainList *[]string) *istioclientv1beta1.VirtualService { +func createIngress(isvc *v1beta1.InferenceService, useDefault bool, config *v1beta1.IngressConfig, + domainList *[]string, isvcConfig *v1beta1.InferenceServicesConfig, +) *istioclientv1beta1.VirtualService { if !isvc.Status.IsConditionReady(v1beta1.PredictorReady) { status := corev1.ConditionFalse if isvc.Status.IsConditionUnknown(v1beta1.PredictorReady) { @@ -586,7 +677,7 @@ func createIngress(isvc *v1beta1.InferenceService, useDefault bool, config *v1be } } annotations := utils.Filter(isvc.Annotations, func(key string) bool { - return !utils.Includes(constants.ServiceAnnotationDisallowedList, key) + return !utils.Includes(isvcConfig.ServiceAnnotationDisallowedList, key) }) desiredIngress := &istioclientv1beta1.VirtualService{ ObjectMeta: metav1.ObjectMeta{ @@ -605,7 +696,7 @@ func createIngress(isvc *v1beta1.InferenceService, useDefault bool, config *v1be } // getDomainList gets all the available domain names available with Knative Serving. -func getDomainList(clientset kubernetes.Interface) *[]string { +func getDomainList(ctx context.Context, clientset kubernetes.Interface) *[]string { res := new([]string) ns := constants.DefaultNSKnativeServing if namespace := os.Getenv(system.NamespaceEnvKey); namespace != "" { @@ -613,7 +704,7 @@ func getDomainList(clientset kubernetes.Interface) *[]string { } // Leverage the clientset to access the configMap to get all the available domain names - configMap, err := clientset.CoreV1().ConfigMaps(ns).Get(context.TODO(), + configMap, err := clientset.CoreV1().ConfigMaps(ns).Get(ctx, config.DomainConfigName, metav1.GetOptions{}) if err != nil { return res @@ -624,92 +715,6 @@ func getDomainList(clientset kubernetes.Interface) *[]string { return res } -func (ir *IngressReconciler) Reconcile(isvc *v1beta1.InferenceService) error { - serviceHost := getServiceHost(isvc) - serviceUrl := getServiceUrl(isvc, ir.ingressConfig) - disableIstioVirtualHost := ir.ingressConfig.DisableIstioVirtualHost - if serviceHost == "" || serviceUrl == "" { - return nil - } - // When Istio virtual host is disabled, we return the underlying component url. - // When Istio virtual host is enabled. we return the url using inference service virtual host name and redirect to the corresponding transformer, predictor or explainer url. - if !disableIstioVirtualHost { - // Check if existing knative service name has default suffix - defaultNameExisting := &knservingv1.Service{} - useDefault := false - err := ir.client.Get(context.TODO(), types.NamespacedName{Name: constants.DefaultPredictorServiceName(isvc.Name), Namespace: isvc.Namespace}, defaultNameExisting) - if err == nil { - useDefault = true - } - domainList := getDomainList(ir.clientset) - desiredIngress := createIngress(isvc, useDefault, ir.ingressConfig, domainList) - if desiredIngress == nil { - return nil - } - - // Create external service which points to local gateway - if err := ir.reconcileExternalService(isvc, ir.ingressConfig); err != nil { - return errors.Wrapf(err, "fails to reconcile external name service") - } - - if err := controllerutil.SetControllerReference(isvc, desiredIngress, ir.scheme); err != nil { - return errors.Wrapf(err, "fails to set owner reference for ingress") - } - - existing := &istioclientv1beta1.VirtualService{} - err = ir.client.Get(context.TODO(), types.NamespacedName{Name: desiredIngress.Name, Namespace: desiredIngress.Namespace}, existing) - if err != nil { - if apierr.IsNotFound(err) { - log.Info("Creating Ingress for isvc", "namespace", desiredIngress.Namespace, "name", desiredIngress.Name) - err = ir.client.Create(context.TODO(), desiredIngress) - } - } else { - if !routeSemanticEquals(desiredIngress, existing) { - deepCopy := existing.DeepCopy() - deepCopy.Spec = *desiredIngress.Spec.DeepCopy() - deepCopy.Annotations = desiredIngress.Annotations - deepCopy.Labels = desiredIngress.Labels - log.Info("Update Ingress for isvc", "namespace", desiredIngress.Namespace, "name", desiredIngress.Name) - err = ir.client.Update(context.TODO(), deepCopy) - } - } - if err != nil { - return errors.Wrapf(err, "fails to create or update ingress") - } - } - - if url, err := apis.ParseURL(serviceUrl); err == nil { - isvc.Status.URL = url - var hostPrefix string - if disableIstioVirtualHost { - // Check if existing kubernetes service name has default suffix - existingServiceWithDefaultSuffix := &corev1.Service{} - useDefault := false - err := ir.client.Get(context.TODO(), types.NamespacedName{Name: constants.DefaultPredictorServiceName(isvc.Name), Namespace: isvc.Namespace}, existingServiceWithDefaultSuffix) - if err == nil { - useDefault = true - } - hostPrefix = getHostPrefix(isvc, disableIstioVirtualHost, useDefault) - } else { - hostPrefix = getHostPrefix(isvc, disableIstioVirtualHost, false) - } - - isvc.Status.Address = &duckv1.Addressable{ - URL: &apis.URL{ - Host: network.GetServiceHostname(hostPrefix, isvc.Namespace), - Scheme: "http", - }, - } - isvc.Status.SetCondition(v1beta1.IngressReady, &apis.Condition{ - Type: v1beta1.IngressReady, - Status: corev1.ConditionTrue, - }) - return nil - } else { - return errors.Wrapf(err, "fails to parse service url") - } -} - func routeSemanticEquals(desired, existing *istioclientv1beta1.VirtualService) bool { return cmp.Equal(desired.Spec.DeepCopy(), existing.Spec.DeepCopy(), protocmp.Transform()) && equality.Semantic.DeepEqual(desired.ObjectMeta.Labels, existing.ObjectMeta.Labels) && diff --git a/pkg/controller/v1beta1/inferenceservice/reconcilers/ingress/ingress_reconciler_test.go b/pkg/controller/v1beta1/inferenceservice/reconcilers/ingress/ingress_reconciler_test.go index afe5fa25955..a9fbf68cf3d 100644 --- a/pkg/controller/v1beta1/inferenceservice/reconcilers/ingress/ingress_reconciler_test.go +++ b/pkg/controller/v1beta1/inferenceservice/reconcilers/ingress/ingress_reconciler_test.go @@ -70,6 +70,11 @@ func TestCreateVirtualService(t *testing.T) { Gateways: []string{constants.KnativeIngressGateway}, }, } + defaultInferenceServiceConfig := &v1beta1.InferenceServicesConfig{ + Explainers: v1beta1.ExplainersConfig{}, + ServiceAnnotationDisallowedList: constants.ServiceAnnotationDisallowedList, + ServiceLabelDisallowedList: constants.RevisionTemplateLabelDisallowedList, + } cases := []struct { name string isvc *v1beta1.InferenceService @@ -78,150 +83,155 @@ func TestCreateVirtualService(t *testing.T) { useDefault bool componentStatus *v1beta1.InferenceServiceStatus expectedService *istioclientv1beta1.VirtualService - }{{ - name: "nil status should not be ready", - componentStatus: nil, - expectedService: nil, - }, { - name: "predictor missing url", - ingressConfig: &v1beta1.IngressConfig{ - IngressGateway: constants.KnativeIngressGateway, - KnativeLocalGatewayService: knativeLocalGatewayService, - LocalGateway: constants.KnativeLocalGateway, - LocalGatewayServiceName: "knative-local-gateway.istio-system.svc.cluster.local", + }{ + { + name: "nil status should not be ready", + componentStatus: nil, + expectedService: nil, }, - useDefault: false, - componentStatus: &v1beta1.InferenceServiceStatus{ - Components: map[v1beta1.ComponentType]v1beta1.ComponentStatusSpec{ - v1beta1.PredictorComponent: {}, + { + name: "predictor missing url", + ingressConfig: &v1beta1.IngressConfig{ + IngressGateway: constants.KnativeIngressGateway, + KnativeLocalGatewayService: knativeLocalGatewayService, + LocalGateway: constants.KnativeLocalGateway, + LocalGatewayServiceName: "knative-local-gateway.istio-system.svc.cluster.local", }, - }, - expectedService: nil, - }, { - name: "found predictor status", - ingressConfig: &v1beta1.IngressConfig{ - IngressGateway: constants.KnativeIngressGateway, - KnativeLocalGatewayService: knativeLocalGatewayService, - LocalGateway: constants.KnativeLocalGateway, - LocalGatewayServiceName: "knative-local-gateway.istio-system.svc.cluster.local", - }, - useDefault: false, - componentStatus: &v1beta1.InferenceServiceStatus{ - Status: duckv1.Status{ - Conditions: duckv1.Conditions{ - { - Type: v1beta1.PredictorReady, - Status: corev1.ConditionTrue, - }, + useDefault: false, + componentStatus: &v1beta1.InferenceServiceStatus{ + Components: map[v1beta1.ComponentType]v1beta1.ComponentStatusSpec{ + v1beta1.PredictorComponent: {}, }, }, - Components: map[v1beta1.ComponentType]v1beta1.ComponentStatusSpec{ - v1beta1.PredictorComponent: { - URL: &apis.URL{ - Scheme: "http", - Host: predictorHostname, + expectedService: nil, + }, + { + name: "found predictor status", + ingressConfig: &v1beta1.IngressConfig{ + IngressGateway: constants.KnativeIngressGateway, + KnativeLocalGatewayService: knativeLocalGatewayService, + LocalGateway: constants.KnativeLocalGateway, + LocalGatewayServiceName: "knative-local-gateway.istio-system.svc.cluster.local", + }, + useDefault: false, + componentStatus: &v1beta1.InferenceServiceStatus{ + Status: duckv1.Status{ + Conditions: duckv1.Conditions{ + { + Type: v1beta1.PredictorReady, + Status: corev1.ConditionTrue, + }, }, - Address: &duckv1.Addressable{ + }, + Components: map[v1beta1.ComponentType]v1beta1.ComponentStatusSpec{ + v1beta1.PredictorComponent: { URL: &apis.URL{ Scheme: "http", - Host: network.GetServiceHostname(constants.PredictorServiceName(serviceName), namespace), + Host: predictorHostname, + }, + Address: &duckv1.Addressable{ + URL: &apis.URL{ + Scheme: "http", + Host: network.GetServiceHostname(constants.PredictorServiceName(serviceName), namespace), + }, }, }, }, }, - }, - expectedService: &istioclientv1beta1.VirtualService{ - ObjectMeta: metav1.ObjectMeta{Name: serviceName, Namespace: namespace, Annotations: annotations, Labels: labels}, - Spec: istiov1beta1.VirtualService{ - Hosts: []string{serviceInternalHostName, serviceHostName}, - Gateways: []string{constants.KnativeLocalGateway, constants.IstioMeshGateway, constants.KnativeIngressGateway}, - Http: []*istiov1beta1.HTTPRoute{ - { - Match: predictorRouteMatch, - Route: []*istiov1beta1.HTTPRouteDestination{ - { - Destination: &istiov1beta1.Destination{Host: knativeLocalGatewayService, Port: &istiov1beta1.PortSelector{Number: constants.CommonDefaultHttpPort}}, - Weight: 100, + expectedService: &istioclientv1beta1.VirtualService{ + ObjectMeta: metav1.ObjectMeta{Name: serviceName, Namespace: namespace, Annotations: annotations, Labels: labels}, + Spec: istiov1beta1.VirtualService{ + Hosts: []string{serviceInternalHostName, serviceHostName}, + Gateways: []string{constants.KnativeLocalGateway, constants.IstioMeshGateway, constants.KnativeIngressGateway}, + Http: []*istiov1beta1.HTTPRoute{ + { + Match: predictorRouteMatch, + Route: []*istiov1beta1.HTTPRouteDestination{ + { + Destination: &istiov1beta1.Destination{Host: knativeLocalGatewayService, Port: &istiov1beta1.PortSelector{Number: constants.CommonDefaultHttpPort}}, + Weight: 100, + }, + }, + Headers: &istiov1beta1.Headers{ + Request: &istiov1beta1.Headers_HeaderOperations{Set: map[string]string{ + "Host": network.GetServiceHostname(constants.PredictorServiceName(serviceName), namespace), + "KServe-Isvc-Name": serviceName, + "KServe-Isvc-Namespace": namespace, + }}, }, - }, - Headers: &istiov1beta1.Headers{ - Request: &istiov1beta1.Headers_HeaderOperations{Set: map[string]string{ - "Host": network.GetServiceHostname(constants.PredictorServiceName(serviceName), namespace), - "KServe-Isvc-Name": serviceName, - "KServe-Isvc-Namespace": namespace, - }}, }, }, }, }, }, - }, { - name: "local cluster predictor", - ingressConfig: &v1beta1.IngressConfig{ - IngressGateway: constants.KnativeIngressGateway, - KnativeLocalGatewayService: knativeLocalGatewayService, - LocalGateway: constants.KnativeLocalGateway, - LocalGatewayServiceName: "knative-local-gateway.istio-system.svc.cluster.local", - }, - useDefault: false, - componentStatus: &v1beta1.InferenceServiceStatus{ - Status: duckv1.Status{ - Conditions: duckv1.Conditions{ - { - Type: v1beta1.PredictorReady, - Status: corev1.ConditionTrue, - }, - }, + { + name: "local cluster predictor", + ingressConfig: &v1beta1.IngressConfig{ + IngressGateway: constants.KnativeIngressGateway, + KnativeLocalGatewayService: knativeLocalGatewayService, + LocalGateway: constants.KnativeLocalGateway, + LocalGatewayServiceName: "knative-local-gateway.istio-system.svc.cluster.local", }, - Components: map[v1beta1.ComponentType]v1beta1.ComponentStatusSpec{ - v1beta1.PredictorComponent: { - URL: &apis.URL{ - Scheme: "http", - Host: network.GetServiceHostname(constants.PredictorServiceName(serviceName), namespace), + useDefault: false, + componentStatus: &v1beta1.InferenceServiceStatus{ + Status: duckv1.Status{ + Conditions: duckv1.Conditions{ + { + Type: v1beta1.PredictorReady, + Status: corev1.ConditionTrue, + }, }, - Address: &duckv1.Addressable{ + }, + Components: map[v1beta1.ComponentType]v1beta1.ComponentStatusSpec{ + v1beta1.PredictorComponent: { URL: &apis.URL{ Scheme: "http", Host: network.GetServiceHostname(constants.PredictorServiceName(serviceName), namespace), }, + Address: &duckv1.Addressable{ + URL: &apis.URL{ + Scheme: "http", + Host: network.GetServiceHostname(constants.PredictorServiceName(serviceName), namespace), + }, + }, }, }, }, - }, - expectedService: &istioclientv1beta1.VirtualService{ - ObjectMeta: metav1.ObjectMeta{Name: serviceName, Namespace: namespace, Annotations: annotations, Labels: labels}, - Spec: istiov1beta1.VirtualService{ - Hosts: []string{serviceInternalHostName}, - Gateways: []string{constants.KnativeLocalGateway, constants.IstioMeshGateway}, - Http: []*istiov1beta1.HTTPRoute{ - { - Match: []*istiov1beta1.HTTPMatchRequest{ - { - Authority: &istiov1beta1.StringMatch{ - MatchType: &istiov1beta1.StringMatch_Regex{ - Regex: constants.HostRegExp(network.GetServiceHostname(serviceName, namespace)), + expectedService: &istioclientv1beta1.VirtualService{ + ObjectMeta: metav1.ObjectMeta{Name: serviceName, Namespace: namespace, Annotations: annotations, Labels: labels}, + Spec: istiov1beta1.VirtualService{ + Hosts: []string{serviceInternalHostName}, + Gateways: []string{constants.KnativeLocalGateway, constants.IstioMeshGateway}, + Http: []*istiov1beta1.HTTPRoute{ + { + Match: []*istiov1beta1.HTTPMatchRequest{ + { + Authority: &istiov1beta1.StringMatch{ + MatchType: &istiov1beta1.StringMatch_Regex{ + Regex: constants.HostRegExp(network.GetServiceHostname(serviceName, namespace)), + }, }, + Gateways: []string{constants.KnativeLocalGateway, constants.IstioMeshGateway}, }, - Gateways: []string{constants.KnativeLocalGateway, constants.IstioMeshGateway}, }, - }, - Route: []*istiov1beta1.HTTPRouteDestination{ - { - Destination: &istiov1beta1.Destination{Host: knativeLocalGatewayService, Port: &istiov1beta1.PortSelector{Number: constants.CommonDefaultHttpPort}}, - Weight: 100, + Route: []*istiov1beta1.HTTPRouteDestination{ + { + Destination: &istiov1beta1.Destination{Host: knativeLocalGatewayService, Port: &istiov1beta1.PortSelector{Number: constants.CommonDefaultHttpPort}}, + Weight: 100, + }, + }, + Headers: &istiov1beta1.Headers{ + Request: &istiov1beta1.Headers_HeaderOperations{Set: map[string]string{ + "Host": network.GetServiceHostname(constants.PredictorServiceName(serviceName), namespace), + "KServe-Isvc-Name": serviceName, + "KServe-Isvc-Namespace": namespace, + }}, }, - }, - Headers: &istiov1beta1.Headers{ - Request: &istiov1beta1.Headers_HeaderOperations{Set: map[string]string{ - "Host": network.GetServiceHostname(constants.PredictorServiceName(serviceName), namespace), - "KServe-Isvc-Name": serviceName, - "KServe-Isvc-Namespace": namespace}}, }, }, }, }, }, - }, { name: "nil transformer status fails with status unknown", ingressConfig: &v1beta1.IngressConfig{ @@ -249,7 +259,8 @@ func TestCreateVirtualService(t *testing.T) { }, }, expectedService: nil, - }, { + }, + { name: "found transformer and predictor status", ingressConfig: &v1beta1.IngressConfig{ IngressGateway: constants.KnativeIngressGateway, @@ -323,7 +334,8 @@ func TestCreateVirtualService(t *testing.T) { }, }, }, - }, { + }, + { name: "found transformer and predictor status", ingressConfig: &v1beta1.IngressConfig{ IngressGateway: constants.KnativeIngressGateway, @@ -397,7 +409,8 @@ func TestCreateVirtualService(t *testing.T) { }, }, }, - }, { + }, + { name: "nil explainer status fails with status unknown", ingressConfig: &v1beta1.IngressConfig{ IngressGateway: constants.KnativeIngressGateway, @@ -424,7 +437,8 @@ func TestCreateVirtualService(t *testing.T) { }, }, expectedService: nil, - }, { + }, + { name: "found explainer and predictor status", ingressConfig: &v1beta1.IngressConfig{ IngressGateway: constants.KnativeIngressGateway, @@ -515,10 +529,12 @@ func TestCreateVirtualService(t *testing.T) { }, }, Headers: &istiov1beta1.Headers{ - Request: &istiov1beta1.Headers_HeaderOperations{Set: map[string]string{ - "Host": network.GetServiceHostname(constants.ExplainerServiceName(serviceName), namespace), - "KServe-Isvc-Name": serviceName, - "KServe-Isvc-Namespace": namespace}, + Request: &istiov1beta1.Headers_HeaderOperations{ + Set: map[string]string{ + "Host": network.GetServiceHostname(constants.ExplainerServiceName(serviceName), namespace), + "KServe-Isvc-Name": serviceName, + "KServe-Isvc-Namespace": namespace, + }, }, }, }, @@ -531,17 +547,20 @@ func TestCreateVirtualService(t *testing.T) { }, }, Headers: &istiov1beta1.Headers{ - Request: &istiov1beta1.Headers_HeaderOperations{Set: map[string]string{ - "Host": network.GetServiceHostname(constants.PredictorServiceName(serviceName), namespace), - "KServe-Isvc-Name": serviceName, - "KServe-Isvc-Namespace": namespace}, + Request: &istiov1beta1.Headers_HeaderOperations{ + Set: map[string]string{ + "Host": network.GetServiceHostname(constants.PredictorServiceName(serviceName), namespace), + "KServe-Isvc-Name": serviceName, + "KServe-Isvc-Namespace": namespace, + }, }, }, }, }, }, }, - }, { + }, + { name: "found predictor status with path template", ingressConfig: &v1beta1.IngressConfig{ IngressGateway: constants.KnativeIngressGateway, @@ -613,7 +632,8 @@ func TestCreateVirtualService(t *testing.T) { Request: &istiov1beta1.Headers_HeaderOperations{Set: map[string]string{ "Host": network.GetServiceHostname(constants.PredictorServiceName(serviceName), namespace), "KServe-Isvc-Name": serviceName, - "KServe-Isvc-Namespace": namespace}}, + "KServe-Isvc-Namespace": namespace, + }}, }, }, { @@ -664,7 +684,8 @@ func TestCreateVirtualService(t *testing.T) { }, }, }, - }, { + }, + { name: "found predictor status with the additional ingress domains", ingressConfig: &v1beta1.IngressConfig{ IngressGateway: constants.KnativeIngressGateway, @@ -706,8 +727,10 @@ func TestCreateVirtualService(t *testing.T) { expectedService: &istioclientv1beta1.VirtualService{ ObjectMeta: metav1.ObjectMeta{Name: serviceName, Namespace: namespace, Annotations: annotations, Labels: labels}, Spec: istiov1beta1.VirtualService{ - Hosts: []string{serviceInternalHostName, serviceHostName, "my-domain.com", - "my-model.test.my-additional-domain.com", "my-model.test.my-second-additional-domain.com"}, + Hosts: []string{ + serviceInternalHostName, serviceHostName, "my-domain.com", + "my-model.test.my-additional-domain.com", "my-model.test.my-second-additional-domain.com", + }, Gateways: []string{constants.KnativeLocalGateway, constants.IstioMeshGateway, constants.KnativeIngressGateway}, Http: []*istiov1beta1.HTTPRoute{ { @@ -755,7 +778,8 @@ func TestCreateVirtualService(t *testing.T) { Request: &istiov1beta1.Headers_HeaderOperations{Set: map[string]string{ "Host": network.GetServiceHostname(constants.PredictorServiceName(serviceName), namespace), "KServe-Isvc-Name": serviceName, - "KServe-Isvc-Namespace": namespace}}, + "KServe-Isvc-Namespace": namespace, + }}, }, }, { @@ -806,7 +830,8 @@ func TestCreateVirtualService(t *testing.T) { }, }, }, - }, { + }, + { name: "found predictor status with the additional ingress domains with duplication", ingressConfig: &v1beta1.IngressConfig{ IngressGateway: constants.KnativeIngressGateway, @@ -848,8 +873,10 @@ func TestCreateVirtualService(t *testing.T) { expectedService: &istioclientv1beta1.VirtualService{ ObjectMeta: metav1.ObjectMeta{Name: serviceName, Namespace: namespace, Annotations: annotations, Labels: labels}, Spec: istiov1beta1.VirtualService{ - Hosts: []string{serviceInternalHostName, serviceHostName, "my-domain.com", - "my-model.test.my-additional-domain.com", "my-model.test.my-second-additional-domain.com"}, + Hosts: []string{ + serviceInternalHostName, serviceHostName, "my-domain.com", + "my-model.test.my-additional-domain.com", "my-model.test.my-second-additional-domain.com", + }, Gateways: []string{constants.KnativeLocalGateway, constants.IstioMeshGateway, constants.KnativeIngressGateway}, Http: []*istiov1beta1.HTTPRoute{ { @@ -889,8 +916,10 @@ func TestCreateVirtualService(t *testing.T) { }, Route: []*istiov1beta1.HTTPRouteDestination{ { - Destination: &istiov1beta1.Destination{Host: knativeLocalGatewayService, - Port: &istiov1beta1.PortSelector{Number: constants.CommonDefaultHttpPort}}, + Destination: &istiov1beta1.Destination{ + Host: knativeLocalGatewayService, + Port: &istiov1beta1.PortSelector{Number: constants.CommonDefaultHttpPort}, + }, Weight: 100, }, }, @@ -898,7 +927,8 @@ func TestCreateVirtualService(t *testing.T) { Request: &istiov1beta1.Headers_HeaderOperations{Set: map[string]string{ "Host": network.GetServiceHostname(constants.PredictorServiceName(serviceName), namespace), "KServe-Isvc-Name": serviceName, - "KServe-Isvc-Namespace": namespace}}, + "KServe-Isvc-Namespace": namespace, + }}, }, }, { @@ -949,7 +979,8 @@ func TestCreateVirtualService(t *testing.T) { }, }, }, - }, { + }, + { name: "found predictor and explainer status with path template", ingressConfig: &v1beta1.IngressConfig{ IngressGateway: constants.KnativeIngressGateway, @@ -1038,16 +1069,18 @@ func TestCreateVirtualService(t *testing.T) { }, }, Route: []*istiov1beta1.HTTPRouteDestination{ - {Destination: &istiov1beta1.Destination{Host: knativeLocalGatewayService, Port: &istiov1beta1.PortSelector{Number: constants.CommonDefaultHttpPort}}, - Weight: 100, + { + Destination: &istiov1beta1.Destination{Host: knativeLocalGatewayService, Port: &istiov1beta1.PortSelector{Number: constants.CommonDefaultHttpPort}}, + Weight: 100, }, }, Headers: &istiov1beta1.Headers{ - Request: &istiov1beta1.Headers_HeaderOperations{Set: map[string]string{ - "Host": network.GetServiceHostname(constants.ExplainerServiceName(serviceName), namespace), - constants.IsvcNameHeader: serviceName, - constants.IsvcNamespaceHeader: namespace, - }, + Request: &istiov1beta1.Headers_HeaderOperations{ + Set: map[string]string{ + "Host": network.GetServiceHostname(constants.ExplainerServiceName(serviceName), namespace), + constants.IsvcNameHeader: serviceName, + constants.IsvcNamespaceHeader: namespace, + }, }, }, }, @@ -1071,15 +1104,17 @@ func TestCreateVirtualService(t *testing.T) { }, }, Route: []*istiov1beta1.HTTPRouteDestination{ - {Destination: &istiov1beta1.Destination{Host: knativeLocalGatewayService, Port: &istiov1beta1.PortSelector{Number: constants.CommonDefaultHttpPort}}, - Weight: 100, + { + Destination: &istiov1beta1.Destination{Host: knativeLocalGatewayService, Port: &istiov1beta1.PortSelector{Number: constants.CommonDefaultHttpPort}}, + Weight: 100, }, }, Headers: &istiov1beta1.Headers{ Request: &istiov1beta1.Headers_HeaderOperations{Set: map[string]string{ "Host": network.GetServiceHostname(constants.PredictorServiceName(serviceName), namespace), constants.IsvcNameHeader: serviceName, - constants.IsvcNamespaceHeader: namespace}}, + constants.IsvcNamespaceHeader: namespace, + }}, }, }, { @@ -1165,7 +1200,8 @@ func TestCreateVirtualService(t *testing.T) { }, }, }, - }, { + }, + { name: "found predictor status with default suffix", ingressConfig: &v1beta1.IngressConfig{ IngressGateway: constants.KnativeIngressGateway, @@ -1216,13 +1252,15 @@ func TestCreateVirtualService(t *testing.T) { Request: &istiov1beta1.Headers_HeaderOperations{Set: map[string]string{ "Host": network.GetServiceHostname(constants.DefaultPredictorServiceName(serviceName), namespace), "KServe-Isvc-Name": serviceName, - "KServe-Isvc-Namespace": namespace}}, + "KServe-Isvc-Namespace": namespace, + }}, }, }, }, }, }, - }, { + }, + { name: "found transformer and predictor status with default suffix", ingressConfig: &v1beta1.IngressConfig{ IngressGateway: constants.KnativeIngressGateway, @@ -1296,7 +1334,8 @@ func TestCreateVirtualService(t *testing.T) { }, }, }, - }, { + }, + { name: "transformer is not ready", ingressConfig: &v1beta1.IngressConfig{ IngressGateway: constants.KnativeIngressGateway, @@ -1346,7 +1385,8 @@ func TestCreateVirtualService(t *testing.T) { }, }, expectedService: nil, - }, { + }, + { name: "nil explainer status fails with status unknown", ingressConfig: &v1beta1.IngressConfig{ IngressGateway: constants.KnativeIngressGateway, @@ -1373,7 +1413,8 @@ func TestCreateVirtualService(t *testing.T) { }, }, expectedService: nil, - }, { + }, + { name: "explainer is not ready", ingressConfig: &v1beta1.IngressConfig{ IngressGateway: constants.KnativeIngressGateway, @@ -1423,7 +1464,8 @@ func TestCreateVirtualService(t *testing.T) { }, }, expectedService: nil, - }, { + }, + { name: "isvc labelled with cluster local visibility", isvc: &v1beta1.InferenceService{ ObjectMeta: metav1.ObjectMeta{ @@ -1498,7 +1540,8 @@ func TestCreateVirtualService(t *testing.T) { Request: &istiov1beta1.Headers_HeaderOperations{Set: map[string]string{ "Host": network.GetServiceHostname(constants.PredictorServiceName(serviceName), namespace), "KServe-Isvc-Name": serviceName, - "KServe-Isvc-Namespace": namespace}}, + "KServe-Isvc-Namespace": namespace, + }}, }, }, }, @@ -1537,7 +1580,7 @@ func TestCreateVirtualService(t *testing.T) { testIsvc.Spec.Explainer = &v1beta1.ExplainerSpec{} } - actualService := createIngress(testIsvc, tc.useDefault, tc.ingressConfig, tc.domainList) + actualService := createIngress(testIsvc, tc.useDefault, tc.ingressConfig, tc.domainList, defaultInferenceServiceConfig) if diff := cmp.Diff(tc.expectedService.DeepCopy(), actualService.DeepCopy(), protocmp.Transform()); diff != "" { t.Errorf("Test %q unexpected status (-want +got): %v", tc.name, diff) } @@ -1546,7 +1589,6 @@ func TestCreateVirtualService(t *testing.T) { } func TestGetServiceHost(t *testing.T) { - testCases := []struct { name string isvc *v1beta1.InferenceService @@ -1703,7 +1745,7 @@ func TestGetServiceUrl(t *testing.T) { Address: nil, URL: nil, Components: map[v1beta1.ComponentType]v1beta1.ComponentStatusSpec{ - v1beta1.PredictorComponent: v1beta1.ComponentStatusSpec{}, + v1beta1.PredictorComponent: {}, }, ModelStatus: v1beta1.ModelStatus{}, }, @@ -1732,7 +1774,7 @@ func TestGetServiceUrl(t *testing.T) { Address: nil, URL: nil, Components: map[v1beta1.ComponentType]v1beta1.ComponentStatusSpec{ - v1beta1.PredictorComponent: v1beta1.ComponentStatusSpec{ + v1beta1.PredictorComponent: { URL: (*apis.URL)(defaultPredictorUrl), }, }, @@ -1763,10 +1805,10 @@ func TestGetServiceUrl(t *testing.T) { Address: nil, URL: nil, Components: map[v1beta1.ComponentType]v1beta1.ComponentStatusSpec{ - v1beta1.PredictorComponent: v1beta1.ComponentStatusSpec{ + v1beta1.PredictorComponent: { URL: (*apis.URL)(defaultPredictorUrl), }, - v1beta1.TransformerComponent: v1beta1.ComponentStatusSpec{ + v1beta1.TransformerComponent: { URL: (*apis.URL)(defaultTransformerUrl), }, }, @@ -1819,7 +1861,7 @@ func TestGetServiceUrl(t *testing.T) { Address: nil, URL: nil, Components: map[v1beta1.ComponentType]v1beta1.ComponentStatusSpec{ - v1beta1.PredictorComponent: v1beta1.ComponentStatusSpec{ + v1beta1.PredictorComponent: { URL: (*apis.URL)(defaultPredictorUrl), }, }, @@ -1850,10 +1892,10 @@ func TestGetServiceUrl(t *testing.T) { Address: nil, URL: nil, Components: map[v1beta1.ComponentType]v1beta1.ComponentStatusSpec{ - v1beta1.PredictorComponent: v1beta1.ComponentStatusSpec{ + v1beta1.PredictorComponent: { URL: (*apis.URL)(defaultPredictorUrl), }, - v1beta1.TransformerComponent: v1beta1.ComponentStatusSpec{ + v1beta1.TransformerComponent: { URL: nil, }, }, @@ -1884,10 +1926,10 @@ func TestGetServiceUrl(t *testing.T) { Address: nil, URL: nil, Components: map[v1beta1.ComponentType]v1beta1.ComponentStatusSpec{ - v1beta1.PredictorComponent: v1beta1.ComponentStatusSpec{ + v1beta1.PredictorComponent: { URL: (*apis.URL)(predictorUrl), }, - v1beta1.TransformerComponent: v1beta1.ComponentStatusSpec{ + v1beta1.TransformerComponent: { URL: (*apis.URL)(transformerUrl), }, }, @@ -1917,7 +1959,7 @@ func TestGetServiceUrl(t *testing.T) { Address: nil, URL: nil, Components: map[v1beta1.ComponentType]v1beta1.ComponentStatusSpec{ - v1beta1.PredictorComponent: v1beta1.ComponentStatusSpec{ + v1beta1.PredictorComponent: { URL: (*apis.URL)(predictorUrl), }, }, @@ -1947,7 +1989,7 @@ func TestGetServiceUrl(t *testing.T) { Address: nil, URL: nil, Components: map[v1beta1.ComponentType]v1beta1.ComponentStatusSpec{ - v1beta1.PredictorComponent: v1beta1.ComponentStatusSpec{ + v1beta1.PredictorComponent: { URL: (*apis.URL)(predictorUrl), }, }, @@ -1978,10 +2020,10 @@ func TestGetServiceUrl(t *testing.T) { Address: nil, URL: nil, Components: map[v1beta1.ComponentType]v1beta1.ComponentStatusSpec{ - v1beta1.PredictorComponent: v1beta1.ComponentStatusSpec{ + v1beta1.PredictorComponent: { URL: (*apis.URL)(predictorUrl), }, - v1beta1.TransformerComponent: v1beta1.ComponentStatusSpec{ + v1beta1.TransformerComponent: { URL: (*apis.URL)(transformerUrl), }, }, @@ -2053,7 +2095,7 @@ func TestGetServiceUrlPathBased(t *testing.T) { Address: nil, URL: nil, Components: map[v1beta1.ComponentType]v1beta1.ComponentStatusSpec{ - v1beta1.PredictorComponent: v1beta1.ComponentStatusSpec{}, + v1beta1.PredictorComponent: {}, }, ModelStatus: v1beta1.ModelStatus{}, }, @@ -2078,7 +2120,7 @@ func TestGetServiceUrlPathBased(t *testing.T) { Address: nil, URL: nil, Components: map[v1beta1.ComponentType]v1beta1.ComponentStatusSpec{ - v1beta1.PredictorComponent: v1beta1.ComponentStatusSpec{ + v1beta1.PredictorComponent: { URL: (*apis.URL)(predictorUrl), }, }, @@ -2141,7 +2183,7 @@ func TestGetHostPrefix(t *testing.T) { }, disableVirtualHost: true, useDefault: false, - matcher: gomega.Equal(fmt.Sprintf("%s-predictor", serviceName)), + matcher: gomega.Equal(serviceName + "-predictor"), }, "istio is disabled and useDefault is false with transformer": { isvc: &v1beta1.InferenceService{ @@ -2169,7 +2211,7 @@ func TestGetHostPrefix(t *testing.T) { }, disableVirtualHost: true, useDefault: false, - matcher: gomega.Equal(fmt.Sprintf("%s-transformer", serviceName)), + matcher: gomega.Equal(serviceName + "-transformer"), }, "istio is disabled and useDefault is true": { isvc: &v1beta1.InferenceService{ @@ -2187,7 +2229,7 @@ func TestGetHostPrefix(t *testing.T) { }, disableVirtualHost: true, useDefault: true, - matcher: gomega.Equal(fmt.Sprintf("%s-predictor-default", serviceName)), + matcher: gomega.Equal(serviceName + "-predictor-default"), }, "istio is disabled and useDefault is true with transformer": { isvc: &v1beta1.InferenceService{ @@ -2215,7 +2257,7 @@ func TestGetHostPrefix(t *testing.T) { }, disableVirtualHost: true, useDefault: true, - matcher: gomega.Equal(fmt.Sprintf("%s-transformer-default", serviceName)), + matcher: gomega.Equal(serviceName + "-transformer-default"), }, } diff --git a/pkg/controller/v1beta1/inferenceservice/reconcilers/ingress/kube_ingress_reconciler.go b/pkg/controller/v1beta1/inferenceservice/reconcilers/ingress/kube_ingress_reconciler.go index f0d508415bc..d9e280d014d 100644 --- a/pkg/controller/v1beta1/inferenceservice/reconcilers/ingress/kube_ingress_reconciler.go +++ b/pkg/controller/v1beta1/inferenceservice/reconcilers/ingress/kube_ingress_reconciler.go @@ -20,9 +20,6 @@ import ( "context" "fmt" - v1beta1 "github.com/kserve/kserve/pkg/apis/serving/v1beta1" - "github.com/kserve/kserve/pkg/constants" - "github.com/kserve/kserve/pkg/utils" corev1 "k8s.io/api/core/v1" netv1 "k8s.io/api/networking/v1" "k8s.io/apimachinery/pkg/api/equality" @@ -31,11 +28,13 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" "knative.dev/pkg/apis" - knapis "knative.dev/pkg/apis" duckv1 "knative.dev/pkg/apis/duck/v1" - "knative.dev/pkg/network" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + + "github.com/kserve/kserve/pkg/apis/serving/v1beta1" + "github.com/kserve/kserve/pkg/constants" + "github.com/kserve/kserve/pkg/utils" ) // RawIngressReconciler reconciles the kubernetes ingress @@ -43,52 +42,79 @@ type RawIngressReconciler struct { client client.Client scheme *runtime.Scheme ingressConfig *v1beta1.IngressConfig + isvcConfig *v1beta1.InferenceServicesConfig } func NewRawIngressReconciler(client client.Client, scheme *runtime.Scheme, - ingressConfig *v1beta1.IngressConfig) (*RawIngressReconciler, error) { + ingressConfig *v1beta1.IngressConfig, + isvcConfig *v1beta1.InferenceServicesConfig, +) (*RawIngressReconciler, error) { return &RawIngressReconciler{ client: client, scheme: scheme, ingressConfig: ingressConfig, + isvcConfig: isvcConfig, }, nil } -func createRawURL(isvc *v1beta1.InferenceService, - ingressConfig *v1beta1.IngressConfig) (*knapis.URL, error) { +func (r *RawIngressReconciler) Reconcile(ctx context.Context, isvc *v1beta1.InferenceService) error { var err error - url := &knapis.URL{} - url.Scheme = ingressConfig.UrlScheme - url.Host, err = GenerateDomainName(isvc.Name, isvc.ObjectMeta, ingressConfig) - if err != nil { - return nil, err + isInternal := false + // disable ingress creation if service is labelled with cluster local or kserve domain is cluster local + if val, ok := isvc.Labels[constants.NetworkVisibility]; ok && val == constants.ClusterLocalVisibility { + isInternal = true } - - return url, nil -} - -func getRawServiceHost(isvc *v1beta1.InferenceService, client client.Client) string { - existingService := &corev1.Service{} - if isvc.Spec.Transformer != nil { - transformerName := constants.TransformerServiceName(isvc.Name) - - // Check if existing transformer service name has default suffix - err := client.Get(context.TODO(), types.NamespacedName{Name: constants.DefaultTransformerServiceName(isvc.Name), Namespace: isvc.Namespace}, existingService) - if err == nil { - transformerName = constants.DefaultTransformerServiceName(isvc.Name) + if r.ingressConfig.IngressDomain == constants.ClusterLocalDomain { + isInternal = true + } + if !isInternal && !r.ingressConfig.DisableIngressCreation { + ingress, err := createRawIngress(ctx, r.scheme, isvc, r.ingressConfig, r.client, r.isvcConfig) + if ingress == nil { + return nil + } + if err != nil { + return err + } + // reconcile ingress + existingIngress := &netv1.Ingress{} + err = r.client.Get(ctx, types.NamespacedName{ + Namespace: isvc.Namespace, + Name: isvc.Name, + }, existingIngress) + if err != nil { + if apierr.IsNotFound(err) { + err = r.client.Create(ctx, ingress) + log.Info("creating ingress", "ingressName", isvc.Name, "err", err) + } else { + return err + } + } else { + if !semanticIngressEquals(ingress, existingIngress) { + err = r.client.Update(ctx, ingress) + log.Info("updating ingress", "ingressName", isvc.Name, "err", err) + } + } + if err != nil { + return err } - return network.GetServiceHostname(transformerName, isvc.Namespace) } - - predictorName := constants.PredictorServiceName(isvc.Name) - - // Check if existing predictor service name has default suffix - err := client.Get(context.TODO(), types.NamespacedName{Name: constants.DefaultPredictorServiceName(isvc.Name), Namespace: isvc.Namespace}, existingService) - if err == nil { - predictorName = constants.DefaultPredictorServiceName(isvc.Name) + isvc.Status.URL, err = createRawURL(isvc, r.ingressConfig) + if err != nil { + return err + } + isvc.Status.Address = &duckv1.Addressable{ + URL: &apis.URL{ + Host: getRawServiceHost(ctx, isvc, r.client), + Scheme: r.ingressConfig.UrlScheme, + Path: "", + }, } - return network.GetServiceHostname(predictorName, isvc.Namespace) + isvc.Status.SetCondition(v1beta1.IngressReady, &apis.Condition{ + Type: v1beta1.IngressReady, + Status: corev1.ConditionTrue, + }) + return nil } func generateRule(ingressHost string, componentName string, path string, port int32) netv1.IngressRule { //nolint:unparam @@ -118,10 +144,12 @@ func generateRule(ingressHost string, componentName string, path string, port in } func generateMetadata(isvc *v1beta1.InferenceService, - componentType constants.InferenceServiceComponent, name string) metav1.ObjectMeta { + componentType constants.InferenceServiceComponent, name string, + isvcConfig *v1beta1.InferenceServicesConfig, +) metav1.ObjectMeta { // get annotations from isvc annotations := utils.Filter(isvc.Annotations, func(key string) bool { - return !utils.Includes(constants.ServiceAnnotationDisallowedList, key) + return !utils.Includes(isvcConfig.ServiceAnnotationDisallowedList, key) }) objectMeta := metav1.ObjectMeta{ Name: name, @@ -138,10 +166,12 @@ func generateMetadata(isvc *v1beta1.InferenceService, // generateIngressHost return the config domain in configmap.IngressDomain func generateIngressHost(ingressConfig *v1beta1.IngressConfig, isvc *v1beta1.InferenceService, + isvcConfig *v1beta1.InferenceServicesConfig, componentType string, topLevelFlag bool, - name string) (string, error) { - metadata := generateMetadata(isvc, constants.InferenceServiceComponent(componentType), name) + name string, +) (string, error) { + metadata := generateMetadata(isvc, constants.InferenceServiceComponent(componentType), name, isvcConfig) if !topLevelFlag { return GenerateDomainName(metadata.Name, isvc.ObjectMeta, ingressConfig) } else { @@ -149,8 +179,10 @@ func generateIngressHost(ingressConfig *v1beta1.IngressConfig, } } -func createRawIngress(scheme *runtime.Scheme, isvc *v1beta1.InferenceService, - ingressConfig *v1beta1.IngressConfig, client client.Client) (*netv1.Ingress, error) { +func createRawIngress(ctx context.Context, scheme *runtime.Scheme, isvc *v1beta1.InferenceService, + ingressConfig *v1beta1.IngressConfig, client client.Client, + isvcConfig *v1beta1.InferenceServicesConfig, +) (*netv1.Ingress, error) { if !isvc.Status.IsConditionReady(v1beta1.PredictorReady) { isvc.Status.SetCondition(v1beta1.IngressReady, &apis.Condition{ Type: v1beta1.IngressReady, @@ -174,22 +206,22 @@ func createRawIngress(scheme *runtime.Scheme, isvc *v1beta1.InferenceService, } transformerName := constants.TransformerServiceName(isvc.Name) explainerName := constants.ExplainerServiceName(isvc.Name) - err := client.Get(context.TODO(), types.NamespacedName{Name: constants.DefaultTransformerServiceName(isvc.Name), Namespace: isvc.Namespace}, existing) + err := client.Get(ctx, types.NamespacedName{Name: constants.DefaultTransformerServiceName(isvc.Name), Namespace: isvc.Namespace}, existing) if err == nil { transformerName = constants.DefaultTransformerServiceName(isvc.Name) predictorName = constants.DefaultPredictorServiceName(isvc.Name) explainerName = constants.DefaultExplainerServiceName(isvc.Name) } - host, err := generateIngressHost(ingressConfig, isvc, string(constants.Transformer), true, transformerName) + host, err := generateIngressHost(ingressConfig, isvc, isvcConfig, string(constants.Transformer), true, transformerName) if err != nil { return nil, fmt.Errorf("failed creating top level transformer ingress host: %w", err) } - transformerHost, err := generateIngressHost(ingressConfig, isvc, string(constants.Transformer), false, transformerName) + transformerHost, err := generateIngressHost(ingressConfig, isvc, isvcConfig, string(constants.Transformer), false, transformerName) if err != nil { return nil, fmt.Errorf("failed creating transformer ingress host: %w", err) } if isvc.Spec.Explainer != nil { - explainerHost, err := generateIngressHost(ingressConfig, isvc, string(constants.Explainer), false, transformerName) + explainerHost, err := generateIngressHost(ingressConfig, isvc, isvcConfig, string(constants.Explainer), false, transformerName) if err != nil { return nil, fmt.Errorf("failed creating explainer ingress host: %w", err) } @@ -208,16 +240,16 @@ func createRawIngress(scheme *runtime.Scheme, isvc *v1beta1.InferenceService, return nil, nil } explainerName := constants.ExplainerServiceName(isvc.Name) - err := client.Get(context.TODO(), types.NamespacedName{Name: constants.DefaultExplainerServiceName(isvc.Name), Namespace: isvc.Namespace}, existing) + err := client.Get(ctx, types.NamespacedName{Name: constants.DefaultExplainerServiceName(isvc.Name), Namespace: isvc.Namespace}, existing) if err == nil { explainerName = constants.DefaultExplainerServiceName(isvc.Name) predictorName = constants.DefaultPredictorServiceName(isvc.Name) } - host, err := generateIngressHost(ingressConfig, isvc, string(constants.Explainer), true, explainerName) + host, err := generateIngressHost(ingressConfig, isvc, isvcConfig, string(constants.Explainer), true, explainerName) if err != nil { return nil, fmt.Errorf("failed creating top level explainer ingress host: %w", err) } - explainerHost, err := generateIngressHost(ingressConfig, isvc, string(constants.Explainer), false, explainerName) + explainerHost, err := generateIngressHost(ingressConfig, isvc, isvcConfig, string(constants.Explainer), false, explainerName) if err != nil { return nil, fmt.Errorf("failed creating explainer ingress host: %w", err) } @@ -225,18 +257,18 @@ func createRawIngress(scheme *runtime.Scheme, isvc *v1beta1.InferenceService, rules = append(rules, generateRule(host, predictorName, "/", constants.CommonDefaultHttpPort)) rules = append(rules, generateRule(explainerHost, explainerName, "/", constants.CommonDefaultHttpPort)) default: - err := client.Get(context.TODO(), types.NamespacedName{Name: constants.DefaultPredictorServiceName(isvc.Name), Namespace: isvc.Namespace}, existing) + err := client.Get(ctx, types.NamespacedName{Name: constants.DefaultPredictorServiceName(isvc.Name), Namespace: isvc.Namespace}, existing) if err == nil { predictorName = constants.DefaultPredictorServiceName(isvc.Name) } - host, err := generateIngressHost(ingressConfig, isvc, string(constants.Predictor), true, predictorName) + host, err := generateIngressHost(ingressConfig, isvc, isvcConfig, string(constants.Predictor), true, predictorName) if err != nil { return nil, fmt.Errorf("failed creating top level predictor ingress host: %w", err) } rules = append(rules, generateRule(host, predictorName, "/", constants.CommonDefaultHttpPort)) } // add predictor rule - predictorHost, err := generateIngressHost(ingressConfig, isvc, string(constants.Predictor), false, predictorName) + predictorHost, err := generateIngressHost(ingressConfig, isvc, isvcConfig, string(constants.Predictor), false, predictorName) if err != nil { return nil, fmt.Errorf("failed creating predictor ingress host: %w", err) } @@ -262,62 +294,3 @@ func createRawIngress(scheme *runtime.Scheme, isvc *v1beta1.InferenceService, func semanticIngressEquals(desired, existing *netv1.Ingress) bool { return equality.Semantic.DeepEqual(desired.Spec, existing.Spec) } - -func (r *RawIngressReconciler) Reconcile(isvc *v1beta1.InferenceService) error { - var err error - isInternal := false - // disable ingress creation if service is labelled with cluster local or kserve domain is cluster local - if val, ok := isvc.Labels[constants.NetworkVisibility]; ok && val == constants.ClusterLocalVisibility { - isInternal = true - } - if r.ingressConfig.IngressDomain == constants.ClusterLocalDomain { - isInternal = true - } - if !isInternal && !r.ingressConfig.DisableIngressCreation { - ingress, err := createRawIngress(r.scheme, isvc, r.ingressConfig, r.client) - if ingress == nil { - return nil - } - if err != nil { - return err - } - // reconcile ingress - existingIngress := &netv1.Ingress{} - err = r.client.Get(context.TODO(), types.NamespacedName{ - Namespace: isvc.Namespace, - Name: isvc.Name, - }, existingIngress) - if err != nil { - if apierr.IsNotFound(err) { - err = r.client.Create(context.TODO(), ingress) - log.Info("creating ingress", "ingressName", isvc.Name, "err", err) - } else { - return err - } - } else { - if !semanticIngressEquals(ingress, existingIngress) { - err = r.client.Update(context.TODO(), ingress) - log.Info("updating ingress", "ingressName", isvc.Name, "err", err) - } - } - if err != nil { - return err - } - } - isvc.Status.URL, err = createRawURL(isvc, r.ingressConfig) - if err != nil { - return err - } - isvc.Status.Address = &duckv1.Addressable{ - URL: &apis.URL{ - Host: getRawServiceHost(isvc, r.client), - Scheme: r.ingressConfig.UrlScheme, - Path: "", - }, - } - isvc.Status.SetCondition(v1beta1.IngressReady, &apis.Condition{ - Type: v1beta1.IngressReady, - Status: corev1.ConditionTrue, - }) - return nil -} diff --git a/pkg/controller/v1beta1/inferenceservice/reconcilers/ingress/path.go b/pkg/controller/v1beta1/inferenceservice/reconcilers/ingress/path.go index edb7f5abb2a..6a76dde6f5f 100644 --- a/pkg/controller/v1beta1/inferenceservice/reconcilers/ingress/path.go +++ b/pkg/controller/v1beta1/inferenceservice/reconcilers/ingress/path.go @@ -19,9 +19,8 @@ package ingress import ( "bytes" "fmt" - "text/template" - "net/url" + "text/template" "github.com/kserve/kserve/pkg/apis/serving/v1beta1" ) diff --git a/pkg/controller/v1beta1/inferenceservice/reconcilers/ingress/path_test.go b/pkg/controller/v1beta1/inferenceservice/reconcilers/ingress/path_test.go index 7715721f57c..098a051e6e6 100644 --- a/pkg/controller/v1beta1/inferenceservice/reconcilers/ingress/path_test.go +++ b/pkg/controller/v1beta1/inferenceservice/reconcilers/ingress/path_test.go @@ -20,6 +20,7 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/kserve/kserve/pkg/apis/serving/v1beta1" ) diff --git a/pkg/controller/v1beta1/inferenceservice/reconcilers/knative/ksvc_reconciler.go b/pkg/controller/v1beta1/inferenceservice/reconcilers/knative/ksvc_reconciler.go index 011145f8609..4e708b5fc94 100644 --- a/pkg/controller/v1beta1/inferenceservice/reconcilers/knative/ksvc_reconciler.go +++ b/pkg/controller/v1beta1/inferenceservice/reconcilers/knative/ksvc_reconciler.go @@ -19,10 +19,8 @@ package knative import ( "context" "fmt" + "strconv" - "github.com/kserve/kserve/pkg/apis/serving/v1beta1" - "github.com/kserve/kserve/pkg/constants" - "github.com/kserve/kserve/pkg/utils" "github.com/pkg/errors" "google.golang.org/protobuf/proto" corev1 "k8s.io/api/core/v1" @@ -38,6 +36,10 @@ import ( knservingv1 "knative.dev/serving/pkg/apis/serving/v1" "sigs.k8s.io/controller-runtime/pkg/client" logf "sigs.k8s.io/controller-runtime/pkg/log" + + "github.com/kserve/kserve/pkg/apis/serving/v1beta1" + "github.com/kserve/kserve/pkg/constants" + "github.com/kserve/kserve/pkg/utils" ) var log = logf.Log.WithName("KsvcReconciler") @@ -61,11 +63,13 @@ func NewKsvcReconciler(client client.Client, componentMeta metav1.ObjectMeta, componentExt *v1beta1.ComponentExtensionSpec, podSpec *corev1.PodSpec, - componentStatus v1beta1.ComponentStatusSpec) *KsvcReconciler { + componentStatus v1beta1.ComponentStatusSpec, + disallowedLabelList []string, +) *KsvcReconciler { return &KsvcReconciler{ client: client, scheme: scheme, - Service: createKnativeService(componentMeta, componentExt, podSpec, componentStatus), + Service: createKnativeService(componentMeta, componentExt, podSpec, componentStatus, disallowedLabelList), componentExt: componentExt, componentStatus: componentStatus, } @@ -74,17 +78,19 @@ func NewKsvcReconciler(client client.Client, func createKnativeService(componentMeta metav1.ObjectMeta, componentExtension *v1beta1.ComponentExtensionSpec, podSpec *corev1.PodSpec, - componentStatus v1beta1.ComponentStatusSpec) *knservingv1.Service { + componentStatus v1beta1.ComponentStatusSpec, + disallowedLabelList []string, +) *knservingv1.Service { annotations := componentMeta.GetAnnotations() if componentExtension.MinReplicas == nil { - annotations[constants.MinScaleAnnotationKey] = fmt.Sprint(constants.DefaultMinReplicas) + annotations[constants.MinScaleAnnotationKey] = strconv.Itoa(int(constants.DefaultMinReplicas)) } else { - annotations[constants.MinScaleAnnotationKey] = fmt.Sprint(*componentExtension.MinReplicas) + annotations[constants.MinScaleAnnotationKey] = strconv.Itoa(int(*componentExtension.MinReplicas)) } if componentExtension.MaxReplicas != 0 { - annotations[constants.MaxScaleAnnotationKey] = fmt.Sprint(componentExtension.MaxReplicas) + annotations[constants.MaxScaleAnnotationKey] = strconv.Itoa(int(componentExtension.MaxReplicas)) } // User can pass down scaling class annotation to overwrite the default scaling KPA @@ -93,7 +99,7 @@ func createKnativeService(componentMeta metav1.ObjectMeta, } if componentExtension.ScaleTarget != nil { - annotations[autoscaling.TargetAnnotationKey] = fmt.Sprint(*componentExtension.ScaleTarget) + annotations[autoscaling.TargetAnnotationKey] = strconv.Itoa(int(*componentExtension.ScaleTarget)) } if componentExtension.ScaleMetric != nil { @@ -113,7 +119,11 @@ func createKnativeService(componentMeta metav1.ObjectMeta, lastRolledoutRevision := componentStatus.LatestRolledoutRevision // Log component status and canary traffic percent - log.Info("revision status:", "LatestRolledoutRevision", componentStatus.LatestRolledoutRevision, "LatestReadyRevision", componentStatus.LatestReadyRevision, "LatestCreatedRevision", componentStatus.LatestCreatedRevision, "PreviousRolledoutRevision", componentStatus.PreviousRolledoutRevision, "CanaryTrafficPercent", componentExtension.CanaryTrafficPercent) + log.Info("revision status:", "LatestRolledoutRevision", componentStatus.LatestRolledoutRevision, + "LatestReadyRevision", componentStatus.LatestReadyRevision, + "LatestCreatedRevision", componentStatus.LatestCreatedRevision, + "PreviousRolledoutRevision", componentStatus.PreviousRolledoutRevision, + "CanaryTrafficPercent", componentExtension.CanaryTrafficPercent) trafficTargets := []knservingv1.TrafficTarget{} // Split traffic when canary traffic percent is specified @@ -138,7 +148,7 @@ func createKnativeService(componentMeta metav1.ObjectMeta, trafficTargets = append(trafficTargets, canaryTarget) } } else { - // blue green rollout + // blue-green rollout latestTarget := knservingv1.TrafficTarget{ LatestRevision: proto.Bool(true), Percent: proto.Int64(100), @@ -148,8 +158,9 @@ func createKnativeService(componentMeta metav1.ObjectMeta, } trafficTargets = append(trafficTargets, latestTarget) } + labels := utils.Filter(componentMeta.Labels, func(key string) bool { - return !utils.Includes(constants.RevisionTemplateLabelDisallowedList, key) + return !utils.Includes(disallowedLabelList, key) }) service := &knservingv1.Service{ @@ -218,13 +229,13 @@ func reconcileKsvc(desired *knservingv1.Service, existing *knservingv1.Service) return nil } -func (r *KsvcReconciler) Reconcile() (*knservingv1.ServiceStatus, error) { +func (r *KsvcReconciler) Reconcile(ctx context.Context) (*knservingv1.ServiceStatus, error) { desired := r.Service existing := &knservingv1.Service{} err := retry.RetryOnConflict(retry.DefaultBackoff, func() error { log.Info("Updating knative service", "namespace", desired.Namespace, "name", desired.Name) - if err := r.client.Get(context.TODO(), types.NamespacedName{Name: desired.Name, Namespace: desired.Namespace}, existing); err != nil { + if err := r.client.Get(ctx, types.NamespacedName{Name: desired.Name, Namespace: desired.Namespace}, existing); err != nil { return err } @@ -237,7 +248,7 @@ func (r *KsvcReconciler) Reconcile() (*knservingv1.ServiceStatus, error) { // Do a dry-run update to avoid diffs generated by default values introduced by knative's defaulter webhook. // This will populate our local knative service object with any default values // that are present on the remote version. - if err := r.client.Update(context.TODO(), desired, client.DryRunAll); err != nil { + if err := r.client.Update(ctx, desired, client.DryRunAll); err != nil { // log only if it is not resource conflict error to avoid spamming if !apierr.IsConflict(err) { log.Error(err, "Failed to perform dry-run update of knative service", "service", desired.Name) @@ -247,13 +258,13 @@ func (r *KsvcReconciler) Reconcile() (*knservingv1.ServiceStatus, error) { if err := reconcileKsvc(desired, existing); err != nil { return err } - return r.client.Update(context.TODO(), existing) + return r.client.Update(ctx, existing) }) if err != nil { // Create service if it does not exist if apierr.IsNotFound(err) { log.Info("Creating knative service", "namespace", desired.Namespace, "name", desired.Name) - return &desired.Status, r.client.Create(context.TODO(), desired) + return &desired.Status, r.client.Create(ctx, desired) } return &existing.Status, errors.Wrapf(err, "fails to reconcile knative service") } diff --git a/pkg/controller/v1beta1/inferenceservice/reconcilers/modelconfig/modelconfig_reconciler.go b/pkg/controller/v1beta1/inferenceservice/reconcilers/modelconfig/modelconfig_reconciler.go index 1a2a66a79bd..ef832c5c173 100644 --- a/pkg/controller/v1beta1/inferenceservice/reconcilers/modelconfig/modelconfig_reconciler.go +++ b/pkg/controller/v1beta1/inferenceservice/reconcilers/modelconfig/modelconfig_reconciler.go @@ -27,7 +27,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" logf "sigs.k8s.io/controller-runtime/pkg/log" - v1beta1api "github.com/kserve/kserve/pkg/apis/serving/v1beta1" + "github.com/kserve/kserve/pkg/apis/serving/v1beta1" "github.com/kserve/kserve/pkg/constants" "github.com/kserve/kserve/pkg/controller/v1alpha1/trainedmodel/sharding/memory" v1beta1utils "github.com/kserve/kserve/pkg/controller/v1beta1/inferenceservice/utils" @@ -50,7 +50,7 @@ func NewModelConfigReconciler(client client.Client, clientset kubernetes.Interfa } } -func (c *ModelConfigReconciler) Reconcile(isvc *v1beta1api.InferenceService) error { +func (c *ModelConfigReconciler) Reconcile(ctx context.Context, isvc *v1beta1.InferenceService) error { if v1beta1utils.IsMMSPredictor(&isvc.Spec.Predictor) { // Create an empty modelConfig for every InferenceService shard // An InferenceService without storageUri is an empty model server with for multi-model serving so a modelConfig configmap should be created @@ -58,7 +58,7 @@ func (c *ModelConfigReconciler) Reconcile(isvc *v1beta1api.InferenceService) err shardStrategy := memory.MemoryStrategy{} for _, id := range shardStrategy.GetShard(isvc) { modelConfigName := constants.ModelConfigName(isvc.Name, id) - _, err := c.clientset.CoreV1().ConfigMaps(isvc.Namespace).Get(context.TODO(), modelConfigName, metav1.GetOptions{}) + _, err := c.clientset.CoreV1().ConfigMaps(isvc.Namespace).Get(ctx, modelConfigName, metav1.GetOptions{}) if err != nil { if errors.IsNotFound(err) { // If the modelConfig does not exist for an InferenceService without storageUri, create an empty modelConfig @@ -70,7 +70,7 @@ func (c *ModelConfigReconciler) Reconcile(isvc *v1beta1api.InferenceService) err if err := controllerutil.SetControllerReference(isvc, newModelConfig, c.scheme); err != nil { return err } - err = c.client.Create(context.TODO(), newModelConfig) + err = c.client.Create(ctx, newModelConfig) if err != nil { return err } diff --git a/pkg/controller/v1beta1/inferenceservice/reconcilers/raw/raw_kube_reconciler.go b/pkg/controller/v1beta1/inferenceservice/reconcilers/raw/raw_kube_reconciler.go index ec53253dabe..c09d73de4d5 100644 --- a/pkg/controller/v1beta1/inferenceservice/reconcilers/raw/raw_kube_reconciler.go +++ b/pkg/controller/v1beta1/inferenceservice/reconcilers/raw/raw_kube_reconciler.go @@ -17,13 +17,9 @@ limitations under the License. package raw import ( + "context" "fmt" - "github.com/kserve/kserve/pkg/apis/serving/v1beta1" - autoscaler "github.com/kserve/kserve/pkg/controller/v1beta1/inferenceservice/reconcilers/autoscaler" - deployment "github.com/kserve/kserve/pkg/controller/v1beta1/inferenceservice/reconcilers/deployment" - "github.com/kserve/kserve/pkg/controller/v1beta1/inferenceservice/reconcilers/ingress" - service "github.com/kserve/kserve/pkg/controller/v1beta1/inferenceservice/reconcilers/service" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -31,8 +27,13 @@ import ( "k8s.io/client-go/kubernetes" knapis "knative.dev/pkg/apis" "sigs.k8s.io/controller-runtime/pkg/client" - logf "sigs.k8s.io/controller-runtime/pkg/log" + + "github.com/kserve/kserve/pkg/apis/serving/v1beta1" + autoscaler "github.com/kserve/kserve/pkg/controller/v1beta1/inferenceservice/reconcilers/autoscaler" + deployment "github.com/kserve/kserve/pkg/controller/v1beta1/inferenceservice/reconcilers/deployment" + "github.com/kserve/kserve/pkg/controller/v1beta1/inferenceservice/reconcilers/ingress" + service "github.com/kserve/kserve/pkg/controller/v1beta1/inferenceservice/reconcilers/service" ) var log = logf.Log.WithName("RawKubeReconciler") @@ -48,19 +49,27 @@ type RawKubeReconciler struct { } // NewRawKubeReconciler creates raw kubernetes resource reconciler. -func NewRawKubeReconciler(client client.Client, +func NewRawKubeReconciler(ctx context.Context, client client.Client, clientset kubernetes.Interface, scheme *runtime.Scheme, componentMeta metav1.ObjectMeta, workerComponentMeta metav1.ObjectMeta, componentExt *v1beta1.ComponentExtensionSpec, - podSpec *corev1.PodSpec, workerPodSpec *corev1.PodSpec) (*RawKubeReconciler, error) { + podSpec *corev1.PodSpec, workerPodSpec *corev1.PodSpec, +) (*RawKubeReconciler, error) { as, err := autoscaler.NewAutoscalerReconciler(client, scheme, componentMeta, componentExt) if err != nil { return nil, err } - - url, err := createRawURL(clientset, componentMeta) + isvcConfigMap, err := v1beta1.GetInferenceServiceConfigMap(ctx, clientset) + if err != nil { + return nil, err + } + ingressConfig, err := v1beta1.NewIngressConfig(isvcConfigMap) + if err != nil { + return nil, err + } + url, err := createRawURL(ingressConfig, componentMeta) if err != nil { return nil, err } @@ -71,7 +80,7 @@ func NewRawKubeReconciler(client client.Client, } // do not return error as service config is optional - serviceConfig, err1 := v1beta1.NewServiceConfig(clientset) + serviceConfig, err1 := v1beta1.NewServiceConfig(isvcConfigMap) if err1 != nil { log.Error(err1, "failed to get service config") } @@ -86,36 +95,30 @@ func NewRawKubeReconciler(client client.Client, }, nil } -func createRawURL(clientset kubernetes.Interface, metadata metav1.ObjectMeta) (*knapis.URL, error) { - ingressConfig, err := v1beta1.NewIngressConfig(clientset) - if err != nil { - return nil, err - } - +func createRawURL(ingressConfig *v1beta1.IngressConfig, metadata metav1.ObjectMeta) (*knapis.URL, error) { url := &knapis.URL{} url.Scheme = "http" - url.Host, err = ingress.GenerateDomainName(metadata.Name, metadata, ingressConfig) - if err != nil { + var err error + if url.Host, err = ingress.GenerateDomainName(metadata.Name, metadata, ingressConfig); err != nil { return nil, fmt.Errorf("failed creating host name: %w", err) } - return url, nil } // Reconcile ... -func (r *RawKubeReconciler) Reconcile() ([]*appsv1.Deployment, error) { +func (r *RawKubeReconciler) Reconcile(ctx context.Context) ([]*appsv1.Deployment, error) { // reconcile Deployment - deploymentList, err := r.Deployment.Reconcile() + deploymentList, err := r.Deployment.Reconcile(ctx) if err != nil { return nil, err } // reconcile Service - _, err = r.Service.Reconcile() + _, err = r.Service.Reconcile(ctx) if err != nil { return nil, err } // reconcile HPA - err = r.Scaler.Reconcile() + err = r.Scaler.Reconcile(ctx) if err != nil { return nil, err } diff --git a/pkg/controller/v1beta1/inferenceservice/reconcilers/service/service_reconciler.go b/pkg/controller/v1beta1/inferenceservice/reconcilers/service/service_reconciler.go index bc9a6ac4d0a..f2d6aa943e8 100644 --- a/pkg/controller/v1beta1/inferenceservice/reconcilers/service/service_reconciler.go +++ b/pkg/controller/v1beta1/inferenceservice/reconcilers/service/service_reconciler.go @@ -18,12 +18,9 @@ package service import ( "context" - "fmt" "sort" - "strconv" + "strings" - "github.com/kserve/kserve/pkg/apis/serving/v1beta1" - "github.com/kserve/kserve/pkg/constants" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/equality" apierr "k8s.io/apimachinery/pkg/api/errors" @@ -31,8 +28,13 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/client" logf "sigs.k8s.io/controller-runtime/pkg/log" + + "github.com/kserve/kserve/pkg/apis/serving/v1beta1" + "github.com/kserve/kserve/pkg/constants" + "github.com/kserve/kserve/pkg/utils" ) var log = logf.Log.WithName("ServiceReconciler") @@ -50,7 +52,8 @@ func NewServiceReconciler(client client.Client, componentMeta metav1.ObjectMeta, componentExt *v1beta1.ComponentExtensionSpec, podSpec *corev1.PodSpec, multiNodeEnabled bool, - serviceConfig *v1beta1.ServiceConfig) *ServiceReconciler { + serviceConfig *v1beta1.ServiceConfig, +) *ServiceReconciler { return &ServiceReconciler{ client: client, scheme: scheme, @@ -59,8 +62,24 @@ func NewServiceReconciler(client client.Client, } } +// isGrpcPort checks if the port is a grpc port or not by port name +func isGrpcPort(port corev1.ContainerPort) bool { + if strings.Contains(port.Name, "grpc") || strings.Contains(port.Name, "h2c") { + return true + } + return false +} + +func getAppProtocol(port corev1.ContainerPort) *string { + if isGrpcPort(port) { + return ptr.To("kubernetes.io/h2c") + } + return nil +} + func createService(componentMeta metav1.ObjectMeta, componentExt *v1beta1.ComponentExtensionSpec, - podSpec *corev1.PodSpec, multiNodeEnabled bool, serviceConfig *v1beta1.ServiceConfig) []*corev1.Service { + podSpec *corev1.PodSpec, multiNodeEnabled bool, serviceConfig *v1beta1.ServiceConfig, +) []*corev1.Service { var svcList []*corev1.Service var isWorkerContainer bool @@ -89,7 +108,8 @@ func createService(componentMeta metav1.ObjectMeta, componentExt *v1beta1.Compon } func createDefaultSvc(componentMeta metav1.ObjectMeta, componentExt *v1beta1.ComponentExtensionSpec, - podSpec *corev1.PodSpec, serviceConfig *v1beta1.ServiceConfig) *corev1.Service { + podSpec *corev1.PodSpec, serviceConfig *v1beta1.ServiceConfig, +) *corev1.Service { var servicePorts []corev1.ServicePort if len(podSpec.Containers) != 0 { @@ -109,7 +129,8 @@ func createDefaultSvc(componentMeta metav1.ObjectMeta, componentExt *v1beta1.Com Type: intstr.Int, IntVal: container.Ports[0].ContainerPort, }, - Protocol: container.Ports[0].Protocol, + Protocol: container.Ports[0].Protocol, + AppProtocol: getAppProtocol(container.Ports[0]), } servicePorts = append(servicePorts, servicePort) @@ -125,19 +146,19 @@ func createDefaultSvc(componentMeta metav1.ObjectMeta, componentExt *v1beta1.Com Type: intstr.Int, IntVal: port.ContainerPort, }, - Protocol: port.Protocol, + Protocol: port.Protocol, + AppProtocol: getAppProtocol(port), } servicePorts = append(servicePorts, servicePort) } } else { - port, _ := strconv.Atoi(constants.InferenceServiceDefaultHttpPort) - portInt32 := int32(port) // nolint #nosec G109 + port, _ := utils.StringToInt32(constants.InferenceServiceDefaultHttpPort) servicePorts = append(servicePorts, corev1.ServicePort{ Name: componentMeta.Name, Port: constants.CommonDefaultHttpPort, TargetPort: intstr.IntOrString{ Type: intstr.Int, - IntVal: portInt32, // #nosec G109 + IntVal: port, }, Protocol: corev1.ProtocolTCP, }) @@ -194,9 +215,9 @@ func createHeadlessSvc(componentMeta metav1.ObjectMeta) *corev1.Service { return service } -func (r *ServiceReconciler) cleanHeadSvc() error { +func (r *ServiceReconciler) cleanHeadSvc(ctx context.Context) error { svcList := &corev1.ServiceList{} - if err := r.client.List(context.TODO(), svcList, client.MatchingLabels{ + if err := r.client.List(ctx, svcList, client.MatchingLabels{ constants.MultiNodeRoleLabelKey: constants.MultiNodeHead, }); err != nil { return err @@ -209,16 +230,16 @@ func (r *ServiceReconciler) cleanHeadSvc() error { // Keep the 3 newest services and delete the rest for i := 3; i < len(svcList.Items); i++ { existingService := &corev1.Service{} - err := r.client.Get(context.TODO(), types.NamespacedName{ + err := r.client.Get(ctx, types.NamespacedName{ Namespace: svcList.Items[i].Namespace, Name: svcList.Items[i].Name, }, existingService) if err == nil { - err := r.client.Delete(context.TODO(), existingService) + err := r.client.Delete(ctx, existingService) if err != nil { - fmt.Printf("Failed to delete service %s: %v\n", existingService.Name, err) + log.Error(err, "Failed to delete service", "name", existingService.Name) } else { - fmt.Printf("Deleted service %s in namespace %s\n", existingService.Name, existingService.Namespace) + log.Info("Deleted service", "name", existingService.Name, "namespace", existingService.Namespace) } } } @@ -226,10 +247,10 @@ func (r *ServiceReconciler) cleanHeadSvc() error { } // checkServiceExist checks if the service exists? -func (r *ServiceReconciler) checkServiceExist(client client.Client, svc *corev1.Service) (constants.CheckResultType, *corev1.Service, error) { +func (r *ServiceReconciler) checkServiceExist(ctx context.Context, client client.Client, svc *corev1.Service) (constants.CheckResultType, *corev1.Service, error) { // get service existingService := &corev1.Service{} - err := client.Get(context.TODO(), types.NamespacedName{ + err := client.Get(ctx, types.NamespacedName{ Namespace: svc.Namespace, Name: svc.Name, }, existingService) @@ -253,10 +274,10 @@ func semanticServiceEquals(desired, existing *corev1.Service) bool { } // Reconcile ... -func (r *ServiceReconciler) Reconcile() ([]*corev1.Service, error) { +func (r *ServiceReconciler) Reconcile(ctx context.Context) ([]*corev1.Service, error) { for _, svc := range r.ServiceList { // reconcile Service - checkResult, _, err := r.checkServiceExist(r.client, svc) + checkResult, _, err := r.checkServiceExist(ctx, r.client, svc) log.Info("service reconcile", "checkResult", checkResult, "err", err) if err != nil { return nil, err @@ -265,9 +286,9 @@ func (r *ServiceReconciler) Reconcile() ([]*corev1.Service, error) { var opErr error switch checkResult { case constants.CheckResultCreate: - opErr = r.client.Create(context.TODO(), svc) + opErr = r.client.Create(ctx, svc) case constants.CheckResultUpdate: - opErr = r.client.Update(context.TODO(), svc) + opErr = r.client.Update(ctx, svc) } if opErr != nil { @@ -276,7 +297,7 @@ func (r *ServiceReconciler) Reconcile() ([]*corev1.Service, error) { } // Clean up head svc when head sevices are more than 3. if len(r.ServiceList) > 1 { - r.cleanHeadSvc() + r.cleanHeadSvc(ctx) } return r.ServiceList, nil } diff --git a/pkg/controller/v1beta1/inferenceservice/reconcilers/service/service_reconciler_test.go b/pkg/controller/v1beta1/inferenceservice/reconcilers/service/service_reconciler_test.go index 47de9b6ae8a..37c1c30f7e0 100644 --- a/pkg/controller/v1beta1/inferenceservice/reconcilers/service/service_reconciler_test.go +++ b/pkg/controller/v1beta1/inferenceservice/reconcilers/service/service_reconciler_test.go @@ -19,18 +19,18 @@ import ( "testing" "github.com/google/go-cmp/cmp" - "github.com/kserve/kserve/pkg/apis/serving/v1beta1" - "github.com/kserve/kserve/pkg/constants" "github.com/stretchr/testify/assert" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" + + "github.com/kserve/kserve/pkg/apis/serving/v1beta1" + "github.com/kserve/kserve/pkg/constants" ) var emptyServiceConfig = &v1beta1.ServiceConfig{} func TestCreateDefaultDeployment(t *testing.T) { - type args struct { componentMeta metav1.ObjectMeta componentExt *v1beta1.ComponentExtensionSpec @@ -226,7 +226,6 @@ func TestCreateDefaultDeployment(t *testing.T) { if diff := cmp.Diff(tt.expected[i], service); diff != "" { t.Errorf("Test %q unexpected service (-want +got): %v", tt.name, diff) } - } }) } @@ -238,8 +237,7 @@ func TestCreateServiceRawServiceConfigEmpty(t *testing.T) { } func TestCreateServiceRawServiceAndConfigNil(t *testing.T) { - serviceConfig := &v1beta1.ServiceConfig{} - serviceConfig = nil + var serviceConfig *v1beta1.ServiceConfig // no service means empty runTestServiceCreate(serviceConfig, "", t) } diff --git a/pkg/controller/v1beta1/inferenceservice/suite_test.go b/pkg/controller/v1beta1/inferenceservice/suite_test.go index 19fe37294e3..40091c45280 100644 --- a/pkg/controller/v1beta1/inferenceservice/suite_test.go +++ b/pkg/controller/v1beta1/inferenceservice/suite_test.go @@ -23,21 +23,18 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - v1 "k8s.io/api/core/v1" - netv1 "k8s.io/api/networking/v1" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/rest" - knservingv1 "knative.dev/serving/pkg/apis/serving/v1" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/envtest" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/log/zap" metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" - kfservingv1alpha1 "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" + "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" "github.com/kserve/kserve/pkg/apis/serving/v1beta1" "github.com/kserve/kserve/pkg/constants" pkgtest "github.com/kserve/kserve/pkg/testing" @@ -49,10 +46,7 @@ import ( var ( cfg *rest.Config k8sClient client.Client - testEnv *envtest.Environment - cancel context.CancelFunc - ctx context.Context - clientset kubernetes.Interface + clientset *kubernetes.Clientset ) func TestV1beta1APIs(t *testing.T) { @@ -63,24 +57,29 @@ func TestV1beta1APIs(t *testing.T) { var _ = BeforeSuite(func() { logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) - ctx, cancel = context.WithCancel(context.TODO()) + ctx, cancel := context.WithCancel(context.TODO()) By("bootstrapping test environment") crdDirectoryPaths := []string{ filepath.Join("..", "..", "..", "..", "test", "crds"), } - testEnv = pkgtest.SetupEnvTest(crdDirectoryPaths) - cfg, err := testEnv.Start() + testEnv := pkgtest.SetupEnvTest(crdDirectoryPaths) + var err error + cfg, err = testEnv.Start() Expect(err).ToNot(HaveOccurred()) Expect(cfg).ToNot(BeNil()) - err = kfservingv1alpha1.AddToScheme(scheme.Scheme) + DeferCleanup(func() { + cancel() + + By("tearing down the test environment") + err := testEnv.Stop() + Expect(err).ToNot(HaveOccurred()) + }) + + err = v1alpha1.AddToScheme(scheme.Scheme) Expect(err).NotTo(HaveOccurred()) err = v1beta1.AddToScheme(scheme.Scheme) Expect(err).NotTo(HaveOccurred()) - err = knservingv1.AddToScheme(scheme.Scheme) - Expect(err).NotTo(HaveOccurred()) - err = netv1.AddToScheme(scheme.Scheme) - Expect(err).NotTo(HaveOccurred()) k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) Expect(err).ToNot(HaveOccurred()) @@ -90,14 +89,6 @@ var _ = BeforeSuite(func() { Expect(err).ToNot(HaveOccurred()) Expect(clientset).ToNot(BeNil()) - //Create namespace - kfservingNamespaceObj := &v1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: constants.KServeNamespace, - }, - } - Expect(k8sClient.Create(context.Background(), kfservingNamespaceObj)).Should(Succeed()) - k8sManager, err := ctrl.NewManager(cfg, ctrl.Options{ Scheme: scheme.Scheme, Metrics: metricsserver.Options{ @@ -106,6 +97,17 @@ var _ = BeforeSuite(func() { }) Expect(err).ToNot(HaveOccurred()) + k8sClient = k8sManager.GetClient() + Expect(k8sClient).ToNot(BeNil()) + + // Create namespace + kserveNamespaceObj := &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: constants.KServeNamespace, + }, + } + Expect(k8sClient.Create(context.Background(), kserveNamespaceObj)).Should(Succeed()) + deployConfig := &v1beta1.DeployConfig{DefaultDeploymentMode: "Serverless"} ingressConfig := &v1beta1.IngressConfig{ IngressGateway: constants.KnativeIngressGateway, @@ -127,14 +129,4 @@ var _ = BeforeSuite(func() { err = k8sManager.Start(ctx) Expect(err).ToNot(HaveOccurred()) }() - - k8sClient = k8sManager.GetClient() - Expect(k8sClient).ToNot(BeNil()) -}) - -var _ = AfterSuite(func() { - cancel() - By("tearing down the test environment") - err := testEnv.Stop() - Expect(err).ToNot(HaveOccurred()) }) diff --git a/pkg/controller/v1beta1/inferenceservice/utils/utils.go b/pkg/controller/v1beta1/inferenceservice/utils/utils.go index 23934636d39..61bb59287e7 100644 --- a/pkg/controller/v1beta1/inferenceservice/utils/utils.go +++ b/pkg/controller/v1beta1/inferenceservice/utils/utils.go @@ -28,7 +28,7 @@ import ( "github.com/pkg/errors" goerrors "github.com/pkg/errors" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -44,7 +44,7 @@ import ( // Constants var ( - SupportedStorageURIPrefixList = []string{"gs://", "s3://", "pvc://", "file://", "https://", "http://", "hdfs://", "webhdfs://", "oci://"} + SupportedStorageURIPrefixList = []string{"gs://", "s3://", "pvc://", "file://", "https://", "http://", "hdfs://", "webhdfs://", "oci://", "hf://"} ) const ( @@ -184,7 +184,7 @@ func GetDeploymentMode(annotations map[string]string, deployConfig *v1beta1.Depl // MergeRuntimeContainers Merge the predictor Container struct with the runtime Container struct, allowing users // to override runtime container settings from the predictor spec. -func MergeRuntimeContainers(runtimeContainer *v1.Container, predictorContainer *v1.Container) (*v1.Container, error) { +func MergeRuntimeContainers(runtimeContainer *corev1.Container, predictorContainer *corev1.Container) (*corev1.Container, error) { // Save runtime container name, as the name can be overridden as empty string during the Unmarshal below // since the Name field does not have the 'omitempty' struct tag. runtimeContainerName := runtimeContainer.Name @@ -200,7 +200,7 @@ func MergeRuntimeContainers(runtimeContainer *v1.Container, predictorContainer * return nil, err } - mergedContainer := v1.Container{} + mergedContainer := corev1.Container{} jsonResult, err := strategicpatch.StrategicMergePatch(runtimeContainerJson, overrides, mergedContainer) if err != nil { return nil, err @@ -222,8 +222,8 @@ func MergeRuntimeContainers(runtimeContainer *v1.Container, predictorContainer * // MergePodSpec Merge the predictor PodSpec struct with the runtime PodSpec struct, allowing users // to override runtime PodSpec settings from the predictor spec. -func MergePodSpec(runtimePodSpec *v1alpha1.ServingRuntimePodSpec, predictorPodSpec *v1beta1.PodSpec) (*v1.PodSpec, error) { - runtimePodSpecJson, err := json.Marshal(v1.PodSpec{ +func MergePodSpec(runtimePodSpec *v1alpha1.ServingRuntimePodSpec, predictorPodSpec *v1beta1.PodSpec) (*corev1.PodSpec, error) { + runtimePodSpecJson, err := json.Marshal(corev1.PodSpec{ NodeSelector: runtimePodSpec.NodeSelector, Affinity: runtimePodSpec.Affinity, Tolerations: runtimePodSpec.Tolerations, @@ -240,7 +240,7 @@ func MergePodSpec(runtimePodSpec *v1alpha1.ServingRuntimePodSpec, predictorPodSp return nil, err } - corePodSpec := v1.PodSpec{} + corePodSpec := corev1.PodSpec{} jsonResult, err := strategicpatch.StrategicMergePatch(runtimePodSpecJson, overrides, corePodSpec) if err != nil { return nil, err @@ -255,9 +255,9 @@ func MergePodSpec(runtimePodSpec *v1alpha1.ServingRuntimePodSpec, predictorPodSp // GetServingRuntime Get a ServingRuntime by name. First, ServingRuntimes in the given namespace will be checked. // If a resource of the specified name is not found, then ClusterServingRuntimes will be checked. -func GetServingRuntime(cl client.Client, name string, namespace string) (*v1alpha1.ServingRuntimeSpec, error) { +func GetServingRuntime(ctx context.Context, cl client.Client, name string, namespace string) (*v1alpha1.ServingRuntimeSpec, error) { runtime := &v1alpha1.ServingRuntime{} - err := cl.Get(context.TODO(), client.ObjectKey{Name: name, Namespace: namespace}, runtime) + err := cl.Get(ctx, client.ObjectKey{Name: name, Namespace: namespace}, runtime) if err == nil { return &runtime.Spec, nil } else if !apierrors.IsNotFound(err) { @@ -265,7 +265,7 @@ func GetServingRuntime(cl client.Client, name string, namespace string) (*v1alph } clusterRuntime := &v1alpha1.ClusterServingRuntime{} - err = cl.Get(context.TODO(), client.ObjectKey{Name: name}, clusterRuntime) + err = cl.Get(ctx, client.ObjectKey{Name: name}, clusterRuntime) if err == nil { return &clusterRuntime.Spec, nil } else if !apierrors.IsNotFound(err) { @@ -275,7 +275,7 @@ func GetServingRuntime(cl client.Client, name string, namespace string) (*v1alph } // ReplacePlaceholders Replace placeholders in runtime container by values from inferenceservice metadata -func ReplacePlaceholders(container *v1.Container, meta metav1.ObjectMeta) error { +func ReplacePlaceholders(container *corev1.Container, meta metav1.ObjectMeta) error { data, _ := json.Marshal(container) tmpl, err := template.New("container-tmpl").Parse(string(data)) if err != nil { @@ -290,7 +290,7 @@ func ReplacePlaceholders(container *v1.Container, meta metav1.ObjectMeta) error } // UpdateImageTag Update image tag if GPU is enabled or runtime version is provided -func UpdateImageTag(container *v1.Container, runtimeVersion *string, servingRuntime *string) { +func UpdateImageTag(container *corev1.Container, runtimeVersion *string, servingRuntime *string) { image := container.Image if runtimeVersion != nil { re := regexp.MustCompile(`(:([\w.\-_]*))$`) @@ -315,13 +315,13 @@ func UpdateImageTag(container *v1.Container, runtimeVersion *string, servingRunt } // ListPodsByLabel Get a PodList by label. -func ListPodsByLabel(cl client.Client, namespace string, labelKey string, labelVal string) (*v1.PodList, error) { - podList := &v1.PodList{} +func ListPodsByLabel(ctx context.Context, cl client.Client, namespace string, labelKey string, labelVal string) (*corev1.PodList, error) { + podList := &corev1.PodList{} opts := []client.ListOption{ client.InNamespace(namespace), client.MatchingLabels{labelKey: labelVal}, } - err := cl.List(context.TODO(), podList, opts...) + err := cl.List(ctx, podList, opts...) if err != nil && !apierrors.IsNotFound(err) { return nil, err } @@ -329,19 +329,19 @@ func ListPodsByLabel(cl client.Client, namespace string, labelKey string, labelV return podList, nil } -func sortPodsByCreatedTimestampDesc(pods *v1.PodList) { +func sortPodsByCreatedTimestampDesc(pods *corev1.PodList) { sort.Slice(pods.Items, func(i, j int) bool { return pods.Items[j].ObjectMeta.CreationTimestamp.Before(&pods.Items[i].ObjectMeta.CreationTimestamp) }) } -func ValidateStorageURI(storageURI *string, client client.Client) error { +func ValidateStorageURI(ctx context.Context, storageURI *string, client client.Client) error { if storageURI == nil { return nil } // Step 1: Passes the validation if we have a storage container CR that supports this storageURI. - storageContainerSpec, err := pod.GetContainerSpecForStorageUri(*storageURI, client) + storageContainerSpec, err := pod.GetContainerSpecForStorageUri(ctx, *storageURI, client) if err != nil { return err } @@ -369,7 +369,7 @@ func ValidateStorageURI(storageURI *string, client client.Client) error { } // Function to add a new environment variable to a specific container in the PodSpec -func AddEnvVarToPodSpec(podSpec *v1.PodSpec, containerName, envName, envValue string) error { +func AddEnvVarToPodSpec(podSpec *corev1.PodSpec, containerName, envName, envValue string) error { updatedResult := false // Iterate over the containers in the PodTemplateSpec to find the specified container for i, container := range podSpec.Containers { @@ -385,7 +385,7 @@ func AddEnvVarToPodSpec(podSpec *v1.PodSpec, containerName, envName, envValue st } } else { // Add the new environment variable to the Env field if it ooes not exist - container.Env = append(container.Env, v1.EnvVar{ + container.Env = append(container.Env, corev1.EnvVar{ Name: envName, Value: envValue, }) @@ -400,7 +400,7 @@ func AddEnvVarToPodSpec(podSpec *v1.PodSpec, containerName, envName, envValue st return nil } -func MergeServingRuntimeAndInferenceServiceSpecs(srContainers []v1.Container, isvcContainer v1.Container, isvc *v1beta1.InferenceService, targetContainerName string, srPodSpec v1alpha1.ServingRuntimePodSpec, isvcPodSpec v1beta1.PodSpec) (int, *v1.Container, *v1.PodSpec, error) { +func MergeServingRuntimeAndInferenceServiceSpecs(srContainers []corev1.Container, isvcContainer corev1.Container, isvc *v1beta1.InferenceService, targetContainerName string, srPodSpec v1alpha1.ServingRuntimePodSpec, isvcPodSpec v1beta1.PodSpec) (int, *corev1.Container, *corev1.PodSpec, error) { var err error containerIndexInSR := -1 for i := range srContainers { diff --git a/pkg/controller/v1beta1/inferenceservice/utils/utils_test.go b/pkg/controller/v1beta1/inferenceservice/utils/utils_test.go index 279a3171fc6..0664b4ea216 100644 --- a/pkg/controller/v1beta1/inferenceservice/utils/utils_test.go +++ b/pkg/controller/v1beta1/inferenceservice/utils/utils_test.go @@ -17,6 +17,7 @@ limitations under the License. package utils import ( + "context" "errors" "strconv" "testing" @@ -25,25 +26,26 @@ import ( "knative.dev/pkg/apis" knativeV1 "knative.dev/pkg/apis/duck/v1" - "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" - . "github.com/kserve/kserve/pkg/apis/serving/v1beta1" - "github.com/kserve/kserve/pkg/constants" "github.com/onsi/gomega" "google.golang.org/protobuf/proto" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "sigs.k8s.io/controller-runtime/pkg/client/fake" + + "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" + . "github.com/kserve/kserve/pkg/apis/serving/v1beta1" + "github.com/kserve/kserve/pkg/constants" ) func TestIsMMSPredictor(t *testing.T) { g := gomega.NewGomegaWithT(t) - var requestedResource = v1.ResourceRequirements{ - Limits: v1.ResourceList{ + requestedResource := corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ "cpu": resource.MustParse("100m"), }, - Requests: v1.ResourceList{ + Requests: corev1.ResourceList{ "cpu": resource.MustParse("90m"), }, } @@ -64,7 +66,7 @@ func TestIsMMSPredictor(t *testing.T) { Name: "sklearn", }, PredictorExtensionSpec: PredictorExtensionSpec{ - Container: v1.Container{ + Container: corev1.Container{ Image: "customImage:0.1.0", Resources: requestedResource, }, @@ -88,7 +90,7 @@ func TestIsMMSPredictor(t *testing.T) { }, PredictorExtensionSpec: PredictorExtensionSpec{ StorageURI: proto.String("gs://someUri"), - Container: v1.Container{ + Container: corev1.Container{ Image: "customImage:0.1.0", Resources: requestedResource, }, @@ -107,7 +109,8 @@ func TestIsMMSPredictor(t *testing.T) { Spec: InferenceServiceSpec{ Predictor: PredictorSpec{ HuggingFace: &HuggingFaceRuntimeSpec{ - PredictorExtensionSpec: PredictorExtensionSpec{RuntimeVersion: proto.String("latest")}}, + PredictorExtensionSpec: PredictorExtensionSpec{RuntimeVersion: proto.String("latest")}, + }, }, }, }, @@ -121,10 +124,11 @@ func TestIsMMSPredictor(t *testing.T) { Spec: InferenceServiceSpec{ Predictor: PredictorSpec{ PodSpec: PodSpec{ - Containers: []v1.Container{ - {Name: constants.InferenceServiceContainerName, + Containers: []corev1.Container{ + { + Name: constants.InferenceServiceContainerName, Image: "some-image", - Env: []v1.EnvVar{{Name: constants.CustomSpecMultiModelServerEnvVarKey, Value: strconv.FormatBool(true)}}, + Env: []corev1.EnvVar{{Name: constants.CustomSpecMultiModelServerEnvVarKey, Value: strconv.FormatBool(true)}}, }, }, }, @@ -141,12 +145,14 @@ func TestIsMMSPredictor(t *testing.T) { Spec: InferenceServiceSpec{ Predictor: PredictorSpec{ PodSpec: PodSpec{ - Containers: []v1.Container{ - {Name: constants.InferenceServiceContainerName, + Containers: []corev1.Container{ + { + Name: constants.InferenceServiceContainerName, Image: "some-image", - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ {Name: constants.CustomSpecMultiModelServerEnvVarKey, Value: strconv.FormatBool(false)}, - {Name: constants.CustomSpecStorageUriEnvVarKey, Value: "gs://some-uri"}}, + {Name: constants.CustomSpecStorageUriEnvVarKey, Value: "gs://some-uri"}, + }, }, }, }, @@ -163,8 +169,9 @@ func TestIsMMSPredictor(t *testing.T) { Spec: InferenceServiceSpec{ Predictor: PredictorSpec{ PodSpec: PodSpec{ - Containers: []v1.Container{ - {Name: constants.InferenceServiceContainerName, + Containers: []corev1.Container{ + { + Name: constants.InferenceServiceContainerName, Image: "some-image", }, }, @@ -190,17 +197,17 @@ func TestIsMemoryResourceAvailable(t *testing.T) { g := gomega.NewGomegaWithT(t) reqResourcesScenarios := map[string]struct { - resource v1.ResourceRequirements + resource corev1.ResourceRequirements expected bool }{ "EnoughMemoryResource": { // Enough memory - resource: v1.ResourceRequirements{ - Limits: v1.ResourceList{ + resource: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ "cpu": resource.MustParse("100m"), "memory": resource.MustParse("100Gi"), }, - Requests: v1.ResourceList{ + Requests: corev1.ResourceList{ "cpu": resource.MustParse("90m"), "memory": resource.MustParse("100Gi"), }, @@ -209,12 +216,12 @@ func TestIsMemoryResourceAvailable(t *testing.T) { }, "NotEnoughMemoryResource": { // Enough memory - resource: v1.ResourceRequirements{ - Limits: v1.ResourceList{ + resource: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ "cpu": resource.MustParse("100m"), "memory": resource.MustParse("1Mi"), }, - Requests: v1.ResourceList{ + Requests: corev1.ResourceList{ "cpu": resource.MustParse("90m"), "memory": resource.MustParse("1Mi"), }, @@ -241,7 +248,7 @@ func TestIsMemoryResourceAvailable(t *testing.T) { Predictor: PredictorSpec{ LightGBM: &LightGBMSpec{ PredictorExtensionSpec: PredictorExtensionSpec{ - Container: v1.Container{ + Container: corev1.Container{ Image: "customImage:0.1.0", Name: constants.InferenceServiceContainerName, Resources: reqResourcesScenario.resource, @@ -262,7 +269,7 @@ func TestIsMemoryResourceAvailable(t *testing.T) { LightGBM: &LightGBMSpec{ PredictorExtensionSpec: PredictorExtensionSpec{ StorageURI: proto.String("gs://someUri"), - Container: v1.Container{ + Container: corev1.Container{ Image: "customImage:0.1.0", Name: constants.InferenceServiceContainerName, Resources: reqResourcesScenario.resource, @@ -282,7 +289,7 @@ func TestIsMemoryResourceAvailable(t *testing.T) { Predictor: PredictorSpec{ ONNX: &ONNXRuntimeSpec{ PredictorExtensionSpec: PredictorExtensionSpec{ - Container: v1.Container{ + Container: corev1.Container{ Image: "mcr.microsoft.com/onnxruntime/server:v0.5.0", Name: constants.InferenceServiceContainerName, Resources: reqResourcesScenario.resource, @@ -303,7 +310,7 @@ func TestIsMemoryResourceAvailable(t *testing.T) { ONNX: &ONNXRuntimeSpec{ PredictorExtensionSpec: PredictorExtensionSpec{ StorageURI: proto.String("gs://someUri"), - Container: v1.Container{ + Container: corev1.Container{ Image: "mcr.microsoft.com/onnxruntime/server:v0.5.0", Name: constants.InferenceServiceContainerName, Resources: reqResourcesScenario.resource, @@ -323,7 +330,7 @@ func TestIsMemoryResourceAvailable(t *testing.T) { Predictor: PredictorSpec{ PMML: &PMMLSpec{ PredictorExtensionSpec: PredictorExtensionSpec{ - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, Resources: reqResourcesScenario.resource, }, @@ -343,7 +350,7 @@ func TestIsMemoryResourceAvailable(t *testing.T) { PMML: &PMMLSpec{ PredictorExtensionSpec: PredictorExtensionSpec{ StorageURI: proto.String("gs://someUri"), - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, Resources: reqResourcesScenario.resource, }, @@ -363,7 +370,7 @@ func TestIsMemoryResourceAvailable(t *testing.T) { SKLearn: &SKLearnSpec{ PredictorExtensionSpec: PredictorExtensionSpec{ ProtocolVersion: &protocolV1, - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, Resources: reqResourcesScenario.resource, }, @@ -383,7 +390,7 @@ func TestIsMemoryResourceAvailable(t *testing.T) { SKLearn: &SKLearnSpec{ PredictorExtensionSpec: PredictorExtensionSpec{ ProtocolVersion: &protocolV2, - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, Resources: reqResourcesScenario.resource, }, @@ -404,7 +411,7 @@ func TestIsMemoryResourceAvailable(t *testing.T) { PredictorExtensionSpec: PredictorExtensionSpec{ StorageURI: proto.String("gs://someUri"), ProtocolVersion: &protocolV1, - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, Resources: reqResourcesScenario.resource, }, @@ -425,7 +432,7 @@ func TestIsMemoryResourceAvailable(t *testing.T) { PredictorExtensionSpec: PredictorExtensionSpec{ StorageURI: proto.String("gs://someUri"), ProtocolVersion: &protocolV2, - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, Resources: reqResourcesScenario.resource, }, @@ -444,7 +451,7 @@ func TestIsMemoryResourceAvailable(t *testing.T) { Predictor: PredictorSpec{ Tensorflow: &TFServingSpec{ PredictorExtensionSpec: PredictorExtensionSpec{ - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, Resources: reqResourcesScenario.resource, }, @@ -464,7 +471,7 @@ func TestIsMemoryResourceAvailable(t *testing.T) { Tensorflow: &TFServingSpec{ PredictorExtensionSpec: PredictorExtensionSpec{ StorageURI: proto.String("gs://someUri"), - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, Resources: reqResourcesScenario.resource, }, @@ -484,7 +491,7 @@ func TestIsMemoryResourceAvailable(t *testing.T) { PyTorch: &TorchServeSpec{ PredictorExtensionSpec: PredictorExtensionSpec{ ProtocolVersion: &protocolV1, - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, Resources: reqResourcesScenario.resource, }, @@ -504,7 +511,7 @@ func TestIsMemoryResourceAvailable(t *testing.T) { PyTorch: &TorchServeSpec{ PredictorExtensionSpec: PredictorExtensionSpec{ ProtocolVersion: &protocolV2, - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, Resources: reqResourcesScenario.resource, }, @@ -525,7 +532,7 @@ func TestIsMemoryResourceAvailable(t *testing.T) { PredictorExtensionSpec: PredictorExtensionSpec{ StorageURI: proto.String("gs://someUri"), ProtocolVersion: &protocolV1, - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, Resources: reqResourcesScenario.resource, }, @@ -546,7 +553,7 @@ func TestIsMemoryResourceAvailable(t *testing.T) { PredictorExtensionSpec: PredictorExtensionSpec{ StorageURI: proto.String("gs://someUri"), ProtocolVersion: &protocolV2, - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, Resources: reqResourcesScenario.resource, }, @@ -566,7 +573,7 @@ func TestIsMemoryResourceAvailable(t *testing.T) { Triton: &TritonSpec{ PredictorExtensionSpec: PredictorExtensionSpec{ ProtocolVersion: &protocolV1, - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, Resources: reqResourcesScenario.resource, }, @@ -587,7 +594,7 @@ func TestIsMemoryResourceAvailable(t *testing.T) { PredictorExtensionSpec: PredictorExtensionSpec{ StorageURI: proto.String("gs://someUri"), ProtocolVersion: &protocolV1, - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, Resources: reqResourcesScenario.resource, }, @@ -607,7 +614,7 @@ func TestIsMemoryResourceAvailable(t *testing.T) { XGBoost: &XGBoostSpec{ PredictorExtensionSpec: PredictorExtensionSpec{ RuntimeVersion: proto.String("0.1.0"), - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, Resources: reqResourcesScenario.resource, }, @@ -628,7 +635,7 @@ func TestIsMemoryResourceAvailable(t *testing.T) { PredictorExtensionSpec: PredictorExtensionSpec{ ProtocolVersion: &protocolV2, RuntimeVersion: proto.String("0.1.0"), - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, Resources: reqResourcesScenario.resource, }, @@ -649,7 +656,7 @@ func TestIsMemoryResourceAvailable(t *testing.T) { PredictorExtensionSpec: PredictorExtensionSpec{ StorageURI: proto.String("gs://someUri"), RuntimeVersion: proto.String("0.1.0"), - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, Resources: reqResourcesScenario.resource, }, @@ -671,7 +678,7 @@ func TestIsMemoryResourceAvailable(t *testing.T) { StorageURI: proto.String("gs://someUri"), ProtocolVersion: &protocolV2, RuntimeVersion: proto.String("0.1.0"), - Container: v1.Container{ + Container: corev1.Container{ Name: constants.InferenceServiceContainerName, Resources: reqResourcesScenario.resource, }, @@ -694,53 +701,54 @@ func TestIsMemoryResourceAvailable(t *testing.T) { } } } + func TestMergeRuntimeContainers(t *testing.T) { g := gomega.NewGomegaWithT(t) scenarios := map[string]struct { - containerBase *v1.Container - containerOverride *v1.Container - expected *v1.Container + containerBase *corev1.Container + containerOverride *corev1.Container + expected *corev1.Container }{ "BasicMerge": { - containerBase: &v1.Container{ + containerBase: &corev1.Container{ Name: "kserve-container", Image: "default-image", Args: []string{ "--foo=bar", "--test=dummy", }, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "PORT", Value: "8080"}, {Name: "PORT2", Value: "8081"}, }, - Resources: v1.ResourceRequirements{ - Limits: v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("1"), - v1.ResourceMemory: resource.MustParse("2Gi"), + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("2Gi"), }, - Requests: v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("1"), - v1.ResourceMemory: resource.MustParse("2Gi"), + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("2Gi"), }, }, }, - containerOverride: &v1.Container{ + containerOverride: &corev1.Container{ Args: []string{ "--new-arg=baz", }, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "PORT2", Value: "8082"}, {Name: "Some", Value: "Var"}, }, - Resources: v1.ResourceRequirements{ - Limits: v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("2"), - v1.ResourceMemory: resource.MustParse("4Gi"), + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("2"), + corev1.ResourceMemory: resource.MustParse("4Gi"), }, }, }, - expected: &v1.Container{ + expected: &corev1.Container{ Name: "kserve-container", Image: "default-image", Args: []string{ @@ -748,19 +756,19 @@ func TestMergeRuntimeContainers(t *testing.T) { "--test=dummy", "--new-arg=baz", }, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "PORT", Value: "8080"}, {Name: "PORT2", Value: "8082"}, {Name: "Some", Value: "Var"}, }, - Resources: v1.ResourceRequirements{ - Limits: v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("2"), - v1.ResourceMemory: resource.MustParse("4Gi"), + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("2"), + corev1.ResourceMemory: resource.MustParse("4Gi"), }, - Requests: v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("1"), - v1.ResourceMemory: resource.MustParse("2Gi"), + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("2Gi"), }, }, }, @@ -783,7 +791,7 @@ func TestMergePodSpec(t *testing.T) { scenarios := map[string]struct { podSpecBase *v1alpha1.ServingRuntimePodSpec podSpecOverride *PodSpec - expected *v1.PodSpec + expected *corev1.PodSpec }{ "BasicMerge": { podSpecBase: &v1alpha1.ServingRuntimePodSpec{ @@ -791,28 +799,28 @@ func TestMergePodSpec(t *testing.T) { "foo": "bar", "aaa": "bbb", }, - Tolerations: []v1.Toleration{ - {Key: "key1", Operator: v1.TolerationOpExists, Effect: v1.TaintEffectNoSchedule}, + Tolerations: []corev1.Toleration{ + {Key: "key1", Operator: corev1.TolerationOpExists, Effect: corev1.TaintEffectNoSchedule}, }, - Volumes: []v1.Volume{ + Volumes: []corev1.Volume{ { Name: "foo", - VolumeSource: v1.VolumeSource{ - PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{ + VolumeSource: corev1.VolumeSource{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ ClaimName: "bar", }, }, }, { Name: "aaa", - VolumeSource: v1.VolumeSource{ - PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{ + VolumeSource: corev1.VolumeSource{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ ClaimName: "bbb", }, }, }, }, - ImagePullSecrets: []v1.LocalObjectReference{ + ImagePullSecrets: []corev1.LocalObjectReference{ {Name: "foo"}, }, }, @@ -822,66 +830,66 @@ func TestMergePodSpec(t *testing.T) { "xxx": "yyy", }, ServiceAccountName: "testAccount", - Volumes: []v1.Volume{ + Volumes: []corev1.Volume{ { Name: "foo", - VolumeSource: v1.VolumeSource{ - PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{ + VolumeSource: corev1.VolumeSource{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ ClaimName: "baz", }, }, }, { Name: "xxx", - VolumeSource: v1.VolumeSource{ - PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{ + VolumeSource: corev1.VolumeSource{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ ClaimName: "yyy", }, }, }, }, - ImagePullSecrets: []v1.LocalObjectReference{ + ImagePullSecrets: []corev1.LocalObjectReference{ {Name: "foo"}, {Name: "bar"}, }, }, - expected: &v1.PodSpec{ + expected: &corev1.PodSpec{ NodeSelector: map[string]string{ "foo": "baz", "xxx": "yyy", "aaa": "bbb", }, - Tolerations: []v1.Toleration{ - {Key: "key1", Operator: v1.TolerationOpExists, Effect: v1.TaintEffectNoSchedule}, + Tolerations: []corev1.Toleration{ + {Key: "key1", Operator: corev1.TolerationOpExists, Effect: corev1.TaintEffectNoSchedule}, }, ServiceAccountName: "testAccount", - Volumes: []v1.Volume{ + Volumes: []corev1.Volume{ { Name: "foo", - VolumeSource: v1.VolumeSource{ - PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{ + VolumeSource: corev1.VolumeSource{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ ClaimName: "baz", }, }, }, { Name: "xxx", - VolumeSource: v1.VolumeSource{ - PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{ + VolumeSource: corev1.VolumeSource{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ ClaimName: "yyy", }, }, }, { Name: "aaa", - VolumeSource: v1.VolumeSource{ - PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{ + VolumeSource: corev1.VolumeSource{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ ClaimName: "bbb", }, }, }, }, - ImagePullSecrets: []v1.LocalObjectReference{ + ImagePullSecrets: []corev1.LocalObjectReference{ {Name: "foo"}, {Name: "bar"}, }, @@ -916,7 +924,7 @@ func TestGetServingRuntime(t *testing.T) { }, }, ServingRuntimePodSpec: v1alpha1.ServingRuntimePodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Name: "kserve-container", Image: tfRuntime + "-image:latest", @@ -933,7 +941,7 @@ func TestGetServingRuntime(t *testing.T) { }, }, ServingRuntimePodSpec: v1alpha1.ServingRuntimePodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Name: "kserve-container", Image: sklearnRuntime + "-image:latest", @@ -987,7 +995,7 @@ func TestGetServingRuntime(t *testing.T) { mockClient := fake.NewClientBuilder().WithLists(runtimes, clusterRuntimes).WithScheme(s).Build() for name, scenario := range scenarios { t.Run(name, func(t *testing.T) { - res, _ := GetServingRuntime(mockClient, scenario.runtimeName, namespace) + res, _ := GetServingRuntime(context.Background(), mockClient, scenario.runtimeName, namespace) if !g.Expect(res).To(gomega.Equal(&scenario.expected)) { t.Errorf("got %v, want %v", res, &scenario.expected) } @@ -996,25 +1004,24 @@ func TestGetServingRuntime(t *testing.T) { // Check invalid case t.Run("InvalidServingRuntime", func(t *testing.T) { - res, err := GetServingRuntime(mockClient, "foo", namespace) + res, err := GetServingRuntime(context.Background(), mockClient, "foo", namespace) if !g.Expect(res).To(gomega.BeNil()) { t.Errorf("got %v, want %v", res, nil) } g.Expect(err.Error()).To(gomega.ContainSubstring("No ServingRuntimes or ClusterServingRuntimes with the name")) }) - } func TestReplacePlaceholders(t *testing.T) { g := gomega.NewGomegaWithT(t) scenarios := map[string]struct { - container *v1.Container + container *corev1.Container meta metav1.ObjectMeta - expected *v1.Container + expected *corev1.Container }{ "ReplaceArgsAndEnvPlaceholders": { - container: &v1.Container{ + container: &corev1.Container{ Name: "kserve-container", Image: "default-image", Args: []string{ @@ -1022,18 +1029,18 @@ func TestReplacePlaceholders(t *testing.T) { "--test=dummy", "--new-arg=baz", }, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "PORT", Value: "8080"}, {Name: "MODELS_DIR", Value: "{{.Labels.modelDir}}"}, }, - Resources: v1.ResourceRequirements{ - Limits: v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("2"), - v1.ResourceMemory: resource.MustParse("4Gi"), + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("2"), + corev1.ResourceMemory: resource.MustParse("4Gi"), }, - Requests: v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("1"), - v1.ResourceMemory: resource.MustParse("2Gi"), + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("2Gi"), }, }, }, @@ -1043,7 +1050,7 @@ func TestReplacePlaceholders(t *testing.T) { "modelDir": "/mnt/models", }, }, - expected: &v1.Container{ + expected: &corev1.Container{ Name: "kserve-container", Image: "default-image", Args: []string{ @@ -1051,18 +1058,18 @@ func TestReplacePlaceholders(t *testing.T) { "--test=dummy", "--new-arg=baz", }, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "PORT", Value: "8080"}, {Name: "MODELS_DIR", Value: "/mnt/models"}, }, - Resources: v1.ResourceRequirements{ - Limits: v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("2"), - v1.ResourceMemory: resource.MustParse("4Gi"), + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("2"), + corev1.ResourceMemory: resource.MustParse("4Gi"), }, - Requests: v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("1"), - v1.ResourceMemory: resource.MustParse("2Gi"), + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("2Gi"), }, }, }, @@ -1082,14 +1089,14 @@ func TestUpdateImageTag(t *testing.T) { g := gomega.NewGomegaWithT(t) scenarios := map[string]struct { - container *v1.Container + container *corev1.Container runtimeVersion *string servingRuntime string isvcConfig *InferenceServicesConfig expected string }{ "UpdateRuntimeVersion": { - container: &v1.Container{ + container: &corev1.Container{ Name: "kserve-container", Image: "tfserving", Args: []string{ @@ -1097,18 +1104,18 @@ func TestUpdateImageTag(t *testing.T) { "--test=dummy", "--new-arg=baz", }, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "PORT", Value: "8080"}, {Name: "MODELS_DIR", Value: "/mnt/models"}, }, - Resources: v1.ResourceRequirements{ - Limits: v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("2"), - v1.ResourceMemory: resource.MustParse("4Gi"), + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("2"), + corev1.ResourceMemory: resource.MustParse("4Gi"), }, - Requests: v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("1"), - v1.ResourceMemory: resource.MustParse("2Gi"), + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("2Gi"), }, }, }, @@ -1117,7 +1124,7 @@ func TestUpdateImageTag(t *testing.T) { expected: "tfserving:2.6.2", }, "UpdateTFServingGPUImageTag": { - container: &v1.Container{ + container: &corev1.Container{ Name: "kserve-container", Image: "tfserving:1.14.0", Args: []string{ @@ -1125,12 +1132,12 @@ func TestUpdateImageTag(t *testing.T) { "--test=dummy", "--new-arg=baz", }, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "PORT", Value: "8080"}, {Name: "MODELS_DIR", Value: "/mnt/models"}, }, - Resources: v1.ResourceRequirements{ - Limits: v1.ResourceList{ + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ "nvidia.com/gpu": resource.MustParse("1"), }, }, @@ -1140,7 +1147,7 @@ func TestUpdateImageTag(t *testing.T) { expected: "tfserving:1.14.0-gpu", }, "UpdateHuggingFaceServerGPUImageTag": { - container: &v1.Container{ + container: &corev1.Container{ Name: "kserve-container", Image: "huggingfaceserver:1.14.0", Args: []string{ @@ -1148,12 +1155,12 @@ func TestUpdateImageTag(t *testing.T) { "--test=dummy", "--new-arg=baz", }, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "PORT", Value: "8080"}, {Name: "MODELS_DIR", Value: "/mnt/models"}, }, - Resources: v1.ResourceRequirements{ - Limits: v1.ResourceList{ + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ "nvidia.com/gpu": resource.MustParse("1"), }, }, @@ -1163,7 +1170,7 @@ func TestUpdateImageTag(t *testing.T) { expected: "huggingfaceserver:1.14.0-gpu", }, "UpdateGPUImageTagWithProxy": { - container: &v1.Container{ + container: &corev1.Container{ Name: "kserve-container", Image: "localhost:8888/tfserving:1.14.0", Args: []string{ @@ -1171,12 +1178,12 @@ func TestUpdateImageTag(t *testing.T) { "--test=dummy", "--new-arg=baz", }, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "PORT", Value: "8080"}, {Name: "MODELS_DIR", Value: "/mnt/models"}, }, - Resources: v1.ResourceRequirements{ - Limits: v1.ResourceList{ + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ "nvidia.com/gpu": resource.MustParse("1"), }, }, @@ -1186,7 +1193,7 @@ func TestUpdateImageTag(t *testing.T) { expected: "localhost:8888/tfserving:1.14.0-gpu", }, "UpdateRuntimeVersionWithProxy": { - container: &v1.Container{ + container: &corev1.Container{ Name: "kserve-container", Image: "localhost:8888/tfserving", Args: []string{ @@ -1194,18 +1201,18 @@ func TestUpdateImageTag(t *testing.T) { "--test=dummy", "--new-arg=baz", }, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "PORT", Value: "8080"}, {Name: "MODELS_DIR", Value: "/mnt/models"}, }, - Resources: v1.ResourceRequirements{ - Limits: v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("2"), - v1.ResourceMemory: resource.MustParse("4Gi"), + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("2"), + corev1.ResourceMemory: resource.MustParse("4Gi"), }, - Requests: v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("1"), - v1.ResourceMemory: resource.MustParse("2Gi"), + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("2Gi"), }, }, }, @@ -1214,7 +1221,7 @@ func TestUpdateImageTag(t *testing.T) { expected: "localhost:8888/tfserving:2.6.2", }, "UpdateRuntimeVersionWithProxyAndTag": { - container: &v1.Container{ + container: &corev1.Container{ Name: "kserve-container", Image: "localhost:8888/tfserving:1.2.3", Args: []string{ @@ -1222,18 +1229,18 @@ func TestUpdateImageTag(t *testing.T) { "--test=dummy", "--new-arg=baz", }, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "PORT", Value: "8080"}, {Name: "MODELS_DIR", Value: "/mnt/models"}, }, - Resources: v1.ResourceRequirements{ - Limits: v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("2"), - v1.ResourceMemory: resource.MustParse("4Gi"), + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("2"), + corev1.ResourceMemory: resource.MustParse("4Gi"), }, - Requests: v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("1"), - v1.ResourceMemory: resource.MustParse("2Gi"), + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("2Gi"), }, }, }, @@ -1264,28 +1271,28 @@ func TestGetDeploymentMode(t *testing.T) { constants.DeploymentMode: string(constants.RawDeployment), }, deployConfig: &DeployConfig{}, - expected: constants.DeploymentModeType(constants.RawDeployment), + expected: constants.RawDeployment, }, "ServerlessDeployment": { annotations: map[string]string{ constants.DeploymentMode: string(constants.Serverless), }, deployConfig: &DeployConfig{}, - expected: constants.DeploymentModeType(constants.Serverless), + expected: constants.Serverless, }, "ModelMeshDeployment": { annotations: map[string]string{ constants.DeploymentMode: string(constants.ModelMeshDeployment), }, deployConfig: &DeployConfig{}, - expected: constants.DeploymentModeType(constants.ModelMeshDeployment), + expected: constants.ModelMeshDeployment, }, "DefaultDeploymentMode": { annotations: map[string]string{}, deployConfig: &DeployConfig{ DefaultDeploymentMode: string(constants.Serverless), }, - expected: constants.DeploymentModeType(constants.Serverless), + expected: constants.Serverless, }, } @@ -1301,11 +1308,11 @@ func TestGetDeploymentMode(t *testing.T) { func TestModelName(t *testing.T) { g := gomega.NewGomegaWithT(t) - var requestedResource = v1.ResourceRequirements{ - Limits: v1.ResourceList{ + requestedResource := corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ "cpu": resource.MustParse("100m"), }, - Requests: v1.ResourceList{ + Requests: corev1.ResourceList{ "cpu": resource.MustParse("90m"), }, } @@ -1326,7 +1333,7 @@ func TestModelName(t *testing.T) { Name: "sklearn", }, PredictorExtensionSpec: PredictorExtensionSpec{ - Container: v1.Container{ + Container: corev1.Container{ Image: "customImage:0.1.0", Resources: requestedResource, }, @@ -1350,7 +1357,7 @@ func TestModelName(t *testing.T) { }, PredictorExtensionSpec: PredictorExtensionSpec{ StorageURI: proto.String("gs://someUri"), - Container: v1.Container{ + Container: corev1.Container{ Args: []string{"--model_name=sklearn-custom"}, Resources: requestedResource, }, @@ -1369,8 +1376,9 @@ func TestModelName(t *testing.T) { Spec: InferenceServiceSpec{ Predictor: PredictorSpec{ PodSpec: PodSpec{ - Containers: []v1.Container{ - {Name: constants.InferenceServiceContainerName, + Containers: []corev1.Container{ + { + Name: constants.InferenceServiceContainerName, Image: "some-image", }, }, @@ -1388,8 +1396,9 @@ func TestModelName(t *testing.T) { Spec: InferenceServiceSpec{ Predictor: PredictorSpec{ PodSpec: PodSpec{ - Containers: []v1.Container{ - {Name: constants.InferenceServiceContainerName, + Containers: []corev1.Container{ + { + Name: constants.InferenceServiceContainerName, Image: "some-image", Args: []string{"--model_name=custom-model"}, }, @@ -1412,8 +1421,8 @@ func TestModelName(t *testing.T) { Name: "sklearn", }, PredictorExtensionSpec: PredictorExtensionSpec{ - Container: v1.Container{ - Env: []v1.EnvVar{{Name: constants.MLServerModelNameEnv, Value: "sklearn-custom"}}, + Container: corev1.Container{ + Env: []corev1.EnvVar{{Name: constants.MLServerModelNameEnv, Value: "sklearn-custom"}}, Resources: requestedResource, }, }, @@ -1431,8 +1440,9 @@ func TestModelName(t *testing.T) { Spec: InferenceServiceSpec{ Transformer: &TransformerSpec{ PodSpec: PodSpec{ - Containers: []v1.Container{ - {Name: constants.InferenceServiceContainerName, + Containers: []corev1.Container{ + { + Name: constants.InferenceServiceContainerName, Image: "some-image", Args: []string{"--model_name=custom-model"}, }, @@ -1445,7 +1455,7 @@ func TestModelName(t *testing.T) { Name: "sklearn", }, PredictorExtensionSpec: PredictorExtensionSpec{ - Container: v1.Container{ + Container: corev1.Container{ Resources: requestedResource, }, }, @@ -1468,7 +1478,7 @@ func TestModelName(t *testing.T) { }, PredictorExtensionSpec: PredictorExtensionSpec{ - Container: v1.Container{ + Container: corev1.Container{ Image: "customImage:0.1.0", Resources: requestedResource, Args: []string{"--model_name=sklearn", "--model_dir", "/mnt/models", "--model_name", "iris"}, @@ -1493,7 +1503,7 @@ func TestModelName(t *testing.T) { }, PredictorExtensionSpec: PredictorExtensionSpec{ - Container: v1.Container{ + Container: corev1.Container{ Image: "customImage:0.1.0", Resources: requestedResource, Args: []string{"--model_dir", "/mnt/models", "--model_name iris"}, // This format is not recognized by the modelserver. So we ignore this format. @@ -1518,7 +1528,7 @@ func TestModelName(t *testing.T) { }, PredictorExtensionSpec: PredictorExtensionSpec{ - Container: v1.Container{ + Container: corev1.Container{ Image: "customImage:0.1.0", Resources: requestedResource, Args: []string{"--model_dir", "/mnt/models", "--model_name", "iris"}, @@ -1544,11 +1554,11 @@ func TestModelName(t *testing.T) { func TestGetPredictorEndpoint(t *testing.T) { g := gomega.NewGomegaWithT(t) - var requestedResource = v1.ResourceRequirements{ - Limits: v1.ResourceList{ + requestedResource := corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ "cpu": resource.MustParse("100m"), }, - Requests: v1.ResourceList{ + Requests: corev1.ResourceList{ "cpu": resource.MustParse("90m"), }, } @@ -1570,7 +1580,7 @@ func TestGetPredictorEndpoint(t *testing.T) { Name: "sklearn", }, PredictorExtensionSpec: PredictorExtensionSpec{ - Container: v1.Container{ + Container: corev1.Container{ Image: "customImage:0.1.0", Resources: requestedResource, }, @@ -1637,7 +1647,7 @@ func TestGetPredictorEndpoint(t *testing.T) { }, Transformer: &TransformerSpec{ PodSpec: PodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, Image: "kserve/transformer:1.0", @@ -1706,11 +1716,11 @@ func TestGetPredictorEndpoint(t *testing.T) { }, Transformer: &TransformerSpec{ PodSpec: PodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, Image: "kserve/transformer:1.0", - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: constants.CustomSpecProtocolEnvVarKey, Value: string(constants.ProtocolV2), @@ -1741,13 +1751,13 @@ func TestGetPredictorEndpoint(t *testing.T) { Spec: InferenceServiceSpec{ Predictor: PredictorSpec{ PodSpec: PodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, Image: "kserve/custom-image:1.0", Args: []string{"--model_name=sklearn-custom"}, Resources: requestedResource, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: constants.CustomSpecProtocolEnvVarKey, Value: string(constants.ProtocolV2), @@ -1796,11 +1806,11 @@ func TestGetPredictorEndpoint(t *testing.T) { }, Transformer: &TransformerSpec{ PodSpec: PodSpec{ - Containers: []v1.Container{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, Image: "kserve/transformer:1.0", - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: constants.CustomSpecProtocolEnvVarKey, Value: string(constants.ProtocolV2), @@ -1855,7 +1865,7 @@ func TestValidateStorageURIForDefaultStorageInitializer(t *testing.T) { } mockClient := fake.NewClientBuilder().WithScheme(s).Build() for _, uri := range validUris { - if err := ValidateStorageURI(&uri, mockClient); err != nil { + if err := ValidateStorageURI(context.Background(), &uri, mockClient); err != nil { t.Errorf("%q validation failed: %s", uri, err) } } @@ -1872,7 +1882,7 @@ func TestValidateStorageURIForCustomPrefix(t *testing.T) { } mockClient := fake.NewClientBuilder().WithScheme(s).Build() for _, uri := range invalidUris { - if err := ValidateStorageURI(&uri, mockClient); err == nil { + if err := ValidateStorageURI(context.Background(), &uri, mockClient); err == nil { t.Errorf("%q validation failed: error expected", uri) } } @@ -1884,11 +1894,11 @@ func TestValidateStorageURIForDefaultStorageInitializerCRD(t *testing.T) { Name: "custom", }, Spec: v1alpha1.StorageContainerSpec{ - Container: v1.Container{ + Container: corev1.Container{ Image: "kserve/storage-initializer:latest", - Resources: v1.ResourceRequirements{ - Limits: v1.ResourceList{ - v1.ResourceMemory: resource.MustParse("200Mi"), + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("200Mi"), }, }, }, @@ -1910,7 +1920,7 @@ func TestValidateStorageURIForDefaultStorageInitializerCRD(t *testing.T) { } mockClient := fake.NewClientBuilder().WithLists(storageContainerSpecs).WithScheme(s).Build() for _, uri := range validUris { - if err := ValidateStorageURI(&uri, mockClient); err != nil { + if err := ValidateStorageURI(context.Background(), &uri, mockClient); err != nil { t.Errorf("%q validation failed: %s", uri, err) } } @@ -1920,24 +1930,24 @@ func TestAddEnvVarToPodSpec(t *testing.T) { g := gomega.NewGomegaWithT(t) scenarios := map[string]struct { - pod *v1.Pod + pod *corev1.Pod targetContainerName string envName string envValue string - expectedPodSpec *v1.PodSpec + expectedPodSpec *corev1.PodSpec expectedErr gomega.OmegaMatcher }{ "addNewEnv": { targetContainerName: "test-container", - pod: &v1.Pod{ + pod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "test-pod", }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: "test-container", - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "EXISTING_VAR", Value: "existing_value", @@ -1949,11 +1959,11 @@ func TestAddEnvVarToPodSpec(t *testing.T) { }, envName: "NEW_ENV", envValue: "new_value", - expectedPodSpec: &v1.PodSpec{ - Containers: []v1.Container{ + expectedPodSpec: &corev1.PodSpec{ + Containers: []corev1.Container{ { Name: "test-container", - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "EXISTING_VAR", Value: "existing_value", @@ -1970,15 +1980,15 @@ func TestAddEnvVarToPodSpec(t *testing.T) { }, "updateExistingEnv": { targetContainerName: "test-container", - pod: &v1.Pod{ + pod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "test-pod", }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: "test-container", - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "EXISTING_VAR", Value: "existing_value", @@ -1990,11 +2000,11 @@ func TestAddEnvVarToPodSpec(t *testing.T) { }, envName: "EXISTING_VAR", envValue: "updated_value", - expectedPodSpec: &v1.PodSpec{ - Containers: []v1.Container{ + expectedPodSpec: &corev1.PodSpec{ + Containers: []corev1.Container{ { Name: "test-container", - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "EXISTING_VAR", Value: "updated_value", @@ -2007,15 +2017,15 @@ func TestAddEnvVarToPodSpec(t *testing.T) { }, "updateExistingEnvWithSpecificContainer": { targetContainerName: "target-container", - pod: &v1.Pod{ + pod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "test-pod", }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: "target-container", - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "EXISTING_VAR", Value: "existing_value", @@ -2024,7 +2034,7 @@ func TestAddEnvVarToPodSpec(t *testing.T) { }, { Name: "test-container", - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "EXISTING_VAR", Value: "existing_value", @@ -2036,11 +2046,11 @@ func TestAddEnvVarToPodSpec(t *testing.T) { }, envName: "EXISTING_VAR", envValue: "updated_value", - expectedPodSpec: &v1.PodSpec{ - Containers: []v1.Container{ + expectedPodSpec: &corev1.PodSpec{ + Containers: []corev1.Container{ { Name: "target-container", - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "EXISTING_VAR", Value: "updated_value", @@ -2049,7 +2059,7 @@ func TestAddEnvVarToPodSpec(t *testing.T) { }, { Name: "test-container", - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "EXISTING_VAR", Value: "existing_value", @@ -2062,15 +2072,15 @@ func TestAddEnvVarToPodSpec(t *testing.T) { }, "addNewEnvWithSpecificContainer": { targetContainerName: "target-container", - pod: &v1.Pod{ + pod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "test-pod", }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: "target-container", - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "EXISTING_VAR", Value: "existing_value", @@ -2079,7 +2089,7 @@ func TestAddEnvVarToPodSpec(t *testing.T) { }, { Name: "test-container", - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "EXISTING_VAR", Value: "existing_value", @@ -2091,11 +2101,11 @@ func TestAddEnvVarToPodSpec(t *testing.T) { }, envName: "NEW_ENV", envValue: "new_value", - expectedPodSpec: &v1.PodSpec{ - Containers: []v1.Container{ + expectedPodSpec: &corev1.PodSpec{ + Containers: []corev1.Container{ { Name: "target-container", - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "EXISTING_VAR", Value: "existing_value", @@ -2108,7 +2118,7 @@ func TestAddEnvVarToPodSpec(t *testing.T) { }, { Name: "test-container", - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "EXISTING_VAR", Value: "existing_value", @@ -2121,15 +2131,15 @@ func TestAddEnvVarToPodSpec(t *testing.T) { }, "AddEnvToWrongContainer": { targetContainerName: "test-container", - pod: &v1.Pod{ + pod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "test-pod", }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: "wrong-container", - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "EXISTING_VAR", Value: "existing_value", @@ -2141,11 +2151,11 @@ func TestAddEnvVarToPodSpec(t *testing.T) { }, envName: "EXISTING_VAR", envValue: "updated_value", - expectedPodSpec: &v1.PodSpec{ - Containers: []v1.Container{ + expectedPodSpec: &corev1.PodSpec{ + Containers: []corev1.Container{ { Name: "test-container", - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "EXISTING_VAR", Value: "existing_value", @@ -2171,47 +2181,51 @@ func TestMergeServingRuntimeAndInferenceServiceSpecs(t *testing.T) { g := gomega.NewGomegaWithT(t) scenarios := map[string]struct { - srContainers []v1.Container - isvcContainer v1.Container + srContainers []corev1.Container + isvcContainer corev1.Container isvc *InferenceService targetContainerName string srPodSpec v1alpha1.ServingRuntimePodSpec isvcPodSpec PodSpec - expectedContainer *v1.Container - expectedPodSpec *v1.PodSpec + expectedContainer *corev1.Container + expectedPodSpec *corev1.PodSpec expectedErr gomega.OmegaMatcher }{ "Merge container when there is no target container": { - srContainers: []v1.Container{ + srContainers: []corev1.Container{ {Name: "containerA"}, }, - isvcContainer: v1.Container{Name: "containerA"}, + isvcContainer: corev1.Container{Name: "containerA"}, isvc: &InferenceService{}, targetContainerName: "containerA", srPodSpec: v1alpha1.ServingRuntimePodSpec{}, isvcPodSpec: PodSpec{}, - expectedContainer: &v1.Container{Name: "containerA"}, - expectedPodSpec: &v1.PodSpec{}, + expectedContainer: &corev1.Container{Name: "containerA"}, + expectedPodSpec: &corev1.PodSpec{}, expectedErr: gomega.BeNil(), }, "Merge container when there is target container": { - srContainers: []v1.Container{ + srContainers: []corev1.Container{ {Name: "containerA"}, }, - isvcContainer: v1.Container{Name: "containerA", - Env: []v1.EnvVar{{Name: "test", Value: "test"}}}, + isvcContainer: corev1.Container{ + Name: "containerA", + Env: []corev1.EnvVar{{Name: "test", Value: "test"}}, + }, isvc: &InferenceService{}, targetContainerName: "containerA", srPodSpec: v1alpha1.ServingRuntimePodSpec{}, isvcPodSpec: PodSpec{}, - expectedContainer: &v1.Container{Name: "containerA", - Env: []v1.EnvVar{{Name: "test", Value: "test"}}}, - expectedPodSpec: &v1.PodSpec{}, + expectedContainer: &corev1.Container{ + Name: "containerA", + Env: []corev1.EnvVar{{Name: "test", Value: "test"}}, + }, + expectedPodSpec: &corev1.PodSpec{}, expectedErr: gomega.BeNil(), }, "Return error when invalid container name": { - srContainers: []v1.Container{{Name: "containerA"}}, - isvcContainer: v1.Container{Name: "containerB"}, + srContainers: []corev1.Container{{Name: "containerA"}}, + isvcContainer: corev1.Container{Name: "containerB"}, isvc: &InferenceService{}, targetContainerName: "nonExistentContainer", srPodSpec: v1alpha1.ServingRuntimePodSpec{}, @@ -2221,29 +2235,29 @@ func TestMergeServingRuntimeAndInferenceServiceSpecs(t *testing.T) { expectedErr: gomega.HaveOccurred(), }, "Merge podSpec when there is target container": { - srContainers: []v1.Container{ + srContainers: []corev1.Container{ {Name: "containerA"}, }, - isvcContainer: v1.Container{Name: "containerA"}, + isvcContainer: corev1.Container{Name: "containerA"}, isvc: &InferenceService{}, targetContainerName: "containerA", - srPodSpec: v1alpha1.ServingRuntimePodSpec{Containers: []v1.Container{{Name: "containerA", Env: []v1.EnvVar{{Name: "original", Value: "original"}}}}}, - isvcPodSpec: PodSpec{Containers: []v1.Container{{Name: "containerA", Env: []v1.EnvVar{{Name: "test", Value: "test"}}}}}, - expectedContainer: &v1.Container{Name: "containerA"}, - expectedPodSpec: &v1.PodSpec{Containers: []v1.Container{{Name: "containerA", Env: []v1.EnvVar{{Name: "original", Value: "original"}, {Name: "test", Value: "test"}}}}}, + srPodSpec: v1alpha1.ServingRuntimePodSpec{Containers: []corev1.Container{{Name: "containerA", Env: []corev1.EnvVar{{Name: "original", Value: "original"}}}}}, + isvcPodSpec: PodSpec{Containers: []corev1.Container{{Name: "containerA", Env: []corev1.EnvVar{{Name: "test", Value: "test"}}}}}, + expectedContainer: &corev1.Container{Name: "containerA"}, + expectedPodSpec: &corev1.PodSpec{Containers: []corev1.Container{{Name: "containerA", Env: []corev1.EnvVar{{Name: "original", Value: "original"}, {Name: "test", Value: "test"}}}}}, expectedErr: gomega.BeNil(), }, "Merge podSpec when there is no target container": { - srContainers: []v1.Container{ + srContainers: []corev1.Container{ {Name: "containerA"}, }, - isvcContainer: v1.Container{Name: "containerA"}, + isvcContainer: corev1.Container{Name: "containerA"}, isvc: &InferenceService{}, targetContainerName: "containerA", - srPodSpec: v1alpha1.ServingRuntimePodSpec{Containers: []v1.Container{{Name: "containerA", Env: []v1.EnvVar{{Name: "original", Value: "original"}}}}}, - isvcPodSpec: PodSpec{Containers: []v1.Container{{Name: "containerB", Env: []v1.EnvVar{{Name: "test", Value: "test"}}}}}, - expectedContainer: &v1.Container{Name: "containerA"}, - expectedPodSpec: &v1.PodSpec{Containers: []v1.Container{{Name: "containerA", Env: []v1.EnvVar{{Name: "original", Value: "original"}}}}}, + srPodSpec: v1alpha1.ServingRuntimePodSpec{Containers: []corev1.Container{{Name: "containerA", Env: []corev1.EnvVar{{Name: "original", Value: "original"}}}}}, + isvcPodSpec: PodSpec{Containers: []corev1.Container{{Name: "containerB", Env: []corev1.EnvVar{{Name: "test", Value: "test"}}}}}, + expectedContainer: &corev1.Container{Name: "containerA"}, + expectedPodSpec: &corev1.PodSpec{Containers: []corev1.Container{{Name: "containerA", Env: []corev1.EnvVar{{Name: "original", Value: "original"}}}}}, expectedErr: gomega.BeNil(), }, } diff --git a/pkg/credentials/azure/azure_secret.go b/pkg/credentials/azure/azure_secret.go index af9016a3046..4148b04c531 100644 --- a/pkg/credentials/azure/azure_secret.go +++ b/pkg/credentials/azure/azure_secret.go @@ -17,7 +17,7 @@ limitations under the License. package azure import ( - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" ) const ( @@ -46,8 +46,8 @@ var ( } ) -func BuildSecretEnvs(secret *v1.Secret) []v1.EnvVar { - envs := make([]v1.EnvVar, 0, len(AzureEnvKeys)) +func BuildSecretEnvs(secret *corev1.Secret) []corev1.EnvVar { + envs := make([]corev1.EnvVar, 0, len(AzureEnvKeys)) for _, k := range AzureEnvKeys { dataKey := k legacyDataKey := legacyAzureEnvKeyMappings[k] @@ -56,11 +56,11 @@ func BuildSecretEnvs(secret *v1.Secret) []v1.EnvVar { } // Leave out the AzureClientSecret or AzureStorageAccessKey env var if not defined as Data in the secret if _, ok := secret.Data[dataKey]; !(!ok && (dataKey == AzureClientSecret || dataKey == AzureStorageAccessKey)) { - envs = append(envs, v1.EnvVar{ + envs = append(envs, corev1.EnvVar{ Name: k, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: secret.Name, }, Key: dataKey, @@ -73,13 +73,13 @@ func BuildSecretEnvs(secret *v1.Secret) []v1.EnvVar { return envs } -func BuildStorageAccessKeySecretEnv(secret *v1.Secret) []v1.EnvVar { - envs := []v1.EnvVar{ +func BuildStorageAccessKeySecretEnv(secret *corev1.Secret) []corev1.EnvVar { + envs := []corev1.EnvVar{ { Name: AzureStorageAccessKey, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: secret.Name, }, Key: AzureStorageAccessKey, diff --git a/pkg/credentials/azure/azure_secret_test.go b/pkg/credentials/azure/azure_secret_test.go index 4479ae1970d..98721f6f1cf 100644 --- a/pkg/credentials/azure/azure_secret_test.go +++ b/pkg/credentials/azure/azure_secret_test.go @@ -20,17 +20,17 @@ import ( "testing" "github.com/google/go-cmp/cmp" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) func TestAzureSecret(t *testing.T) { scenarios := map[string]struct { - secret *v1.Secret - expected []v1.EnvVar + secret *corev1.Secret + expected []corev1.EnvVar }{ "AzureSecretEnvsWithClientSecret": { - secret: &v1.Secret{ + secret: &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: "azcreds", }, @@ -41,12 +41,12 @@ func TestAzureSecret(t *testing.T) { AzureClientSecret: []byte("AzureClientSecret"), }, }, - expected: []v1.EnvVar{ + expected: []corev1.EnvVar{ { Name: AzureSubscriptionId, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "azcreds", }, Key: AzureSubscriptionId, @@ -55,9 +55,9 @@ func TestAzureSecret(t *testing.T) { }, { Name: AzureTenantId, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "azcreds", }, Key: AzureTenantId, @@ -66,9 +66,9 @@ func TestAzureSecret(t *testing.T) { }, { Name: AzureClientId, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "azcreds", }, Key: AzureClientId, @@ -77,9 +77,9 @@ func TestAzureSecret(t *testing.T) { }, { Name: AzureClientSecret, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "azcreds", }, Key: AzureClientSecret, @@ -89,7 +89,7 @@ func TestAzureSecret(t *testing.T) { }, }, "AzureSecretEnvsWithStorageAccessKey": { - secret: &v1.Secret{ + secret: &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: "azcreds", }, @@ -100,12 +100,12 @@ func TestAzureSecret(t *testing.T) { AzureStorageAccessKey: []byte("AzureStorageAccessKey"), }, }, - expected: []v1.EnvVar{ + expected: []corev1.EnvVar{ { Name: AzureSubscriptionId, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "azcreds", }, Key: AzureSubscriptionId, @@ -114,9 +114,9 @@ func TestAzureSecret(t *testing.T) { }, { Name: AzureTenantId, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "azcreds", }, Key: AzureTenantId, @@ -125,9 +125,9 @@ func TestAzureSecret(t *testing.T) { }, { Name: AzureClientId, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "azcreds", }, Key: AzureClientId, @@ -136,9 +136,9 @@ func TestAzureSecret(t *testing.T) { }, { Name: AzureStorageAccessKey, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "azcreds", }, Key: AzureStorageAccessKey, @@ -148,7 +148,7 @@ func TestAzureSecret(t *testing.T) { }, }, "AzureSecretEnvsWithClientSecretAndStorageAccessKey": { - secret: &v1.Secret{ + secret: &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: "azcreds", }, @@ -160,12 +160,12 @@ func TestAzureSecret(t *testing.T) { AzureStorageAccessKey: []byte("AzureStorageAccessKey"), }, }, - expected: []v1.EnvVar{ + expected: []corev1.EnvVar{ { Name: AzureSubscriptionId, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "azcreds", }, Key: AzureSubscriptionId, @@ -174,9 +174,9 @@ func TestAzureSecret(t *testing.T) { }, { Name: AzureTenantId, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "azcreds", }, Key: AzureTenantId, @@ -185,9 +185,9 @@ func TestAzureSecret(t *testing.T) { }, { Name: AzureClientId, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "azcreds", }, Key: AzureClientId, @@ -196,9 +196,9 @@ func TestAzureSecret(t *testing.T) { }, { Name: AzureClientSecret, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "azcreds", }, Key: AzureClientSecret, @@ -207,9 +207,9 @@ func TestAzureSecret(t *testing.T) { }, { Name: AzureStorageAccessKey, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "azcreds", }, Key: AzureStorageAccessKey, @@ -219,7 +219,7 @@ func TestAzureSecret(t *testing.T) { }, }, "AzureSecretEnvsWithoutClientSecret": { - secret: &v1.Secret{ + secret: &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: "azcreds", }, @@ -230,12 +230,12 @@ func TestAzureSecret(t *testing.T) { AzureStorageAccessKey: []byte("AzureStorageAccessKey"), }, }, - expected: []v1.EnvVar{ + expected: []corev1.EnvVar{ { Name: AzureSubscriptionId, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "azcreds", }, Key: AzureSubscriptionId, @@ -244,9 +244,9 @@ func TestAzureSecret(t *testing.T) { }, { Name: AzureTenantId, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "azcreds", }, Key: AzureTenantId, @@ -255,9 +255,9 @@ func TestAzureSecret(t *testing.T) { }, { Name: AzureClientId, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "azcreds", }, Key: AzureClientId, @@ -266,9 +266,9 @@ func TestAzureSecret(t *testing.T) { }, { Name: AzureStorageAccessKey, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "azcreds", }, Key: AzureStorageAccessKey, @@ -278,7 +278,7 @@ func TestAzureSecret(t *testing.T) { }, }, "AzureSecretEnvsWithoutStorageAccessKey": { - secret: &v1.Secret{ + secret: &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: "azcreds", }, @@ -289,12 +289,12 @@ func TestAzureSecret(t *testing.T) { AzureClientSecret: []byte("AzureClientSecret"), }, }, - expected: []v1.EnvVar{ + expected: []corev1.EnvVar{ { Name: AzureSubscriptionId, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "azcreds", }, Key: AzureSubscriptionId, @@ -303,9 +303,9 @@ func TestAzureSecret(t *testing.T) { }, { Name: AzureTenantId, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "azcreds", }, Key: AzureTenantId, @@ -314,9 +314,9 @@ func TestAzureSecret(t *testing.T) { }, { Name: AzureClientId, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "azcreds", }, Key: AzureClientId, @@ -325,9 +325,9 @@ func TestAzureSecret(t *testing.T) { }, { Name: AzureClientSecret, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "azcreds", }, Key: AzureClientSecret, @@ -337,7 +337,7 @@ func TestAzureSecret(t *testing.T) { }, }, "AzureSecretEnvsWithoutClientSecretAndStorageAccessKey": { - secret: &v1.Secret{ + secret: &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: "azcreds", }, @@ -347,12 +347,12 @@ func TestAzureSecret(t *testing.T) { AzureClientId: []byte("AzureClientId"), }, }, - expected: []v1.EnvVar{ + expected: []corev1.EnvVar{ { Name: AzureSubscriptionId, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "azcreds", }, Key: AzureSubscriptionId, @@ -361,9 +361,9 @@ func TestAzureSecret(t *testing.T) { }, { Name: AzureTenantId, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "azcreds", }, Key: AzureTenantId, @@ -372,9 +372,9 @@ func TestAzureSecret(t *testing.T) { }, { Name: AzureClientId, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "azcreds", }, Key: AzureClientId, @@ -396,21 +396,21 @@ func TestAzureSecret(t *testing.T) { func TestAzureStrorageAccessSecret(t *testing.T) { scenarios := map[string]struct { - secret *v1.Secret - expected []v1.EnvVar + secret *corev1.Secret + expected []corev1.EnvVar }{ "AzureSecretEnvs": { - secret: &v1.Secret{ + secret: &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: "azcreds", }, }, - expected: []v1.EnvVar{ + expected: []corev1.EnvVar{ { Name: AzureStorageAccessKey, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "azcreds", }, Key: AzureStorageAccessKey, diff --git a/pkg/credentials/credentials_suite_test.go b/pkg/credentials/credentials_suite_test.go index 929cb89a847..103fbe06a38 100644 --- a/pkg/credentials/credentials_suite_test.go +++ b/pkg/credentials/credentials_suite_test.go @@ -29,9 +29,11 @@ import ( pkgtest "github.com/kserve/kserve/pkg/testing" ) -var cfg *rest.Config -var c client.Client -var clientset kubernetes.Interface +var ( + cfg *rest.Config + c client.Client + clientset kubernetes.Interface +) func TestMain(m *testing.M) { crdDirectoryPaths := []string{ diff --git a/pkg/credentials/gcs/gcs_secret.go b/pkg/credentials/gcs/gcs_secret.go index f024a6aa267..c4b6341a797 100644 --- a/pkg/credentials/gcs/gcs_secret.go +++ b/pkg/credentials/gcs/gcs_secret.go @@ -17,7 +17,7 @@ limitations under the License. package gcs import ( - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" ) const ( @@ -31,16 +31,16 @@ type GCSConfig struct { GCSCredentialFileName string `json:"gcsCredentialFileName,omitempty"` } -func BuildSecretVolume(secret *v1.Secret) (v1.Volume, v1.VolumeMount) { - volume := v1.Volume{ +func BuildSecretVolume(secret *corev1.Secret) (corev1.Volume, corev1.VolumeMount) { + volume := corev1.Volume{ Name: GCSCredentialVolumeName, - VolumeSource: v1.VolumeSource{ - Secret: &v1.SecretVolumeSource{ + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ SecretName: secret.Name, }, }, } - volumeMount := v1.VolumeMount{ + volumeMount := corev1.VolumeMount{ MountPath: GCSCredentialVolumeMountPath, Name: GCSCredentialVolumeName, ReadOnly: true, diff --git a/pkg/credentials/gcs/gcs_secret_test.go b/pkg/credentials/gcs/gcs_secret_test.go index 4603feb2b6a..15c2d2482e1 100644 --- a/pkg/credentials/gcs/gcs_secret_test.go +++ b/pkg/credentials/gcs/gcs_secret_test.go @@ -17,20 +17,21 @@ limitations under the License. package gcs import ( + "testing" + "github.com/google/go-cmp/cmp" - "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "testing" ) func TestGcsSecret(t *testing.T) { scenarios := map[string]struct { - secret *v1.Secret - expectedVolume v1.Volume - expectedVolumeMount v1.VolumeMount + secret *corev1.Secret + expectedVolume corev1.Volume + expectedVolumeMount corev1.VolumeMount }{ "GCSSecretVolume": { - secret: &v1.Secret{ + secret: &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: "user-gcp-sa", }, @@ -38,15 +39,15 @@ func TestGcsSecret(t *testing.T) { GCSCredentialFileName: {}, }, }, - expectedVolumeMount: v1.VolumeMount{ + expectedVolumeMount: corev1.VolumeMount{ Name: GCSCredentialVolumeName, ReadOnly: true, MountPath: GCSCredentialVolumeMountPath, }, - expectedVolume: v1.Volume{ + expectedVolume: corev1.Volume{ Name: GCSCredentialVolumeName, - VolumeSource: v1.VolumeSource{ - Secret: &v1.SecretVolumeSource{ + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ SecretName: "user-gcp-sa", }, }, diff --git a/pkg/credentials/hdfs/hdfs_secret.go b/pkg/credentials/hdfs/hdfs_secret.go index af99cf7be64..c80f743743c 100644 --- a/pkg/credentials/hdfs/hdfs_secret.go +++ b/pkg/credentials/hdfs/hdfs_secret.go @@ -17,7 +17,7 @@ limitations under the License. package hdfs import ( - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" ) const ( @@ -32,17 +32,17 @@ const ( HdfsVolumeName = "hdfs-secrets" ) -func BuildSecret(secret *v1.Secret) (v1.Volume, v1.VolumeMount) { - volume := v1.Volume{ +func BuildSecret(secret *corev1.Secret) (corev1.Volume, corev1.VolumeMount) { + volume := corev1.Volume{ Name: HdfsVolumeName, - VolumeSource: v1.VolumeSource{ - Secret: &v1.SecretVolumeSource{ + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ SecretName: secret.Name, }, }, } - volumeMount := v1.VolumeMount{ + volumeMount := corev1.VolumeMount{ MountPath: MountPath, Name: HdfsVolumeName, ReadOnly: true, diff --git a/pkg/credentials/hdfs/hdfs_secret_test.go b/pkg/credentials/hdfs/hdfs_secret_test.go index 86fb1eb572e..1d4929113d2 100644 --- a/pkg/credentials/hdfs/hdfs_secret_test.go +++ b/pkg/credentials/hdfs/hdfs_secret_test.go @@ -20,7 +20,7 @@ import ( "testing" "github.com/google/go-cmp/cmp" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -30,12 +30,12 @@ const ( func TestHdfsSecret(t *testing.T) { scenarios := map[string]struct { - secret *v1.Secret - expectedVolume v1.Volume - expectedVolumeMount v1.VolumeMount + secret *corev1.Secret + expectedVolume corev1.Volume + expectedVolumeMount corev1.VolumeMount }{ "HdfsSecret": { - secret: &v1.Secret{ + secret: &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: secretName, }, @@ -46,15 +46,15 @@ func TestHdfsSecret(t *testing.T) { KerberosKeytab: []byte("AAA="), }, }, - expectedVolume: v1.Volume{ + expectedVolume: corev1.Volume{ Name: HdfsVolumeName, - VolumeSource: v1.VolumeSource{ - Secret: &v1.SecretVolumeSource{ + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ SecretName: secretName, }, }, }, - expectedVolumeMount: v1.VolumeMount{ + expectedVolumeMount: corev1.VolumeMount{ Name: HdfsVolumeName, ReadOnly: true, MountPath: MountPath, diff --git a/pkg/credentials/hf/hf_secret.go b/pkg/credentials/hf/hf_secret.go index 4db2136243e..8909a8eaf50 100644 --- a/pkg/credentials/hf/hf_secret.go +++ b/pkg/credentials/hf/hf_secret.go @@ -17,7 +17,7 @@ limitations under the License. package hf import ( - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" ) const ( @@ -25,11 +25,11 @@ const ( HFTransfer = "HF_HUB_ENABLE_HF_TRANSFER" ) -func BuildSecretEnvs(secret *v1.Secret) []v1.EnvVar { - envs := make([]v1.EnvVar, 0) +func BuildSecretEnvs(secret *corev1.Secret) []corev1.EnvVar { + envs := make([]corev1.EnvVar, 0) if token, ok := secret.Data[HFTokenKey]; ok { - envs = append(envs, []v1.EnvVar{ + envs = append(envs, []corev1.EnvVar{ { Name: HFTokenKey, Value: string(token), diff --git a/pkg/credentials/https/https_secret.go b/pkg/credentials/https/https_secret.go index a522c4f10b2..1f36a6085f5 100644 --- a/pkg/credentials/https/https_secret.go +++ b/pkg/credentials/https/https_secret.go @@ -17,7 +17,7 @@ limitations under the License. package https import ( - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" ) // Create constants -- baseURI @@ -33,8 +33,8 @@ var ( ) // Can be used for http and https uris -func BuildSecretEnvs(secret *v1.Secret) []v1.EnvVar { - envs := []v1.EnvVar{} +func BuildSecretEnvs(secret *corev1.Secret) []corev1.EnvVar { + envs := []corev1.EnvVar{} uriHost, ok := secret.Data[HTTPSHost] if !ok { @@ -47,7 +47,7 @@ func BuildSecretEnvs(secret *v1.Secret) []v1.EnvVar { return envs } - envs = append(envs, v1.EnvVar{ + envs = append(envs, corev1.EnvVar{ Name: string(uriHost) + HeadersSuffix, Value: string(headers), }) diff --git a/pkg/credentials/https/https_secret_test.go b/pkg/credentials/https/https_secret_test.go index 24087544ac1..63302a184f0 100644 --- a/pkg/credentials/https/https_secret_test.go +++ b/pkg/credentials/https/https_secret_test.go @@ -17,10 +17,11 @@ limitations under the License. package https import ( + "testing" + "github.com/google/go-cmp/cmp" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "testing" ) var ( @@ -33,36 +34,36 @@ var ( func TestHTTPSSecret(t *testing.T) { scenarios := map[string]struct { - secret *v1.Secret - expected []v1.EnvVar + secret *corev1.Secret + expected []corev1.EnvVar }{ "noUriHost": { - secret: &v1.Secret{ + secret: &corev1.Secret{ Data: map[string][]byte{ header1: []byte(headerValue1), header2: []byte(headerValue2), }, }, - expected: []v1.EnvVar{}, + expected: []corev1.EnvVar{}, }, "noHeaders": { - secret: &v1.Secret{ + secret: &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{}, Data: map[string][]byte{ HTTPSHost: []byte(uriHost), }, }, - expected: []v1.EnvVar{}, + expected: []corev1.EnvVar{}, }, "secretEnvs": { - secret: &v1.Secret{ + secret: &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{}, Data: map[string][]byte{ HTTPSHost: []byte(uriHost), HEADERS: []byte(`{` + NEWLINE + header1 + ColonSeparator + headerValue1 + NEWLINE + header2 + ColonSeparator + headerValue2 + NEWLINE + `}`), }, }, - expected: []v1.EnvVar{ + expected: []corev1.EnvVar{ { Name: uriHost + HeadersSuffix, Value: `{` + NEWLINE + header1 + ColonSeparator + headerValue1 + NEWLINE + header2 + ColonSeparator + headerValue2 + NEWLINE + `}`, diff --git a/pkg/credentials/s3/s3_secret.go b/pkg/credentials/s3/s3_secret.go index 15d8869dc17..b54a4d8505d 100644 --- a/pkg/credentials/s3/s3_secret.go +++ b/pkg/credentials/s3/s3_secret.go @@ -17,8 +17,9 @@ limitations under the License. package s3 import ( + corev1 "k8s.io/api/core/v1" + "github.com/kserve/kserve/pkg/constants" - v1 "k8s.io/api/core/v1" ) /* @@ -69,7 +70,7 @@ var ( InferenceServiceS3CABundleAnnotation = constants.KServeAPIGroupName + "/" + "s3-cabundle" ) -func BuildSecretEnvs(secret *v1.Secret, s3Config *S3Config) []v1.EnvVar { +func BuildSecretEnvs(secret *corev1.Secret, s3Config *S3Config) []corev1.EnvVar { s3SecretAccessKeyName := AWSSecretAccessKeyName s3AccessKeyIdName := AWSAccessKeyIdName if s3Config.S3AccessKeyIDName != "" { @@ -79,12 +80,12 @@ func BuildSecretEnvs(secret *v1.Secret, s3Config *S3Config) []v1.EnvVar { if s3Config.S3SecretAccessKeyName != "" { s3SecretAccessKeyName = s3Config.S3SecretAccessKeyName } - envs := []v1.EnvVar{ + envs := []corev1.EnvVar{ { Name: AWSAccessKeyId, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: secret.Name, }, Key: s3AccessKeyIdName, @@ -93,9 +94,9 @@ func BuildSecretEnvs(secret *v1.Secret, s3Config *S3Config) []v1.EnvVar { }, { Name: AWSSecretAccessKey, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: secret.Name, }, Key: s3SecretAccessKeyName, diff --git a/pkg/credentials/s3/s3_secret_test.go b/pkg/credentials/s3/s3_secret_test.go index 7aad1177eb9..f63ee22e10c 100644 --- a/pkg/credentials/s3/s3_secret_test.go +++ b/pkg/credentials/s3/s3_secret_test.go @@ -17,20 +17,21 @@ limitations under the License. package s3 import ( + "testing" + "github.com/google/go-cmp/cmp" - "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "testing" ) func TestS3Secret(t *testing.T) { scenarios := map[string]struct { config S3Config - secret *v1.Secret - expected []v1.EnvVar + secret *corev1.Secret + expected []corev1.EnvVar }{ "S3SecretEnvs": { - secret: &v1.Secret{ + secret: &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: "s3-secret", Annotations: map[string]string{ @@ -38,12 +39,12 @@ func TestS3Secret(t *testing.T) { }, }, }, - expected: []v1.EnvVar{ + expected: []corev1.EnvVar{ { Name: AWSAccessKeyId, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "s3-secret", }, Key: AWSAccessKeyIdName, @@ -52,9 +53,9 @@ func TestS3Secret(t *testing.T) { }, { Name: AWSSecretAccessKey, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "s3-secret", }, Key: AWSSecretAccessKeyName, @@ -73,7 +74,7 @@ func TestS3Secret(t *testing.T) { }, "S3SecretHttpsOverrideEnvs": { - secret: &v1.Secret{ + secret: &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: "s3-secret", Annotations: map[string]string{ @@ -84,12 +85,12 @@ func TestS3Secret(t *testing.T) { }, }, - expected: []v1.EnvVar{ + expected: []corev1.EnvVar{ { Name: AWSAccessKeyId, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "s3-secret", }, Key: AWSAccessKeyIdName, @@ -98,9 +99,9 @@ func TestS3Secret(t *testing.T) { }, { Name: AWSSecretAccessKey, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "s3-secret", }, Key: AWSSecretAccessKeyName, @@ -127,7 +128,7 @@ func TestS3Secret(t *testing.T) { }, "S3SecretEnvsWithAnonymousCredentials": { - secret: &v1.Secret{ + secret: &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: "s3-secret", Annotations: map[string]string{ @@ -136,12 +137,12 @@ func TestS3Secret(t *testing.T) { }, }, }, - expected: []v1.EnvVar{ + expected: []corev1.EnvVar{ { Name: AWSAccessKeyId, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "s3-secret", }, Key: AWSAccessKeyIdName, @@ -150,9 +151,9 @@ func TestS3Secret(t *testing.T) { }, { Name: AWSSecretAccessKey, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "s3-secret", }, Key: AWSSecretAccessKeyName, @@ -181,17 +182,17 @@ func TestS3Secret(t *testing.T) { S3UseHttps: "0", S3Endpoint: "s3.aws.com", }, - secret: &v1.Secret{ + secret: &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: "s3-secret", }, }, - expected: []v1.EnvVar{ + expected: []corev1.EnvVar{ { Name: AWSAccessKeyId, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "s3-secret", }, Key: "test-keyId", @@ -200,9 +201,9 @@ func TestS3Secret(t *testing.T) { }, { Name: AWSSecretAccessKey, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "s3-secret", }, Key: "test-access-key", diff --git a/pkg/credentials/s3/s3_service_account.go b/pkg/credentials/s3/s3_service_account.go index 602173dc896..20d8dad7934 100644 --- a/pkg/credentials/s3/s3_service_account.go +++ b/pkg/credentials/s3/s3_service_account.go @@ -17,7 +17,7 @@ limitations under the License. package s3 import ( - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" ) /* @@ -26,8 +26,8 @@ AWS Cli: https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars. Boto: https://boto3.amazonaws.com/v1/documentation/api/latest/guide/configuration.html#using-environment-variables */ -func BuildServiceAccountEnvs(serviceAccount *v1.ServiceAccount, s3Config *S3Config) []v1.EnvVar { - envs := []v1.EnvVar{} +func BuildServiceAccountEnvs(serviceAccount *corev1.ServiceAccount, s3Config *S3Config) []corev1.EnvVar { + envs := []corev1.EnvVar{} envs = append(envs, BuildS3EnvVars(serviceAccount.Annotations, s3Config)...) diff --git a/pkg/credentials/s3/s3_service_account_test.go b/pkg/credentials/s3/s3_service_account_test.go index 3b85980b71d..5593c9f2dc5 100644 --- a/pkg/credentials/s3/s3_service_account_test.go +++ b/pkg/credentials/s3/s3_service_account_test.go @@ -20,27 +20,27 @@ import ( "testing" "github.com/google/go-cmp/cmp" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) func TestS3ServiceAccount(t *testing.T) { scenarios := map[string]struct { config S3Config - serviceAccount *v1.ServiceAccount - expected []v1.EnvVar + serviceAccount *corev1.ServiceAccount + expected []corev1.EnvVar }{ "NoConfig": { - serviceAccount: &v1.ServiceAccount{ + serviceAccount: &corev1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{ Name: "s3-service-account", }, }, - expected: []v1.EnvVar{}, + expected: []corev1.EnvVar{}, }, "S3Endpoint": { - serviceAccount: &v1.ServiceAccount{ + serviceAccount: &corev1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{ Name: "s3-service-account", Annotations: map[string]string{ @@ -48,7 +48,7 @@ func TestS3ServiceAccount(t *testing.T) { }, }, }, - expected: []v1.EnvVar{ + expected: []corev1.EnvVar{ { Name: S3Endpoint, Value: "s3.aws.com", @@ -61,7 +61,7 @@ func TestS3ServiceAccount(t *testing.T) { }, "S3HttpsOverrideEnvs": { - serviceAccount: &v1.ServiceAccount{ + serviceAccount: &corev1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{ Name: "s3-service-account", Annotations: map[string]string{ @@ -71,7 +71,7 @@ func TestS3ServiceAccount(t *testing.T) { }, }, }, - expected: []v1.EnvVar{ + expected: []corev1.EnvVar{ { Name: S3UseHttps, Value: "0", @@ -92,7 +92,7 @@ func TestS3ServiceAccount(t *testing.T) { }, "S3EnvsWithAnonymousCredentials": { - serviceAccount: &v1.ServiceAccount{ + serviceAccount: &corev1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{ Name: "s3-service-account", Annotations: map[string]string{ @@ -101,7 +101,7 @@ func TestS3ServiceAccount(t *testing.T) { }, }, }, - expected: []v1.EnvVar{ + expected: []corev1.EnvVar{ { Name: S3Endpoint, Value: "s3.aws.com", @@ -123,12 +123,12 @@ func TestS3ServiceAccount(t *testing.T) { S3Endpoint: "s3.aws.com", S3UseAnonymousCredential: "true", }, - serviceAccount: &v1.ServiceAccount{ + serviceAccount: &corev1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{ Name: "s3-service-account", }, }, - expected: []v1.EnvVar{ + expected: []corev1.EnvVar{ { Name: S3UseHttps, Value: "0", diff --git a/pkg/credentials/s3/utils.go b/pkg/credentials/s3/utils.go index 45850e5a821..2dd16be86f6 100644 --- a/pkg/credentials/s3/utils.go +++ b/pkg/credentials/s3/utils.go @@ -16,10 +16,10 @@ limitations under the License. package s3 -import v1 "k8s.io/api/core/v1" +import corev1 "k8s.io/api/core/v1" -func BuildS3EnvVars(annotations map[string]string, s3Config *S3Config) []v1.EnvVar { - envs := []v1.EnvVar{} +func BuildS3EnvVars(annotations map[string]string, s3Config *S3Config) []corev1.EnvVar { + envs := []corev1.EnvVar{} if s3Endpoint, ok := annotations[InferenceServiceS3SecretEndpointAnnotation]; ok { s3EndpointUrl := "https://" + s3Endpoint @@ -27,16 +27,16 @@ func BuildS3EnvVars(annotations map[string]string, s3Config *S3Config) []v1.EnvV if s3UseHttps == "0" { s3EndpointUrl = "http://" + annotations[InferenceServiceS3SecretEndpointAnnotation] } - envs = append(envs, v1.EnvVar{ + envs = append(envs, corev1.EnvVar{ Name: S3UseHttps, Value: s3UseHttps, }) } - envs = append(envs, v1.EnvVar{ + envs = append(envs, corev1.EnvVar{ Name: S3Endpoint, Value: s3Endpoint, }) - envs = append(envs, v1.EnvVar{ + envs = append(envs, corev1.EnvVar{ Name: AWSEndpointUrl, Value: s3EndpointUrl, }) @@ -44,16 +44,16 @@ func BuildS3EnvVars(annotations map[string]string, s3Config *S3Config) []v1.EnvV s3EndpointUrl := "https://" + s3Config.S3Endpoint if s3Config.S3UseHttps == "0" { s3EndpointUrl = "http://" + s3Config.S3Endpoint - envs = append(envs, v1.EnvVar{ + envs = append(envs, corev1.EnvVar{ Name: S3UseHttps, Value: s3Config.S3UseHttps, }) } - envs = append(envs, v1.EnvVar{ + envs = append(envs, corev1.EnvVar{ Name: S3Endpoint, Value: s3Config.S3Endpoint, }) - envs = append(envs, v1.EnvVar{ + envs = append(envs, corev1.EnvVar{ Name: AWSEndpointUrl, Value: s3EndpointUrl, }) @@ -65,7 +65,7 @@ func BuildS3EnvVars(annotations map[string]string, s3Config *S3Config) []v1.EnvV verifySsl = s3Config.S3VerifySSL } if verifySsl != "" { - envs = append(envs, v1.EnvVar{ + envs = append(envs, corev1.EnvVar{ Name: S3VerifySSL, Value: verifySsl, }) @@ -76,7 +76,7 @@ func BuildS3EnvVars(annotations map[string]string, s3Config *S3Config) []v1.EnvV useAnonymousCredential = s3Config.S3UseAnonymousCredential } if useAnonymousCredential != "" { - envs = append(envs, v1.EnvVar{ + envs = append(envs, corev1.EnvVar{ Name: AWSAnonymousCredential, Value: useAnonymousCredential, }) @@ -87,7 +87,7 @@ func BuildS3EnvVars(annotations map[string]string, s3Config *S3Config) []v1.EnvV s3Region = s3Config.S3Region } if s3Region != "" { - envs = append(envs, v1.EnvVar{ + envs = append(envs, corev1.EnvVar{ Name: AWSRegion, Value: s3Region, }) @@ -98,7 +98,7 @@ func BuildS3EnvVars(annotations map[string]string, s3Config *S3Config) []v1.EnvV useVirtualBucket = s3Config.S3UseVirtualBucket } if useVirtualBucket != "" { - envs = append(envs, v1.EnvVar{ + envs = append(envs, corev1.EnvVar{ Name: S3UseVirtualBucket, Value: useVirtualBucket, }) @@ -109,7 +109,7 @@ func BuildS3EnvVars(annotations map[string]string, s3Config *S3Config) []v1.EnvV useAccelerate = s3Config.S3UseAccelerate } if useAccelerate != "" { - envs = append(envs, v1.EnvVar{ + envs = append(envs, corev1.EnvVar{ Name: S3UseAccelerate, Value: useAccelerate, }) @@ -120,7 +120,7 @@ func BuildS3EnvVars(annotations map[string]string, s3Config *S3Config) []v1.EnvV customCABundle = s3Config.S3CABundle } if customCABundle != "" { - envs = append(envs, v1.EnvVar{ + envs = append(envs, corev1.EnvVar{ Name: AWSCABundle, Value: customCABundle, }) @@ -131,7 +131,7 @@ func BuildS3EnvVars(annotations map[string]string, s3Config *S3Config) []v1.EnvV customCABundleConfigMap = s3Config.S3CABundleConfigMap } if customCABundleConfigMap != "" { - envs = append(envs, v1.EnvVar{ + envs = append(envs, corev1.EnvVar{ Name: AWSCABundleConfigMap, Value: customCABundleConfigMap, }) diff --git a/pkg/credentials/s3/utils_test.go b/pkg/credentials/s3/utils_test.go index 135f3ccf6d4..ab6b6dd15f3 100644 --- a/pkg/credentials/s3/utils_test.go +++ b/pkg/credentials/s3/utils_test.go @@ -20,20 +20,20 @@ import ( "testing" "github.com/google/go-cmp/cmp" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" ) func TestBuildS3EnvVars(t *testing.T) { scenarios := map[string]struct { config S3Config annotations map[string]string - expected []v1.EnvVar + expected []corev1.EnvVar }{ "S3Endpoint": { annotations: map[string]string{ InferenceServiceS3SecretEndpointAnnotation: "s3.aws.com", }, - expected: []v1.EnvVar{ + expected: []corev1.EnvVar{ { Name: S3Endpoint, Value: "s3.aws.com", @@ -56,7 +56,7 @@ func TestBuildS3EnvVars(t *testing.T) { InferenceServiceS3CABundleAnnotation: "value", InferenceServiceS3CABundleConfigMapAnnotation: "value", }, - expected: []v1.EnvVar{ + expected: []corev1.EnvVar{ { Name: S3UseHttps, Value: "0", diff --git a/pkg/credentials/service_account_credentials.go b/pkg/credentials/service_account_credentials.go index 152820e4c25..883d5419fa0 100644 --- a/pkg/credentials/service_account_credentials.go +++ b/pkg/credentials/service_account_credentials.go @@ -23,7 +23,7 @@ import ( "fmt" "strings" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" apierr "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" @@ -71,7 +71,7 @@ type CredentialBuilder struct { var log = logf.Log.WithName("CredentialBuilder") -func NewCredentialBuilder(client client.Client, clientset kubernetes.Interface, config *v1.ConfigMap) *CredentialBuilder { +func NewCredentialBuilder(client client.Client, clientset kubernetes.Interface, config *corev1.ConfigMap) *CredentialBuilder { credentialConfig := CredentialConfig{} if credential, ok := config.Data[CredentialConfigKeyName]; ok { err := json.Unmarshal([]byte(credential), &credentialConfig) @@ -88,7 +88,8 @@ func NewCredentialBuilder(client client.Client, clientset kubernetes.Interface, } func (c *CredentialBuilder) CreateStorageSpecSecretEnvs(namespace string, annotations map[string]string, storageKey string, - overrideParams map[string]string, container *v1.Container) error { + overrideParams map[string]string, container *corev1.Container, +) error { stype := overrideParams["type"] bucket := overrideParams["bucket"] @@ -142,7 +143,7 @@ func (c *CredentialBuilder) CreateStorageSpecSecretEnvs(namespace string, annota bucket = storageDataJson["bucket"] } if cabundle_configmap, ok := storageDataJson["cabundle_configmap"]; ok { - container.Env = append(container.Env, v1.EnvVar{ + container.Env = append(container.Env, corev1.EnvVar{ Name: s3.AWSCABundleConfigMap, Value: cabundle_configmap, }) @@ -150,11 +151,11 @@ func (c *CredentialBuilder) CreateStorageSpecSecretEnvs(namespace string, annota } // Pass storage config json as SecretKeyRef env var - container.Env = append(container.Env, v1.EnvVar{ + container.Env = append(container.Env, corev1.EnvVar{ Name: StorageConfigEnvKey, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: storageSecretName, }, Key: storageKey, @@ -184,7 +185,7 @@ func (c *CredentialBuilder) CreateStorageSpecSecretEnvs(namespace string, annota // Provide override secret values if parameters are provided if len(overrideParams) != 0 { if overrideParamsJSON, err := json.Marshal(overrideParams); err == nil { - container.Env = append(container.Env, v1.EnvVar{ + container.Env = append(container.Env, corev1.EnvVar{ Name: StorageOverrideConfigEnvKey, Value: string(overrideParamsJSON), }) @@ -195,7 +196,8 @@ func (c *CredentialBuilder) CreateStorageSpecSecretEnvs(namespace string, annota } func (c *CredentialBuilder) CreateSecretVolumeAndEnv(namespace string, annotations map[string]string, serviceAccountName string, - container *v1.Container, volumes *[]v1.Volume) error { + container *corev1.Container, volumes *[]corev1.Volume, +) error { if serviceAccountName == "" { serviceAccountName = "default" } @@ -238,7 +240,8 @@ func (c *CredentialBuilder) CreateSecretVolumeAndEnv(namespace string, annotatio } func (c *CredentialBuilder) mountSecretCredential(secretName string, namespace string, - container *v1.Container, volumes *[]v1.Volume) error { + container *corev1.Container, volumes *[]corev1.Volume, +) error { secret, err := c.clientset.CoreV1().Secrets(namespace).Get(context.TODO(), secretName, metav1.GetOptions{}) if err != nil { log.Error(err, "Failed to find secret", "SecretName", secretName) @@ -265,10 +268,9 @@ func (c *CredentialBuilder) mountSecretCredential(secretName string, namespace s log.Info("Setting secret volume for gcs", "GCSSecret", secret.Name) volume, volumeMount := gcs.BuildSecretVolume(secret) *volumes = utils.AppendVolumeIfNotExists(*volumes, volume) - container.VolumeMounts = - append(container.VolumeMounts, volumeMount) + container.VolumeMounts = append(container.VolumeMounts, volumeMount) container.Env = append(container.Env, - v1.EnvVar{ + corev1.EnvVar{ Name: gcs.GCSCredentialEnvKey, Value: gcs.GCSCredentialVolumeMountPath + gcsCredentialFileName, }) diff --git a/pkg/credentials/service_account_credentials_test.go b/pkg/credentials/service_account_credentials_test.go index 2826240273d..a884c961cc2 100644 --- a/pkg/credentials/service_account_credentials_test.go +++ b/pkg/credentials/service_account_credentials_test.go @@ -29,12 +29,12 @@ import ( "github.com/google/go-cmp/cmp" "github.com/onsi/gomega" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" knservingv1 "knative.dev/serving/pkg/apis/serving/v1" ) -var configMap = &v1.ConfigMap{ +var configMap = &corev1.ConfigMap{ Data: map[string]string{ "credentials": `{ "storageSecretNameAnnotation": "serving.kserve.io/storageSecretName", @@ -57,19 +57,19 @@ var configMap = &v1.ConfigMap{ func TestS3CredentialBuilder(t *testing.T) { g := gomega.NewGomegaWithT(t) - existingServiceAccount := &v1.ServiceAccount{ + existingServiceAccount := &corev1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{ Name: "default", Namespace: "default", }, - Secrets: []v1.ObjectReference{ + Secrets: []corev1.ObjectReference{ { Name: "s3-secret", Namespace: "default", }, }, } - existingS3Secret := &v1.Secret{ + existingS3Secret := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: "s3-secret", Namespace: "default", @@ -83,7 +83,7 @@ func TestS3CredentialBuilder(t *testing.T) { }, } scenarios := map[string]struct { - serviceAccount *v1.ServiceAccount + serviceAccount *corev1.ServiceAccount inputConfiguration *knservingv1.Configuration expectedConfiguration *knservingv1.Configuration shouldFail bool @@ -94,8 +94,8 @@ func TestS3CredentialBuilder(t *testing.T) { Spec: knservingv1.ConfigurationSpec{ Template: knservingv1.RevisionTemplateSpec{ Spec: knservingv1.RevisionSpec{ - PodSpec: v1.PodSpec{ - Containers: []v1.Container{ + PodSpec: corev1.PodSpec{ + Containers: []corev1.Container{ {}, }, }, @@ -107,15 +107,15 @@ func TestS3CredentialBuilder(t *testing.T) { Spec: knservingv1.ConfigurationSpec{ Template: knservingv1.RevisionTemplateSpec{ Spec: knservingv1.RevisionSpec{ - PodSpec: v1.PodSpec{ - Containers: []v1.Container{ + PodSpec: corev1.PodSpec{ + Containers: []corev1.Container{ { - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: s3.AWSAccessKeyId, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "s3-secret", }, Key: "awsAccessKeyID", @@ -124,9 +124,9 @@ func TestS3CredentialBuilder(t *testing.T) { }, { Name: s3.AWSSecretAccessKey, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "s3-secret", }, Key: "awsSecretAccessKey", @@ -189,19 +189,18 @@ func TestS3CredentialBuilder(t *testing.T) { } g.Expect(c.Delete(context.TODO(), existingServiceAccount)).NotTo(gomega.HaveOccurred()) g.Expect(c.Delete(context.TODO(), existingS3Secret)).NotTo(gomega.HaveOccurred()) - } } func TestS3CredentialBuilderWithStorageSecret(t *testing.T) { g := gomega.NewGomegaWithT(t) - existingServiceAccount := &v1.ServiceAccount{ + existingServiceAccount := &corev1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{ Name: "default", Namespace: "default", }, } - existingS3Secret := &v1.Secret{ + existingS3Secret := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: "s3-secret", Namespace: "default", @@ -224,8 +223,8 @@ func TestS3CredentialBuilderWithStorageSecret(t *testing.T) { Spec: knservingv1.ConfigurationSpec{ Template: knservingv1.RevisionTemplateSpec{ Spec: knservingv1.RevisionSpec{ - PodSpec: v1.PodSpec{ - Containers: []v1.Container{ + PodSpec: corev1.PodSpec{ + Containers: []corev1.Container{ {}, }, }, @@ -237,15 +236,15 @@ func TestS3CredentialBuilderWithStorageSecret(t *testing.T) { Spec: knservingv1.ConfigurationSpec{ Template: knservingv1.RevisionTemplateSpec{ Spec: knservingv1.RevisionSpec{ - PodSpec: v1.PodSpec{ - Containers: []v1.Container{ + PodSpec: corev1.PodSpec{ + Containers: []corev1.Container{ { - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: s3.AWSAccessKeyId, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "s3-secret", }, Key: "awsAccessKeyID", @@ -254,9 +253,9 @@ func TestS3CredentialBuilderWithStorageSecret(t *testing.T) { }, { Name: s3.AWSSecretAccessKey, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "s3-secret", }, Key: "awsSecretAccessKey", @@ -321,13 +320,12 @@ func TestS3CredentialBuilderWithStorageSecret(t *testing.T) { } g.Expect(c.Delete(context.TODO(), existingServiceAccount)).NotTo(gomega.HaveOccurred()) g.Expect(c.Delete(context.TODO(), existingS3Secret)).NotTo(gomega.HaveOccurred()) - } } func TestS3ServiceAccountCredentialBuilder(t *testing.T) { g := gomega.NewGomegaWithT(t) - existingServiceAccount := &v1.ServiceAccount{ + existingServiceAccount := &corev1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{ Name: "default", Namespace: "default", @@ -339,7 +337,7 @@ func TestS3ServiceAccountCredentialBuilder(t *testing.T) { }, } scenarios := map[string]struct { - serviceAccount *v1.ServiceAccount + serviceAccount *corev1.ServiceAccount inputConfiguration *knservingv1.Configuration expectedConfiguration *knservingv1.Configuration shouldFail bool @@ -350,8 +348,8 @@ func TestS3ServiceAccountCredentialBuilder(t *testing.T) { Spec: knservingv1.ConfigurationSpec{ Template: knservingv1.RevisionTemplateSpec{ Spec: knservingv1.RevisionSpec{ - PodSpec: v1.PodSpec{ - Containers: []v1.Container{ + PodSpec: corev1.PodSpec{ + Containers: []corev1.Container{ {}, }, }, @@ -363,10 +361,10 @@ func TestS3ServiceAccountCredentialBuilder(t *testing.T) { Spec: knservingv1.ConfigurationSpec{ Template: knservingv1.RevisionTemplateSpec{ Spec: knservingv1.RevisionSpec{ - PodSpec: v1.PodSpec{ - Containers: []v1.Container{ + PodSpec: corev1.PodSpec{ + Containers: []corev1.Container{ { - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: s3.S3Endpoint, Value: "s3.aws.com", @@ -420,25 +418,24 @@ func TestS3ServiceAccountCredentialBuilder(t *testing.T) { } } g.Expect(c.Delete(context.TODO(), existingServiceAccount)).NotTo(gomega.HaveOccurred()) - } } func TestGCSCredentialBuilder(t *testing.T) { g := gomega.NewGomegaWithT(t) - existingServiceAccount := &v1.ServiceAccount{ + existingServiceAccount := &corev1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{ Name: "default", Namespace: "default", }, - Secrets: []v1.ObjectReference{ + Secrets: []corev1.ObjectReference{ { Name: "user-gcp-sa", Namespace: "default", }, }, } - existingGCSSecret := &v1.Secret{ + existingGCSSecret := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: "user-gcp-sa", Namespace: "default", @@ -448,7 +445,7 @@ func TestGCSCredentialBuilder(t *testing.T) { }, } scenarios := map[string]struct { - serviceAccount *v1.ServiceAccount + serviceAccount *corev1.ServiceAccount inputConfiguration *knservingv1.Configuration expectedConfiguration *knservingv1.Configuration shouldFail bool @@ -459,8 +456,8 @@ func TestGCSCredentialBuilder(t *testing.T) { Spec: knservingv1.ConfigurationSpec{ Template: knservingv1.RevisionTemplateSpec{ Spec: knservingv1.RevisionSpec{ - PodSpec: v1.PodSpec{ - Containers: []v1.Container{ + PodSpec: corev1.PodSpec{ + Containers: []corev1.Container{ {}, }, }, @@ -472,17 +469,17 @@ func TestGCSCredentialBuilder(t *testing.T) { Spec: knservingv1.ConfigurationSpec{ Template: knservingv1.RevisionTemplateSpec{ Spec: knservingv1.RevisionSpec{ - PodSpec: v1.PodSpec{ - Containers: []v1.Container{ + PodSpec: corev1.PodSpec{ + Containers: []corev1.Container{ { - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: gcs.GCSCredentialVolumeName, ReadOnly: true, MountPath: gcs.GCSCredentialVolumeMountPath, }, }, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: gcs.GCSCredentialEnvKey, Value: gcs.GCSCredentialVolumeMountPath + "gcloud-application-credentials.json", @@ -490,11 +487,11 @@ func TestGCSCredentialBuilder(t *testing.T) { }, }, }, - Volumes: []v1.Volume{ + Volumes: []corev1.Volume{ { Name: gcs.GCSCredentialVolumeName, - VolumeSource: v1.VolumeSource{ - Secret: &v1.SecretVolumeSource{ + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ SecretName: "user-gcp-sa", }, }, @@ -532,25 +529,24 @@ func TestGCSCredentialBuilder(t *testing.T) { } g.Expect(c.Delete(context.TODO(), existingServiceAccount)).NotTo(gomega.HaveOccurred()) g.Expect(c.Delete(context.TODO(), existingGCSSecret)).NotTo(gomega.HaveOccurred()) - } } func TestLegacyAzureCredentialBuilder(t *testing.T) { g := gomega.NewGomegaWithT(t) - customOnlyServiceAccount := &v1.ServiceAccount{ + customOnlyServiceAccount := &corev1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{ Name: "custom-sa", Namespace: "default", }, - Secrets: []v1.ObjectReference{ + Secrets: []corev1.ObjectReference{ { Name: "az-custom-secret", Namespace: "default", }, }, } - customAzureSecret := &v1.Secret{ + customAzureSecret := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: "az-custom-secret", Namespace: "default", @@ -564,7 +560,7 @@ func TestLegacyAzureCredentialBuilder(t *testing.T) { } scenarios := map[string]struct { - serviceAccount *v1.ServiceAccount + serviceAccount *corev1.ServiceAccount inputConfiguration *knservingv1.Configuration expectedConfiguration *knservingv1.Configuration shouldFail bool @@ -575,8 +571,8 @@ func TestLegacyAzureCredentialBuilder(t *testing.T) { Spec: knservingv1.ConfigurationSpec{ Template: knservingv1.RevisionTemplateSpec{ Spec: knservingv1.RevisionSpec{ - PodSpec: v1.PodSpec{ - Containers: []v1.Container{ + PodSpec: corev1.PodSpec{ + Containers: []corev1.Container{ {}, }, }, @@ -588,15 +584,15 @@ func TestLegacyAzureCredentialBuilder(t *testing.T) { Spec: knservingv1.ConfigurationSpec{ Template: knservingv1.RevisionTemplateSpec{ Spec: knservingv1.RevisionSpec{ - PodSpec: v1.PodSpec{ - Containers: []v1.Container{ + PodSpec: corev1.PodSpec{ + Containers: []corev1.Container{ { - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: azure.AzureSubscriptionId, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "az-custom-secret", }, Key: azure.LegacyAzureSubscriptionId, @@ -605,9 +601,9 @@ func TestLegacyAzureCredentialBuilder(t *testing.T) { }, { Name: azure.AzureTenantId, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "az-custom-secret", }, Key: azure.LegacyAzureTenantId, @@ -616,9 +612,9 @@ func TestLegacyAzureCredentialBuilder(t *testing.T) { }, { Name: azure.AzureClientId, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "az-custom-secret", }, Key: azure.LegacyAzureClientId, @@ -627,9 +623,9 @@ func TestLegacyAzureCredentialBuilder(t *testing.T) { }, { Name: azure.AzureClientSecret, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "az-custom-secret", }, Key: azure.LegacyAzureClientSecret, @@ -653,7 +649,6 @@ func TestLegacyAzureCredentialBuilder(t *testing.T) { builder := NewCredentialBuilder(c, clientset, configMap) for name, scenario := range scenarios { - err := builder.CreateSecretVolumeAndEnv(scenario.serviceAccount.Namespace, nil, scenario.serviceAccount.Name, &scenario.inputConfiguration.Spec.Template.Spec.Containers[0], &scenario.inputConfiguration.Spec.Template.Spec.Volumes, @@ -678,19 +673,19 @@ func TestLegacyAzureCredentialBuilder(t *testing.T) { func TestHdfsCredentialBuilder(t *testing.T) { g := gomega.NewGomegaWithT(t) - customOnlyServiceAccount := &v1.ServiceAccount{ + customOnlyServiceAccount := &corev1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{ Name: "custom-sa", Namespace: "default", }, - Secrets: []v1.ObjectReference{ + Secrets: []corev1.ObjectReference{ { Name: "hdfs-custom-secret", Namespace: "default", }, }, } - customHdfsSecret := &v1.Secret{ + customHdfsSecret := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: "hdfs-custom-secret", Namespace: "default", @@ -704,7 +699,7 @@ func TestHdfsCredentialBuilder(t *testing.T) { } scenarios := map[string]struct { - serviceAccount *v1.ServiceAccount + serviceAccount *corev1.ServiceAccount inputConfiguration *knservingv1.Configuration expectedConfiguration *knservingv1.Configuration shouldFail bool @@ -715,8 +710,8 @@ func TestHdfsCredentialBuilder(t *testing.T) { Spec: knservingv1.ConfigurationSpec{ Template: knservingv1.RevisionTemplateSpec{ Spec: knservingv1.RevisionSpec{ - PodSpec: v1.PodSpec{ - Containers: []v1.Container{ + PodSpec: corev1.PodSpec{ + Containers: []corev1.Container{ {}, }, }, @@ -728,10 +723,10 @@ func TestHdfsCredentialBuilder(t *testing.T) { Spec: knservingv1.ConfigurationSpec{ Template: knservingv1.RevisionTemplateSpec{ Spec: knservingv1.RevisionSpec{ - PodSpec: v1.PodSpec{ - Containers: []v1.Container{ + PodSpec: corev1.PodSpec{ + Containers: []corev1.Container{ { - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: hdfs.HdfsVolumeName, ReadOnly: true, @@ -740,11 +735,11 @@ func TestHdfsCredentialBuilder(t *testing.T) { }, }, }, - Volumes: []v1.Volume{ + Volumes: []corev1.Volume{ { Name: hdfs.HdfsVolumeName, - VolumeSource: v1.VolumeSource{ - Secret: &v1.SecretVolumeSource{ + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ SecretName: "hdfs-custom-secret", }, }, @@ -764,7 +759,6 @@ func TestHdfsCredentialBuilder(t *testing.T) { builder := NewCredentialBuilder(c, clientset, configMap) for name, scenario := range scenarios { - err := builder.CreateSecretVolumeAndEnv(scenario.serviceAccount.Namespace, nil, scenario.serviceAccount.Name, &scenario.inputConfiguration.Spec.Template.Spec.Containers[0], &scenario.inputConfiguration.Spec.Template.Spec.Volumes, @@ -789,19 +783,19 @@ func TestHdfsCredentialBuilder(t *testing.T) { func TestAzureCredentialBuilder(t *testing.T) { g := gomega.NewGomegaWithT(t) - customOnlyServiceAccount := &v1.ServiceAccount{ + customOnlyServiceAccount := &corev1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{ Name: "custom-sa", Namespace: "default", }, - Secrets: []v1.ObjectReference{ + Secrets: []corev1.ObjectReference{ { Name: "az-custom-secret", Namespace: "default", }, }, } - customAzureSecret := &v1.Secret{ + customAzureSecret := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: "az-custom-secret", Namespace: "default", @@ -816,7 +810,7 @@ func TestAzureCredentialBuilder(t *testing.T) { } scenarios := map[string]struct { - serviceAccount *v1.ServiceAccount + serviceAccount *corev1.ServiceAccount inputConfiguration *knservingv1.Configuration expectedConfiguration *knservingv1.Configuration shouldFail bool @@ -827,8 +821,8 @@ func TestAzureCredentialBuilder(t *testing.T) { Spec: knservingv1.ConfigurationSpec{ Template: knservingv1.RevisionTemplateSpec{ Spec: knservingv1.RevisionSpec{ - PodSpec: v1.PodSpec{ - Containers: []v1.Container{ + PodSpec: corev1.PodSpec{ + Containers: []corev1.Container{ {}, }, }, @@ -840,15 +834,15 @@ func TestAzureCredentialBuilder(t *testing.T) { Spec: knservingv1.ConfigurationSpec{ Template: knservingv1.RevisionTemplateSpec{ Spec: knservingv1.RevisionSpec{ - PodSpec: v1.PodSpec{ - Containers: []v1.Container{ + PodSpec: corev1.PodSpec{ + Containers: []corev1.Container{ { - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: azure.AzureSubscriptionId, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "az-custom-secret", }, Key: azure.AzureSubscriptionId, @@ -857,9 +851,9 @@ func TestAzureCredentialBuilder(t *testing.T) { }, { Name: azure.AzureTenantId, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "az-custom-secret", }, Key: azure.AzureTenantId, @@ -868,9 +862,9 @@ func TestAzureCredentialBuilder(t *testing.T) { }, { Name: azure.AzureClientId, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "az-custom-secret", }, Key: azure.AzureClientId, @@ -879,9 +873,9 @@ func TestAzureCredentialBuilder(t *testing.T) { }, { Name: azure.AzureClientSecret, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "az-custom-secret", }, Key: azure.AzureClientSecret, @@ -890,9 +884,9 @@ func TestAzureCredentialBuilder(t *testing.T) { }, { Name: azure.AzureStorageAccessKey, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "az-custom-secret", }, Key: azure.AzureStorageAccessKey, @@ -916,7 +910,6 @@ func TestAzureCredentialBuilder(t *testing.T) { builder := NewCredentialBuilder(c, clientset, configMap) for name, scenario := range scenarios { - err := builder.CreateSecretVolumeAndEnv(scenario.serviceAccount.Namespace, nil, scenario.serviceAccount.Name, &scenario.inputConfiguration.Spec.Template.Spec.Containers[0], &scenario.inputConfiguration.Spec.Template.Spec.Volumes, @@ -941,19 +934,19 @@ func TestAzureCredentialBuilder(t *testing.T) { func TestAzureStorageAccessKeyCredentialBuilder(t *testing.T) { g := gomega.NewGomegaWithT(t) - customOnlyServiceAccount := &v1.ServiceAccount{ + customOnlyServiceAccount := &corev1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{ Name: "custom-sa", Namespace: "default", }, - Secrets: []v1.ObjectReference{ + Secrets: []corev1.ObjectReference{ { Name: "az-custom-secret", Namespace: "default", }, }, } - customAzureSecret := &v1.Secret{ + customAzureSecret := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: "az-custom-secret", Namespace: "default", @@ -964,7 +957,7 @@ func TestAzureStorageAccessKeyCredentialBuilder(t *testing.T) { } scenarios := map[string]struct { - serviceAccount *v1.ServiceAccount + serviceAccount *corev1.ServiceAccount inputConfiguration *knservingv1.Configuration expectedConfiguration *knservingv1.Configuration shouldFail bool @@ -975,8 +968,8 @@ func TestAzureStorageAccessKeyCredentialBuilder(t *testing.T) { Spec: knservingv1.ConfigurationSpec{ Template: knservingv1.RevisionTemplateSpec{ Spec: knservingv1.RevisionSpec{ - PodSpec: v1.PodSpec{ - Containers: []v1.Container{ + PodSpec: corev1.PodSpec{ + Containers: []corev1.Container{ {}, }, }, @@ -988,15 +981,15 @@ func TestAzureStorageAccessKeyCredentialBuilder(t *testing.T) { Spec: knservingv1.ConfigurationSpec{ Template: knservingv1.RevisionTemplateSpec{ Spec: knservingv1.RevisionSpec{ - PodSpec: v1.PodSpec{ - Containers: []v1.Container{ + PodSpec: corev1.PodSpec{ + Containers: []corev1.Container{ { - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: azure.AzureStorageAccessKey, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "az-custom-secret", }, Key: azure.AzureStorageAccessKey, @@ -1020,7 +1013,6 @@ func TestAzureStorageAccessKeyCredentialBuilder(t *testing.T) { builder := NewCredentialBuilder(c, clientset, configMap) for name, scenario := range scenarios { - err := builder.CreateSecretVolumeAndEnv(scenario.serviceAccount.Namespace, nil, scenario.serviceAccount.Name, &scenario.inputConfiguration.Spec.Template.Spec.Containers[0], &scenario.inputConfiguration.Spec.Template.Spec.Volumes, @@ -1049,16 +1041,16 @@ func TestCredentialBuilder_CreateStorageSpecSecretEnvs(t *testing.T) { builder := NewCredentialBuilder(c, clientset, configMap) scenarios := map[string]struct { - secret *v1.Secret + secret *corev1.Secret storageKey string storageSecretName string overrideParams map[string]string - container *v1.Container + container *corev1.Container shouldFail bool matcher types.GomegaMatcher }{ "fail on storage secret name is empty": { - secret: &v1.Secret{ + secret: &corev1.Secret{ TypeMeta: metav1.TypeMeta{ Kind: "Secret", }, @@ -1071,12 +1063,12 @@ func TestCredentialBuilder_CreateStorageSpecSecretEnvs(t *testing.T) { storageKey: "", storageSecretName: "", overrideParams: make(map[string]string), - container: &v1.Container{}, + container: &corev1.Container{}, shouldFail: true, matcher: gomega.HaveOccurred(), }, "storage spec with empty override params": { - secret: &v1.Secret{ + secret: &corev1.Secret{ TypeMeta: metav1.TypeMeta{ Kind: "Secret", APIVersion: "v1", @@ -1090,7 +1082,7 @@ func TestCredentialBuilder_CreateStorageSpecSecretEnvs(t *testing.T) { storageKey: "minio", storageSecretName: "storage-secret", overrideParams: map[string]string{"type": "", "bucket": ""}, - container: &v1.Container{ + container: &corev1.Container{ Name: "init-container", Image: "kserve/init-container:latest", Args: []string{ @@ -1099,23 +1091,23 @@ func TestCredentialBuilder_CreateStorageSpecSecretEnvs(t *testing.T) { }, }, shouldFail: false, - matcher: gomega.Equal(&v1.Container{ + matcher: gomega.Equal(&corev1.Container{ Name: "init-container", Image: "kserve/init-container:latest", Args: []string{ "s3://test-bucket/models/", "/mnt/models/", }, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "STORAGE_CONFIG", Value: "", - ValueFrom: &v1.EnvVarSource{ + ValueFrom: &corev1.EnvVarSource{ FieldRef: nil, ResourceFieldRef: nil, ConfigMapKeyRef: nil, - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "storage-secret", }, Key: "minio", @@ -1131,7 +1123,7 @@ func TestCredentialBuilder_CreateStorageSpecSecretEnvs(t *testing.T) { }), }, "simple storage spec": { - secret: &v1.Secret{ + secret: &corev1.Secret{ TypeMeta: metav1.TypeMeta{ Kind: "Secret", APIVersion: "v1", @@ -1145,7 +1137,7 @@ func TestCredentialBuilder_CreateStorageSpecSecretEnvs(t *testing.T) { storageKey: "minio", storageSecretName: "storage-secret", overrideParams: map[string]string{"type": "s3", "bucket": "test-bucket"}, - container: &v1.Container{ + container: &corev1.Container{ Name: "init-container", Image: "kserve/init-container:latest", Args: []string{ @@ -1154,23 +1146,23 @@ func TestCredentialBuilder_CreateStorageSpecSecretEnvs(t *testing.T) { }, }, shouldFail: false, - matcher: gomega.Equal(&v1.Container{ + matcher: gomega.Equal(&corev1.Container{ Name: "init-container", Image: "kserve/init-container:latest", Args: []string{ "s3://test-bucket/models/", "/mnt/models/", }, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "STORAGE_CONFIG", Value: "", - ValueFrom: &v1.EnvVarSource{ + ValueFrom: &corev1.EnvVarSource{ FieldRef: nil, ResourceFieldRef: nil, ConfigMapKeyRef: nil, - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "storage-secret", }, Key: "minio", @@ -1186,7 +1178,7 @@ func TestCredentialBuilder_CreateStorageSpecSecretEnvs(t *testing.T) { }), }, "wrong storage key": { - secret: &v1.Secret{ + secret: &corev1.Secret{ TypeMeta: metav1.TypeMeta{ Kind: "Secret", APIVersion: "v1", @@ -1200,7 +1192,7 @@ func TestCredentialBuilder_CreateStorageSpecSecretEnvs(t *testing.T) { storageKey: "wrong-key", storageSecretName: "storage-secret", overrideParams: map[string]string{"type": "s3", "bucket": "test-bucket"}, - container: &v1.Container{ + container: &corev1.Container{ Name: "init-container", Image: "kserve/init-container:latest", Args: []string{ @@ -1212,7 +1204,7 @@ func TestCredentialBuilder_CreateStorageSpecSecretEnvs(t *testing.T) { matcher: gomega.HaveOccurred(), }, "default storage key": { - secret: &v1.Secret{ + secret: &corev1.Secret{ TypeMeta: metav1.TypeMeta{ Kind: "Secret", APIVersion: "v1", @@ -1226,7 +1218,7 @@ func TestCredentialBuilder_CreateStorageSpecSecretEnvs(t *testing.T) { storageKey: "", storageSecretName: "storage-secret", overrideParams: map[string]string{"type": "s3", "bucket": "test-bucket"}, - container: &v1.Container{ + container: &corev1.Container{ Name: "init-container", Image: "kserve/init-container:latest", Args: []string{ @@ -1235,23 +1227,23 @@ func TestCredentialBuilder_CreateStorageSpecSecretEnvs(t *testing.T) { }, }, shouldFail: false, - matcher: gomega.Equal(&v1.Container{ + matcher: gomega.Equal(&corev1.Container{ Name: "init-container", Image: "kserve/init-container:latest", Args: []string{ "s3://test-bucket/models/", "/mnt/models/", }, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "STORAGE_CONFIG", Value: "", - ValueFrom: &v1.EnvVarSource{ + ValueFrom: &corev1.EnvVarSource{ FieldRef: nil, ResourceFieldRef: nil, ConfigMapKeyRef: nil, - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "storage-secret", }, Key: "default_s3", @@ -1267,7 +1259,7 @@ func TestCredentialBuilder_CreateStorageSpecSecretEnvs(t *testing.T) { }), }, "default storage key with empty storage type": { - secret: &v1.Secret{ + secret: &corev1.Secret{ TypeMeta: metav1.TypeMeta{ Kind: "Secret", APIVersion: "v1", @@ -1281,7 +1273,7 @@ func TestCredentialBuilder_CreateStorageSpecSecretEnvs(t *testing.T) { storageKey: "", storageSecretName: "storage-secret", overrideParams: map[string]string{"type": "", "bucket": "test-bucket"}, - container: &v1.Container{ + container: &corev1.Container{ Name: "init-container", Image: "kserve/init-container:latest", Args: []string{ @@ -1290,23 +1282,23 @@ func TestCredentialBuilder_CreateStorageSpecSecretEnvs(t *testing.T) { }, }, shouldFail: false, - matcher: gomega.Equal(&v1.Container{ + matcher: gomega.Equal(&corev1.Container{ Name: "init-container", Image: "kserve/init-container:latest", Args: []string{ "s3://test-bucket/models/", "/mnt/models/", }, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "STORAGE_CONFIG", Value: "", - ValueFrom: &v1.EnvVarSource{ + ValueFrom: &corev1.EnvVarSource{ FieldRef: nil, ResourceFieldRef: nil, ConfigMapKeyRef: nil, - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "storage-secret", }, Key: "default", @@ -1322,7 +1314,7 @@ func TestCredentialBuilder_CreateStorageSpecSecretEnvs(t *testing.T) { }), }, "storage spec with uri scheme placeholder": { - secret: &v1.Secret{ + secret: &corev1.Secret{ TypeMeta: metav1.TypeMeta{ Kind: "Secret", APIVersion: "v1", @@ -1336,7 +1328,7 @@ func TestCredentialBuilder_CreateStorageSpecSecretEnvs(t *testing.T) { storageKey: "minio", storageSecretName: "storage-secret", overrideParams: map[string]string{"type": "s3", "bucket": "test-bucket"}, - container: &v1.Container{ + container: &corev1.Container{ Name: "init-container", Image: "kserve/init-container:latest", Args: []string{ @@ -1345,23 +1337,23 @@ func TestCredentialBuilder_CreateStorageSpecSecretEnvs(t *testing.T) { }, }, shouldFail: false, - matcher: gomega.Equal(&v1.Container{ + matcher: gomega.Equal(&corev1.Container{ Name: "init-container", Image: "kserve/init-container:latest", Args: []string{ "s3://test-bucket/models/example-model/", "/mnt/models/", }, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "STORAGE_CONFIG", Value: "", - ValueFrom: &v1.EnvVarSource{ + ValueFrom: &corev1.EnvVarSource{ FieldRef: nil, ResourceFieldRef: nil, ConfigMapKeyRef: nil, - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "storage-secret", }, Key: "minio", @@ -1377,7 +1369,7 @@ func TestCredentialBuilder_CreateStorageSpecSecretEnvs(t *testing.T) { }), }, "hdfs with uri scheme placeholder": { - secret: &v1.Secret{ + secret: &corev1.Secret{ TypeMeta: metav1.TypeMeta{ Kind: "Secret", APIVersion: "v1", @@ -1391,7 +1383,7 @@ func TestCredentialBuilder_CreateStorageSpecSecretEnvs(t *testing.T) { storageKey: "hdfs", storageSecretName: "storage-secret", overrideParams: map[string]string{"type": "hdfs", "bucket": ""}, - container: &v1.Container{ + container: &corev1.Container{ Name: "init-container", Image: "kserve/init-container:latest", Args: []string{ @@ -1400,23 +1392,23 @@ func TestCredentialBuilder_CreateStorageSpecSecretEnvs(t *testing.T) { }, }, shouldFail: false, - matcher: gomega.Equal(&v1.Container{ + matcher: gomega.Equal(&corev1.Container{ Name: "init-container", Image: "kserve/init-container:latest", Args: []string{ "hdfs://models/example-model/", "/mnt/models/", }, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "STORAGE_CONFIG", Value: "", - ValueFrom: &v1.EnvVarSource{ + ValueFrom: &corev1.EnvVarSource{ FieldRef: nil, ResourceFieldRef: nil, ConfigMapKeyRef: nil, - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "storage-secret", }, Key: "hdfs", @@ -1432,7 +1424,7 @@ func TestCredentialBuilder_CreateStorageSpecSecretEnvs(t *testing.T) { }), }, "unsupported storage type": { - secret: &v1.Secret{ + secret: &corev1.Secret{ TypeMeta: metav1.TypeMeta{ Kind: "Secret", APIVersion: "v1", @@ -1446,7 +1438,7 @@ func TestCredentialBuilder_CreateStorageSpecSecretEnvs(t *testing.T) { storageKey: "minio", storageSecretName: "storage-secret", overrideParams: map[string]string{"type": "", "bucket": "test-bucket"}, - container: &v1.Container{ + container: &corev1.Container{ Name: "init-container", Image: "kserve/init-container:latest", Args: []string{ @@ -1458,7 +1450,7 @@ func TestCredentialBuilder_CreateStorageSpecSecretEnvs(t *testing.T) { matcher: gomega.HaveOccurred(), }, "secret data with syntax error": { - secret: &v1.Secret{ + secret: &corev1.Secret{ TypeMeta: metav1.TypeMeta{ Kind: "Secret", APIVersion: "v1", @@ -1472,7 +1464,7 @@ func TestCredentialBuilder_CreateStorageSpecSecretEnvs(t *testing.T) { storageKey: "minio", storageSecretName: "storage-secret", overrideParams: map[string]string{"type": "", "bucket": "test-bucket"}, - container: &v1.Container{ + container: &corev1.Container{ Name: "init-container", Image: "kserve/init-container:latest", Args: []string{ @@ -1484,7 +1476,7 @@ func TestCredentialBuilder_CreateStorageSpecSecretEnvs(t *testing.T) { matcher: gomega.HaveOccurred(), }, "fail on storage type is empty": { - secret: &v1.Secret{ + secret: &corev1.Secret{ TypeMeta: metav1.TypeMeta{ Kind: "Secret", APIVersion: "v1", @@ -1498,7 +1490,7 @@ func TestCredentialBuilder_CreateStorageSpecSecretEnvs(t *testing.T) { storageKey: "minio", storageSecretName: "storage-secret", overrideParams: map[string]string{"type": "", "bucket": "test-bucket"}, - container: &v1.Container{ + container: &corev1.Container{ Name: "init-container", Image: "kserve/init-container:latest", Args: []string{ @@ -1510,7 +1502,7 @@ func TestCredentialBuilder_CreateStorageSpecSecretEnvs(t *testing.T) { matcher: gomega.HaveOccurred(), }, "fail on bucket is empty on s3 storage": { - secret: &v1.Secret{ + secret: &corev1.Secret{ TypeMeta: metav1.TypeMeta{ Kind: "Secret", APIVersion: "v1", @@ -1524,7 +1516,7 @@ func TestCredentialBuilder_CreateStorageSpecSecretEnvs(t *testing.T) { storageKey: "minio", storageSecretName: "storage-secret", overrideParams: map[string]string{"type": "s3", "bucket": ""}, - container: &v1.Container{ + container: &corev1.Container{ Name: "init-container", Image: "kserve/init-container:latest", Args: []string{ @@ -1543,7 +1535,7 @@ func TestCredentialBuilder_CreateStorageSpecSecretEnvs(t *testing.T) { } err := builder.CreateStorageSpecSecretEnvs(namespace, nil, tc.storageKey, tc.overrideParams, tc.container) if !tc.shouldFail { - g.Expect(err).Should(gomega.BeNil()) + g.Expect(err).ShouldNot(gomega.HaveOccurred()) g.Expect(tc.container).Should(tc.matcher) } else { g.Expect(err).To(tc.matcher) diff --git a/pkg/logger/dispatcher.go b/pkg/logger/dispatcher.go index 0516383382d..f22ab15119d 100644 --- a/pkg/logger/dispatcher.go +++ b/pkg/logger/dispatcher.go @@ -27,23 +27,19 @@ func StartDispatcher(nworkers int, logger *zap.SugaredLogger) { WorkerQueue = make(chan chan LogRequest, nworkers) // Now, create all of our workers. - for i := 0; i < nworkers; i++ { + for i := range nworkers { logger.Info("Starting worker ", i+1) worker := NewWorker(i+1, WorkerQueue, logger) worker.Start() } - // nolint: gosimple go func() { for { - select { - case work := <-WorkQueue: - go func() { - worker := <-WorkerQueue - - worker <- work - }() - } + work := <-WorkQueue + go func() { + worker := <-WorkerQueue + worker <- work + }() } }() } diff --git a/pkg/logger/handler.go b/pkg/logger/handler.go index 814a52da62e..050bf0b5da5 100644 --- a/pkg/logger/handler.go +++ b/pkg/logger/handler.go @@ -26,10 +26,11 @@ import ( "github.com/go-logr/logr" guuid "github.com/google/uuid" - "github.com/kserve/kserve/pkg/apis/serving/v1beta1" "knative.dev/pkg/network" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/log/zap" + + "github.com/kserve/kserve/pkg/apis/serving/v1beta1" ) // loggingResponseWriter is a wrapper around an http.ResponseWriter that logs the response body @@ -88,7 +89,8 @@ type LoggerHandler struct { func New(logUrl *url.URL, sourceUri *url.URL, logMode v1beta1.LoggerType, inferenceService string, namespace string, endpoint string, component string, next http.Handler, metadataHeaders []string, - certName string, tlsSkipVerify bool) http.Handler { + certName string, tlsSkipVerify bool, +) http.Handler { logf.SetLogger(zap.New()) return &LoggerHandler{ log: logf.Log.WithName("Logger"), diff --git a/pkg/logger/handler_test.go b/pkg/logger/handler_test.go index bcfc82f242d..54f9be0e608 100644 --- a/pkg/logger/handler_test.go +++ b/pkg/logger/handler_test.go @@ -26,15 +26,15 @@ import ( "net/url" "testing" - "github.com/kserve/kserve/pkg/apis/serving/v1beta1" "github.com/onsi/gomega" pkglogging "knative.dev/pkg/logging" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/log/zap" + + "github.com/kserve/kserve/pkg/apis/serving/v1beta1" ) func TestLogger(t *testing.T) { - g := gomega.NewGomegaWithT(t) predictorRequest := []byte(`{"instances":[[0,0,0]]}`) @@ -44,11 +44,11 @@ func TestLogger(t *testing.T) { // Start a local HTTP server logSvc := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { b, err := io.ReadAll(req.Body) - g.Expect(err).To(gomega.BeNil()) + g.Expect(err).ToNot(gomega.HaveOccurred()) responseChan <- string(b) g.Expect(b).To(gomega.Or(gomega.Equal(predictorRequest), gomega.Equal(predictorResponse))) _, err = rw.Write([]byte(`ok`)) - g.Expect(err).To(gomega.BeNil()) + g.Expect(err).ToNot(gomega.HaveOccurred()) })) // Close the server when test finishes defer logSvc.Close() @@ -56,25 +56,25 @@ func TestLogger(t *testing.T) { // Start a local HTTP server predictor := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { b, err := io.ReadAll(req.Body) - g.Expect(err).To(gomega.BeNil()) + g.Expect(err).ToNot(gomega.HaveOccurred()) g.Expect(b).To(gomega.Or(gomega.Equal(predictorRequest), gomega.Equal(predictorResponse))) _, err = rw.Write(predictorResponse) - g.Expect(err).To(gomega.BeNil()) + g.Expect(err).ToNot(gomega.HaveOccurred()) })) // Close the server when test finishes defer predictor.Close() reader := bytes.NewReader(predictorRequest) - r := httptest.NewRequest("POST", "http://a", reader) + r := httptest.NewRequest(http.MethodPost, "http://a", reader) w := httptest.NewRecorder() logger, _ := pkglogging.NewLogger("", "INFO") logf.SetLogger(zap.New()) logSvcUrl, err := url.Parse(logSvc.URL) - g.Expect(err).To(gomega.BeNil()) + g.Expect(err).ToNot(gomega.HaveOccurred()) sourceUri, err := url.Parse("http://localhost:9081/") - g.Expect(err).To(gomega.BeNil()) + g.Expect(err).ToNot(gomega.HaveOccurred()) targetUri, err := url.Parse(predictor.URL) - g.Expect(err).To(gomega.BeNil()) + g.Expect(err).ToNot(gomega.HaveOccurred()) StartDispatcher(5, logger) httpProxy := httputil.NewSingleHostReverseProxy(targetUri) @@ -83,7 +83,9 @@ func TestLogger(t *testing.T) { oh.ServeHTTP(w, r) - b2, _ := io.ReadAll(w.Result().Body) + resp := w.Result() + defer resp.Body.Close() + b2, _ := io.ReadAll(resp.Body) g.Expect(b2).To(gomega.Equal(predictorResponse)) // get logRequest <-responseChan @@ -92,7 +94,6 @@ func TestLogger(t *testing.T) { } func TestLoggerWithMetadata(t *testing.T) { - g := gomega.NewGomegaWithT(t) predictorRequest := []byte(`{"instances":[[0,0,0]]}`) @@ -102,11 +103,11 @@ func TestLoggerWithMetadata(t *testing.T) { // Start a local HTTP server logSvc := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { b, err := io.ReadAll(req.Body) - g.Expect(err).To(gomega.BeNil()) + g.Expect(err).ToNot(gomega.HaveOccurred()) responseChan <- string(b) g.Expect(b).To(gomega.Or(gomega.Equal(predictorRequest), gomega.Equal(predictorResponse))) _, err = rw.Write([]byte(`ok`)) - g.Expect(err).To(gomega.BeNil()) + g.Expect(err).ToNot(gomega.HaveOccurred()) // If this is request, check the metadata if req.Header["Ce-Type"][0] == "org.kubeflow.serving.inference.request" { @@ -116,7 +117,6 @@ func TestLoggerWithMetadata(t *testing.T) { g.Expect(metadata["Foo"]).To(gomega.Equal([]string{"bar"})) } - })) // Close the server when test finishes defer logSvc.Close() @@ -124,26 +124,26 @@ func TestLoggerWithMetadata(t *testing.T) { // Start a local HTTP server predictor := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { b, err := io.ReadAll(req.Body) - g.Expect(err).To(gomega.BeNil()) + g.Expect(err).ToNot(gomega.HaveOccurred()) g.Expect(b).To(gomega.Or(gomega.Equal(predictorRequest), gomega.Equal(predictorResponse))) _, err = rw.Write(predictorResponse) - g.Expect(err).To(gomega.BeNil()) + g.Expect(err).ToNot(gomega.HaveOccurred()) })) // Close the server when test finishes defer predictor.Close() reader := bytes.NewReader(predictorRequest) - r := httptest.NewRequest("POST", "http://a", reader) + r := httptest.NewRequest(http.MethodPost, "http://a", reader) r.Header.Add("Foo", "bar") w := httptest.NewRecorder() logger, _ := pkglogging.NewLogger("", "INFO") logf.SetLogger(zap.New()) logSvcUrl, err := url.Parse(logSvc.URL) - g.Expect(err).To(gomega.BeNil()) + g.Expect(err).ToNot(gomega.HaveOccurred()) sourceUri, err := url.Parse("http://localhost:9081/") - g.Expect(err).To(gomega.BeNil()) + g.Expect(err).ToNot(gomega.HaveOccurred()) targetUri, err := url.Parse(predictor.URL) - g.Expect(err).To(gomega.BeNil()) + g.Expect(err).ToNot(gomega.HaveOccurred()) StartDispatcher(5, logger) httpProxy := httputil.NewSingleHostReverseProxy(targetUri) @@ -152,7 +152,9 @@ func TestLoggerWithMetadata(t *testing.T) { oh.ServeHTTP(w, r) - b2, _ := io.ReadAll(w.Result().Body) + resp := w.Result() + defer resp.Body.Close() + b2, _ := io.ReadAll(resp.Body) g.Expect(b2).To(gomega.Equal(predictorResponse)) // get logRequest <-responseChan @@ -161,7 +163,6 @@ func TestLoggerWithMetadata(t *testing.T) { } func TestBadResponse(t *testing.T) { - g := gomega.NewGomegaWithT(t) predictorRequest := []byte(`{"instances":[[0,0,0]]}`) @@ -170,24 +171,24 @@ func TestBadResponse(t *testing.T) { // Start a local HTTP server predictor := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { b, err := io.ReadAll(req.Body) - g.Expect(err).To(gomega.BeNil()) - g.Expect(b).To(gomega.Or(gomega.Equal(predictorRequest), gomega.Equal(predictorResponse))) + g.Expect(err).ToNot(gomega.HaveOccurred()) + g.Expect(b).To(gomega.Equal(predictorRequest)) http.Error(rw, "BadRequest", http.StatusBadRequest) })) // Close the server when test finishes defer predictor.Close() reader := bytes.NewReader(predictorRequest) - r := httptest.NewRequest("POST", "http://a", reader) + r := httptest.NewRequest(http.MethodPost, "http://a", reader) w := httptest.NewRecorder() logger, _ := pkglogging.NewLogger("", "INFO") logf.SetLogger(zap.New()) logSvcUrl, err := url.Parse("http://loggersvc") - g.Expect(err).To(gomega.BeNil()) + g.Expect(err).ToNot(gomega.HaveOccurred()) sourceUri, err := url.Parse("http://localhost:9081/") - g.Expect(err).To(gomega.BeNil()) + g.Expect(err).ToNot(gomega.HaveOccurred()) targetUri, err := url.Parse(predictor.URL) - g.Expect(err).To(gomega.BeNil()) + g.Expect(err).ToNot(gomega.HaveOccurred()) StartDispatcher(1, logger) httpProxy := httputil.NewSingleHostReverseProxy(targetUri) diff --git a/pkg/logger/worker.go b/pkg/logger/worker.go index c79b0eb20da..7a98f43b22a 100644 --- a/pkg/logger/worker.go +++ b/pkg/logger/worker.go @@ -21,6 +21,7 @@ import ( "crypto/tls" "crypto/x509" "encoding/json" + "errors" "fmt" "net/http" "os" @@ -29,6 +30,8 @@ import ( cloudevents "github.com/cloudevents/sdk-go/v2" cehttp "github.com/cloudevents/sdk-go/v2/protocol/http" "go.uber.org/zap" + + "github.com/kserve/kserve/pkg/constants" ) const ( @@ -36,7 +39,7 @@ const ( CEInferenceResponse = "org.kubeflow.serving.inference.response" // cloud events extension attributes have to be lowercase alphanumeric - //TODO: ideally request id would have its own header but make do with ce-id for now + // TODO: ideally request id would have its own header but make do with ce-id for now InferenceServiceAttr = "inferenceservicename" NamespaceAttr = "namespace" ComponentAttr = "component" @@ -45,7 +48,6 @@ const ( EndpointAttr = "endpoint" LoggerWorkerQueueSize = 100 - LoggerCaCertMountPath = "/etc/tls/logger" CloudEventsIdHeader = "Ce-Id" ) @@ -68,7 +70,6 @@ func NewWorker(id int, workerQueue chan chan LogRequest, logger *zap.SugaredLogg Work: make(chan LogRequest), WorkerQueue: workerQueue, QuitChan: make(chan bool), - CeCtx: cloudevents.WithEncodingBinary(context.Background()), } } @@ -78,26 +79,24 @@ type Worker struct { Work chan LogRequest WorkerQueue chan chan LogRequest QuitChan chan bool - CeCtx context.Context } func (w *Worker) sendCloudEvent(logReq LogRequest) error { t, err := cloudevents.NewHTTP( cloudevents.WithTarget(logReq.Url.String()), ) - if err != nil { return fmt.Errorf("while creating http transport: %w", err) } if logReq.Url.Scheme == "https" { - caCertFilePath := filepath.Join(LoggerCaCertMountPath, logReq.CertName) + caCertFilePath := filepath.Join(constants.LoggerCaCertMountPath, logReq.CertName) caCertFile, err := os.ReadFile(caCertFilePath) // Do not fail if certificates not found, for backwards compatibility if err == nil { clientCertPool := x509.NewCertPool() if !clientCertPool.AppendCertsFromPEM(caCertFile) { - return fmt.Errorf("while parsing CA certificate") + return errors.New("while parsing CA certificate") } tlsTransport := &http.Transport{ @@ -138,8 +137,8 @@ func (w *Worker) sendCloudEvent(logReq LogRequest) error { if err := event.SetData(logReq.ContentType, *logReq.Bytes); err != nil { return fmt.Errorf("while setting cloudevents data: %w", err) } - - res := c.Send(w.CeCtx, event) + ceCtx := cloudevents.WithEncodingBinary(context.Background()) + res := c.Send(ceCtx, event) if cloudevents.IsUndelivered(res) { return fmt.Errorf("while sending event: %w", res) } else { @@ -176,7 +175,7 @@ func (w *Worker) Start() { case <-w.QuitChan: // We have been asked to stop. - fmt.Printf("worker %d stopping\n", w.ID) + w.Log.Infof("worker %d stopping\n", w.ID) return } } diff --git a/pkg/modelconfig/configmap.go b/pkg/modelconfig/configmap.go index e06a819e658..2a0b44b747e 100644 --- a/pkg/modelconfig/configmap.go +++ b/pkg/modelconfig/configmap.go @@ -20,16 +20,19 @@ import ( "fmt" jsoniter "github.com/json-iterator/go" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/log" + "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" "github.com/kserve/kserve/pkg/apis/serving/v1beta1" "github.com/kserve/kserve/pkg/constants" - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "sigs.k8s.io/controller-runtime/pkg/log" ) -var logger = log.Log.WithName("ModelConfig") -var json = jsoniter.ConfigCompatibleWithStandardLibrary +var ( + logger = log.Log.WithName("ModelConfig") + json = jsoniter.ConfigCompatibleWithStandardLibrary +) type ModelConfig struct { Name string `json:"modelName"` @@ -79,7 +82,7 @@ func NewConfigsDelta(updatedConfigs ModelConfigs, deletedConfigs []string) *Conf // } // } // ] -func (config *ConfigsDelta) Process(configMap *v1.ConfigMap) (err error) { +func (config *ConfigsDelta) Process(configMap *corev1.ConfigMap) (err error) { if len(config.updated) == 0 && len(config.deleted) == 0 { return nil } @@ -113,10 +116,10 @@ func (config *ConfigsDelta) Process(configMap *v1.ConfigMap) (err error) { return nil } -func CreateEmptyModelConfig(isvc *v1beta1.InferenceService, shardId int) (*v1.ConfigMap, error) { +func CreateEmptyModelConfig(isvc *v1beta1.InferenceService, shardId int) (*corev1.ConfigMap, error) { multiModelConfigMapName := constants.ModelConfigName(isvc.Name, shardId) // Create a modelConfig without any models in it - multiModelConfigMap := &v1.ConfigMap{ + multiModelConfigMap := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: multiModelConfigMapName, Namespace: isvc.Namespace, diff --git a/pkg/modelconfig/configmap_test.go b/pkg/modelconfig/configmap_test.go index 148ff88abe8..f70221f4f94 100644 --- a/pkg/modelconfig/configmap_test.go +++ b/pkg/modelconfig/configmap_test.go @@ -20,23 +20,25 @@ import ( "sort" "testing" - "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" - "github.com/kserve/kserve/pkg/apis/serving/v1beta1" - "github.com/kserve/kserve/pkg/constants" - "github.com/kserve/kserve/pkg/controller/v1alpha1/trainedmodel/sharding/memory" testify "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "google.golang.org/protobuf/proto" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/log/zap" + + "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" + "github.com/kserve/kserve/pkg/apis/serving/v1beta1" + "github.com/kserve/kserve/pkg/constants" + "github.com/kserve/kserve/pkg/controller/v1alpha1/trainedmodel/sharding/memory" ) func TestProcessAddOrUpdate(t *testing.T) { log.SetLogger(zap.New()) testCases := map[string]struct { modelConfigs ModelConfigs - configMap *v1.ConfigMap + configMap *corev1.ConfigMap expected string }{ "add to nil data": { @@ -46,7 +48,7 @@ func TestProcessAddOrUpdate(t *testing.T) { Spec: v1alpha1.ModelSpec{StorageURI: "s3//model1", Framework: "framework1"}, }, }, - configMap: &v1.ConfigMap{ + configMap: &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{Name: "empty-config", Namespace: "test"}, }, expected: `[{"modelName":"model1","modelSpec":{"storageUri":"s3//model1","framework":"framework1","memory":"0"}}]`, @@ -58,7 +60,7 @@ func TestProcessAddOrUpdate(t *testing.T) { Spec: v1alpha1.ModelSpec{StorageURI: "s3//model1", Framework: "framework1"}, }, }, - configMap: &v1.ConfigMap{ + configMap: &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{Name: "empty-config", Namespace: "test"}, Data: map[string]string{ constants.ModelConfigFileName: "", @@ -73,7 +75,7 @@ func TestProcessAddOrUpdate(t *testing.T) { Spec: v1alpha1.ModelSpec{StorageURI: "s3//model1", Framework: "framework1"}, }, }, - configMap: &v1.ConfigMap{ + configMap: &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{Name: "empty-config", Namespace: "test"}, Data: map[string]string{ constants.ModelConfigFileName: "[]", @@ -88,7 +90,7 @@ func TestProcessAddOrUpdate(t *testing.T) { Spec: v1alpha1.ModelSpec{StorageURI: "s3//model2", Framework: "framework2"}, }, }, - configMap: &v1.ConfigMap{ + configMap: &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{Name: "test-config", Namespace: "test"}, Data: map[string]string{ constants.ModelConfigFileName: `[{"modelName":"model1","modelSpec":{"storageUri":"s3//model1","framework":"framework1","memory":"0"}}]`, @@ -104,7 +106,7 @@ func TestProcessAddOrUpdate(t *testing.T) { Spec: v1alpha1.ModelSpec{StorageURI: "s3//new-model1", Framework: "new-framework1"}, }, }, - configMap: &v1.ConfigMap{ + configMap: &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{Name: "test-config", Namespace: "test"}, Data: map[string]string{ constants.ModelConfigFileName: `[{"modelName":"model1","modelSpec":{"storageUri":"s3//model1","framework":"framework1","memory":"0"}},` + @@ -118,11 +120,11 @@ func TestProcessAddOrUpdate(t *testing.T) { for _, tc := range testCases { mConfig := NewConfigsDelta(tc.modelConfigs, nil) err := mConfig.Process(tc.configMap) - testify.Nil(t, err) + require.NoError(t, err) data, err := getSortedConfigData(tc.configMap.Data[constants.ModelConfigFileName]) - testify.Nil(t, err) + require.NoError(t, err) expected, _ := getSortedConfigData(tc.expected) - testify.Equal(t, data, expected) + testify.Equal(t, expected, data) } } @@ -130,19 +132,19 @@ func TestProcessDelete(t *testing.T) { log.SetLogger(zap.New()) testCases := map[string]struct { modelConfigs []string - configMap *v1.ConfigMap + configMap *corev1.ConfigMap expected string }{ "delete nil data": { modelConfigs: []string{"model1"}, - configMap: &v1.ConfigMap{ + configMap: &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{Name: "empty-config", Namespace: "test"}, }, expected: "[]", }, "delete empty data": { modelConfigs: []string{"model1"}, - configMap: &v1.ConfigMap{ + configMap: &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{Name: "empty-config", Namespace: "test"}, Data: map[string]string{ constants.ModelConfigFileName: "", @@ -152,7 +154,7 @@ func TestProcessDelete(t *testing.T) { }, "delete empty data value": { modelConfigs: []string{"model1"}, - configMap: &v1.ConfigMap{ + configMap: &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{Name: "empty-config", Namespace: "test"}, Data: map[string]string{ constants.ModelConfigFileName: "[]", @@ -162,7 +164,7 @@ func TestProcessDelete(t *testing.T) { }, "delete filename non-exist": { modelConfigs: []string{"model1"}, - configMap: &v1.ConfigMap{ + configMap: &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{Name: "test-config", Namespace: "test"}, Data: map[string]string{ constants.ModelConfigFileName: `[{"modelName":"model2","modelSpec":{"storageUri":"s3//model2","framework":"framework2","memory":"0"}}]`, @@ -172,7 +174,7 @@ func TestProcessDelete(t *testing.T) { }, "delete filename exist": { modelConfigs: []string{"model1"}, - configMap: &v1.ConfigMap{ + configMap: &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{Name: "test-config", Namespace: "test"}, Data: map[string]string{ constants.ModelConfigFileName: `[{"modelName":"model1","modelSpec":{"storageUri":"s3//model1","framework":"framework1","memory":"0"}}]`, @@ -184,11 +186,11 @@ func TestProcessDelete(t *testing.T) { for _, tc := range testCases { mConfig := NewConfigsDelta(nil, tc.modelConfigs) err := mConfig.Process(tc.configMap) - testify.Nil(t, err) + require.NoError(t, err) data, err := getSortedConfigData(tc.configMap.Data[constants.ModelConfigFileName]) - testify.Nil(t, err) + require.NoError(t, err) expected, _ := getSortedConfigData(tc.expected) - testify.Equal(t, data, expected) + testify.Equal(t, expected, data) } } @@ -197,7 +199,7 @@ func TestProcess(t *testing.T) { testCases := map[string]struct { updated ModelConfigs deleted []string - configMap *v1.ConfigMap + configMap *corev1.ConfigMap expected string }{ "process configmap": { @@ -212,7 +214,7 @@ func TestProcess(t *testing.T) { }, }, deleted: []string{"model2"}, - configMap: &v1.ConfigMap{ + configMap: &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{Name: "test-config", Namespace: "test"}, Data: map[string]string{ constants.ModelConfigFileName: `[{"modelName":"model1","modelSpec":{"storageUri":"s3//model1","framework":"framework1","memory":"0"}},` + @@ -226,11 +228,11 @@ func TestProcess(t *testing.T) { for _, tc := range testCases { mConfig := NewConfigsDelta(tc.updated, tc.deleted) err := mConfig.Process(tc.configMap) - testify.Nil(t, err) + require.NoError(t, err) data, err := getSortedConfigData(tc.configMap.Data[constants.ModelConfigFileName]) - testify.Nil(t, err) + require.NoError(t, err) expected, _ := getSortedConfigData(tc.expected) - testify.Equal(t, data, expected) + testify.Equal(t, expected, data) } } @@ -265,7 +267,7 @@ func TestCreateEmptyModelConfig(t *testing.T) { } shardStrategy := memory.MemoryStrategy{} shardId := shardStrategy.GetShard(isvc)[0] - expected := &v1.ConfigMap{ + expected := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: constants.ModelConfigName(isvc.Name, shardId), Namespace: isvc.Namespace, @@ -277,7 +279,6 @@ func TestCreateEmptyModelConfig(t *testing.T) { } configMap, err := CreateEmptyModelConfig(isvc, shardId) - testify.Nil(t, err) - testify.Equal(t, configMap, expected) - + require.NoError(t, err) + testify.Equal(t, expected, configMap) } diff --git a/pkg/openapi/openapi_generated.go b/pkg/openapi/openapi_generated.go index 0316157ba2a..88d3ae2c863 100644 --- a/pkg/openapi/openapi_generated.go +++ b/pkg/openapi/openapi_generated.go @@ -96,6 +96,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/kserve/kserve/pkg/apis/serving/v1beta1.PodSpec": schema_pkg_apis_serving_v1beta1_PodSpec(ref), "github.com/kserve/kserve/pkg/apis/serving/v1beta1.PredictorExtensionSpec": schema_pkg_apis_serving_v1beta1_PredictorExtensionSpec(ref), "github.com/kserve/kserve/pkg/apis/serving/v1beta1.PredictorSpec": schema_pkg_apis_serving_v1beta1_PredictorSpec(ref), + "github.com/kserve/kserve/pkg/apis/serving/v1beta1.ResourceConfig": schema_pkg_apis_serving_v1beta1_ResourceConfig(ref), "github.com/kserve/kserve/pkg/apis/serving/v1beta1.SKLearnSpec": schema_pkg_apis_serving_v1beta1_SKLearnSpec(ref), "github.com/kserve/kserve/pkg/apis/serving/v1beta1.SecurityConfig": schema_pkg_apis_serving_v1beta1_SecurityConfig(ref), "github.com/kserve/kserve/pkg/apis/serving/v1beta1.ServiceConfig": schema_pkg_apis_serving_v1beta1_ServiceConfig(ref), @@ -2674,7 +2675,7 @@ func schema_pkg_apis_serving_v1beta1_CustomExplainer(ref common.ReferenceCallbac }, "nodeName": { SchemaProps: spec.SchemaProps{ - Description: "NodeName is a request to schedule this pod onto a specific node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits resource requirements.", + Description: "NodeName indicates in which node this pod is scheduled. If empty, this pod is a candidate for scheduling by the scheduler defined in schedulerName. Once this field is set, the kubelet for this node becomes responsible for the lifecycle of this pod. This field should not be used to express a desire for the pod to be scheduled on a specific node. https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodename", Type: []string{"string"}, Format: "", }, @@ -2915,7 +2916,7 @@ func schema_pkg_apis_serving_v1beta1_CustomExplainer(ref common.ReferenceCallbac }, "os": { SchemaProps: spec.SchemaProps{ - Description: "Specifies the OS of the containers in the pod. Some pod and container fields are restricted if this is set.\n\nIf the OS field is set to linux, the following fields must be unset: -securityContext.windowsOptions\n\nIf the OS field is set to windows, following fields must be unset: - spec.hostPID - spec.hostIPC - spec.hostUsers - spec.securityContext.appArmorProfile - spec.securityContext.seLinuxOptions - spec.securityContext.seccompProfile - spec.securityContext.fsGroup - spec.securityContext.fsGroupChangePolicy - spec.securityContext.sysctls - spec.shareProcessNamespace - spec.securityContext.runAsUser - spec.securityContext.runAsGroup - spec.securityContext.supplementalGroups - spec.containers[*].securityContext.appArmorProfile - spec.containers[*].securityContext.seLinuxOptions - spec.containers[*].securityContext.seccompProfile - spec.containers[*].securityContext.capabilities - spec.containers[*].securityContext.readOnlyRootFilesystem - spec.containers[*].securityContext.privileged - spec.containers[*].securityContext.allowPrivilegeEscalation - spec.containers[*].securityContext.procMount - spec.containers[*].securityContext.runAsUser - spec.containers[*].securityContext.runAsGroup", + Description: "Specifies the OS of the containers in the pod. Some pod and container fields are restricted if this is set.\n\nIf the OS field is set to linux, the following fields must be unset: -securityContext.windowsOptions\n\nIf the OS field is set to windows, following fields must be unset: - spec.hostPID - spec.hostIPC - spec.hostUsers - spec.securityContext.appArmorProfile - spec.securityContext.seLinuxOptions - spec.securityContext.seccompProfile - spec.securityContext.fsGroup - spec.securityContext.fsGroupChangePolicy - spec.securityContext.sysctls - spec.shareProcessNamespace - spec.securityContext.runAsUser - spec.securityContext.runAsGroup - spec.securityContext.supplementalGroups - spec.securityContext.supplementalGroupsPolicy - spec.containers[*].securityContext.appArmorProfile - spec.containers[*].securityContext.seLinuxOptions - spec.containers[*].securityContext.seccompProfile - spec.containers[*].securityContext.capabilities - spec.containers[*].securityContext.readOnlyRootFilesystem - spec.containers[*].securityContext.privileged - spec.containers[*].securityContext.allowPrivilegeEscalation - spec.containers[*].securityContext.procMount - spec.containers[*].securityContext.runAsUser - spec.containers[*].securityContext.runAsGroup", Ref: ref("k8s.io/api/core/v1.PodOS"), }, }, @@ -2974,12 +2975,18 @@ func schema_pkg_apis_serving_v1beta1_CustomExplainer(ref common.ReferenceCallbac }, }, }, + "resources": { + SchemaProps: spec.SchemaProps{ + Description: "Resources is the total amount of CPU and Memory resources required by all containers in the pod. It supports specifying Requests and Limits for \"cpu\" and \"memory\" resource names only. ResourceClaims are not supported.\n\nThis field enables fine-grained control over resource allocation for the entire pod, allowing resource sharing among containers in a pod.\n\nThis is an alpha field and requires enabling the PodLevelResources feature gate.", + Ref: ref("k8s.io/api/core/v1.ResourceRequirements"), + }, + }, }, Required: []string{"containers"}, }, }, Dependencies: []string{ - "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.Container", "k8s.io/api/core/v1.EphemeralContainer", "k8s.io/api/core/v1.HostAlias", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodDNSConfig", "k8s.io/api/core/v1.PodOS", "k8s.io/api/core/v1.PodReadinessGate", "k8s.io/api/core/v1.PodResourceClaim", "k8s.io/api/core/v1.PodSchedulingGate", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.Toleration", "k8s.io/api/core/v1.TopologySpreadConstraint", "k8s.io/api/core/v1.Volume", "k8s.io/apimachinery/pkg/api/resource.Quantity"}, + "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.Container", "k8s.io/api/core/v1.EphemeralContainer", "k8s.io/api/core/v1.HostAlias", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodDNSConfig", "k8s.io/api/core/v1.PodOS", "k8s.io/api/core/v1.PodReadinessGate", "k8s.io/api/core/v1.PodResourceClaim", "k8s.io/api/core/v1.PodSchedulingGate", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.ResourceRequirements", "k8s.io/api/core/v1.Toleration", "k8s.io/api/core/v1.TopologySpreadConstraint", "k8s.io/api/core/v1.Volume", "k8s.io/apimachinery/pkg/api/resource.Quantity"}, } } @@ -3158,7 +3165,7 @@ func schema_pkg_apis_serving_v1beta1_CustomPredictor(ref common.ReferenceCallbac }, "nodeName": { SchemaProps: spec.SchemaProps{ - Description: "NodeName is a request to schedule this pod onto a specific node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits resource requirements.", + Description: "NodeName indicates in which node this pod is scheduled. If empty, this pod is a candidate for scheduling by the scheduler defined in schedulerName. Once this field is set, the kubelet for this node becomes responsible for the lifecycle of this pod. This field should not be used to express a desire for the pod to be scheduled on a specific node. https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodename", Type: []string{"string"}, Format: "", }, @@ -3399,7 +3406,7 @@ func schema_pkg_apis_serving_v1beta1_CustomPredictor(ref common.ReferenceCallbac }, "os": { SchemaProps: spec.SchemaProps{ - Description: "Specifies the OS of the containers in the pod. Some pod and container fields are restricted if this is set.\n\nIf the OS field is set to linux, the following fields must be unset: -securityContext.windowsOptions\n\nIf the OS field is set to windows, following fields must be unset: - spec.hostPID - spec.hostIPC - spec.hostUsers - spec.securityContext.appArmorProfile - spec.securityContext.seLinuxOptions - spec.securityContext.seccompProfile - spec.securityContext.fsGroup - spec.securityContext.fsGroupChangePolicy - spec.securityContext.sysctls - spec.shareProcessNamespace - spec.securityContext.runAsUser - spec.securityContext.runAsGroup - spec.securityContext.supplementalGroups - spec.containers[*].securityContext.appArmorProfile - spec.containers[*].securityContext.seLinuxOptions - spec.containers[*].securityContext.seccompProfile - spec.containers[*].securityContext.capabilities - spec.containers[*].securityContext.readOnlyRootFilesystem - spec.containers[*].securityContext.privileged - spec.containers[*].securityContext.allowPrivilegeEscalation - spec.containers[*].securityContext.procMount - spec.containers[*].securityContext.runAsUser - spec.containers[*].securityContext.runAsGroup", + Description: "Specifies the OS of the containers in the pod. Some pod and container fields are restricted if this is set.\n\nIf the OS field is set to linux, the following fields must be unset: -securityContext.windowsOptions\n\nIf the OS field is set to windows, following fields must be unset: - spec.hostPID - spec.hostIPC - spec.hostUsers - spec.securityContext.appArmorProfile - spec.securityContext.seLinuxOptions - spec.securityContext.seccompProfile - spec.securityContext.fsGroup - spec.securityContext.fsGroupChangePolicy - spec.securityContext.sysctls - spec.shareProcessNamespace - spec.securityContext.runAsUser - spec.securityContext.runAsGroup - spec.securityContext.supplementalGroups - spec.securityContext.supplementalGroupsPolicy - spec.containers[*].securityContext.appArmorProfile - spec.containers[*].securityContext.seLinuxOptions - spec.containers[*].securityContext.seccompProfile - spec.containers[*].securityContext.capabilities - spec.containers[*].securityContext.readOnlyRootFilesystem - spec.containers[*].securityContext.privileged - spec.containers[*].securityContext.allowPrivilegeEscalation - spec.containers[*].securityContext.procMount - spec.containers[*].securityContext.runAsUser - spec.containers[*].securityContext.runAsGroup", Ref: ref("k8s.io/api/core/v1.PodOS"), }, }, @@ -3458,12 +3465,18 @@ func schema_pkg_apis_serving_v1beta1_CustomPredictor(ref common.ReferenceCallbac }, }, }, + "resources": { + SchemaProps: spec.SchemaProps{ + Description: "Resources is the total amount of CPU and Memory resources required by all containers in the pod. It supports specifying Requests and Limits for \"cpu\" and \"memory\" resource names only. ResourceClaims are not supported.\n\nThis field enables fine-grained control over resource allocation for the entire pod, allowing resource sharing among containers in a pod.\n\nThis is an alpha field and requires enabling the PodLevelResources feature gate.", + Ref: ref("k8s.io/api/core/v1.ResourceRequirements"), + }, + }, }, Required: []string{"containers"}, }, }, Dependencies: []string{ - "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.Container", "k8s.io/api/core/v1.EphemeralContainer", "k8s.io/api/core/v1.HostAlias", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodDNSConfig", "k8s.io/api/core/v1.PodOS", "k8s.io/api/core/v1.PodReadinessGate", "k8s.io/api/core/v1.PodResourceClaim", "k8s.io/api/core/v1.PodSchedulingGate", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.Toleration", "k8s.io/api/core/v1.TopologySpreadConstraint", "k8s.io/api/core/v1.Volume", "k8s.io/apimachinery/pkg/api/resource.Quantity"}, + "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.Container", "k8s.io/api/core/v1.EphemeralContainer", "k8s.io/api/core/v1.HostAlias", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodDNSConfig", "k8s.io/api/core/v1.PodOS", "k8s.io/api/core/v1.PodReadinessGate", "k8s.io/api/core/v1.PodResourceClaim", "k8s.io/api/core/v1.PodSchedulingGate", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.ResourceRequirements", "k8s.io/api/core/v1.Toleration", "k8s.io/api/core/v1.TopologySpreadConstraint", "k8s.io/api/core/v1.Volume", "k8s.io/apimachinery/pkg/api/resource.Quantity"}, } } @@ -3642,7 +3655,7 @@ func schema_pkg_apis_serving_v1beta1_CustomTransformer(ref common.ReferenceCallb }, "nodeName": { SchemaProps: spec.SchemaProps{ - Description: "NodeName is a request to schedule this pod onto a specific node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits resource requirements.", + Description: "NodeName indicates in which node this pod is scheduled. If empty, this pod is a candidate for scheduling by the scheduler defined in schedulerName. Once this field is set, the kubelet for this node becomes responsible for the lifecycle of this pod. This field should not be used to express a desire for the pod to be scheduled on a specific node. https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodename", Type: []string{"string"}, Format: "", }, @@ -3883,7 +3896,7 @@ func schema_pkg_apis_serving_v1beta1_CustomTransformer(ref common.ReferenceCallb }, "os": { SchemaProps: spec.SchemaProps{ - Description: "Specifies the OS of the containers in the pod. Some pod and container fields are restricted if this is set.\n\nIf the OS field is set to linux, the following fields must be unset: -securityContext.windowsOptions\n\nIf the OS field is set to windows, following fields must be unset: - spec.hostPID - spec.hostIPC - spec.hostUsers - spec.securityContext.appArmorProfile - spec.securityContext.seLinuxOptions - spec.securityContext.seccompProfile - spec.securityContext.fsGroup - spec.securityContext.fsGroupChangePolicy - spec.securityContext.sysctls - spec.shareProcessNamespace - spec.securityContext.runAsUser - spec.securityContext.runAsGroup - spec.securityContext.supplementalGroups - spec.containers[*].securityContext.appArmorProfile - spec.containers[*].securityContext.seLinuxOptions - spec.containers[*].securityContext.seccompProfile - spec.containers[*].securityContext.capabilities - spec.containers[*].securityContext.readOnlyRootFilesystem - spec.containers[*].securityContext.privileged - spec.containers[*].securityContext.allowPrivilegeEscalation - spec.containers[*].securityContext.procMount - spec.containers[*].securityContext.runAsUser - spec.containers[*].securityContext.runAsGroup", + Description: "Specifies the OS of the containers in the pod. Some pod and container fields are restricted if this is set.\n\nIf the OS field is set to linux, the following fields must be unset: -securityContext.windowsOptions\n\nIf the OS field is set to windows, following fields must be unset: - spec.hostPID - spec.hostIPC - spec.hostUsers - spec.securityContext.appArmorProfile - spec.securityContext.seLinuxOptions - spec.securityContext.seccompProfile - spec.securityContext.fsGroup - spec.securityContext.fsGroupChangePolicy - spec.securityContext.sysctls - spec.shareProcessNamespace - spec.securityContext.runAsUser - spec.securityContext.runAsGroup - spec.securityContext.supplementalGroups - spec.securityContext.supplementalGroupsPolicy - spec.containers[*].securityContext.appArmorProfile - spec.containers[*].securityContext.seLinuxOptions - spec.containers[*].securityContext.seccompProfile - spec.containers[*].securityContext.capabilities - spec.containers[*].securityContext.readOnlyRootFilesystem - spec.containers[*].securityContext.privileged - spec.containers[*].securityContext.allowPrivilegeEscalation - spec.containers[*].securityContext.procMount - spec.containers[*].securityContext.runAsUser - spec.containers[*].securityContext.runAsGroup", Ref: ref("k8s.io/api/core/v1.PodOS"), }, }, @@ -3942,12 +3955,18 @@ func schema_pkg_apis_serving_v1beta1_CustomTransformer(ref common.ReferenceCallb }, }, }, + "resources": { + SchemaProps: spec.SchemaProps{ + Description: "Resources is the total amount of CPU and Memory resources required by all containers in the pod. It supports specifying Requests and Limits for \"cpu\" and \"memory\" resource names only. ResourceClaims are not supported.\n\nThis field enables fine-grained control over resource allocation for the entire pod, allowing resource sharing among containers in a pod.\n\nThis is an alpha field and requires enabling the PodLevelResources feature gate.", + Ref: ref("k8s.io/api/core/v1.ResourceRequirements"), + }, + }, }, Required: []string{"containers"}, }, }, Dependencies: []string{ - "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.Container", "k8s.io/api/core/v1.EphemeralContainer", "k8s.io/api/core/v1.HostAlias", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodDNSConfig", "k8s.io/api/core/v1.PodOS", "k8s.io/api/core/v1.PodReadinessGate", "k8s.io/api/core/v1.PodResourceClaim", "k8s.io/api/core/v1.PodSchedulingGate", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.Toleration", "k8s.io/api/core/v1.TopologySpreadConstraint", "k8s.io/api/core/v1.Volume", "k8s.io/apimachinery/pkg/api/resource.Quantity"}, + "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.Container", "k8s.io/api/core/v1.EphemeralContainer", "k8s.io/api/core/v1.HostAlias", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodDNSConfig", "k8s.io/api/core/v1.PodOS", "k8s.io/api/core/v1.PodReadinessGate", "k8s.io/api/core/v1.PodResourceClaim", "k8s.io/api/core/v1.PodSchedulingGate", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.ResourceRequirements", "k8s.io/api/core/v1.Toleration", "k8s.io/api/core/v1.TopologySpreadConstraint", "k8s.io/api/core/v1.Volume", "k8s.io/apimachinery/pkg/api/resource.Quantity"}, } } @@ -4374,7 +4393,7 @@ func schema_pkg_apis_serving_v1beta1_ExplainerSpec(ref common.ReferenceCallback) }, }, SchemaProps: spec.SchemaProps{ - Description: "List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/", + Description: "List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ @@ -4414,7 +4433,7 @@ func schema_pkg_apis_serving_v1beta1_ExplainerSpec(ref common.ReferenceCallback) }, }, SchemaProps: spec.SchemaProps{ - Description: "List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource. This field is beta-level and available on clusters that haven't disabled the EphemeralContainers feature gate.", + Description: "List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource.", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ @@ -4428,7 +4447,7 @@ func schema_pkg_apis_serving_v1beta1_ExplainerSpec(ref common.ReferenceCallback) }, "restartPolicy": { SchemaProps: spec.SchemaProps{ - Description: "Restart policy for all containers within the pod. One of Always, OnFailure, Never. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy", + Description: "Restart policy for all containers within the pod. One of Always, OnFailure, Never. In some contexts, only a subset of those values may be permitted. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy", Type: []string{"string"}, Format: "", }, @@ -4484,7 +4503,7 @@ func schema_pkg_apis_serving_v1beta1_ExplainerSpec(ref common.ReferenceCallback) }, "serviceAccount": { SchemaProps: spec.SchemaProps{ - Description: "DeprecatedServiceAccount is a depreciated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead.", + Description: "DeprecatedServiceAccount is a deprecated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead.", Type: []string{"string"}, Format: "", }, @@ -4498,7 +4517,7 @@ func schema_pkg_apis_serving_v1beta1_ExplainerSpec(ref common.ReferenceCallback) }, "nodeName": { SchemaProps: spec.SchemaProps{ - Description: "NodeName is a request to schedule this pod onto a specific node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits resource requirements.", + Description: "NodeName indicates in which node this pod is scheduled. If empty, this pod is a candidate for scheduling by the scheduler defined in schedulerName. Once this field is set, the kubelet for this node becomes responsible for the lifecycle of this pod. This field should not be used to express a desire for the pod to be scheduled on a specific node. https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodename", Type: []string{"string"}, Format: "", }, @@ -4545,7 +4564,7 @@ func schema_pkg_apis_serving_v1beta1_ExplainerSpec(ref common.ReferenceCallback) }, }, SchemaProps: spec.SchemaProps{ - Description: "ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. For example, in the case of docker, only DockerConfig type secrets are honored. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod", + Description: "ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ @@ -4606,7 +4625,7 @@ func schema_pkg_apis_serving_v1beta1_ExplainerSpec(ref common.ReferenceCallback) }, }, SchemaProps: spec.SchemaProps{ - Description: "HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. This is only valid for non-hostNetwork pods.", + Description: "HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified.", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ @@ -4654,7 +4673,7 @@ func schema_pkg_apis_serving_v1beta1_ExplainerSpec(ref common.ReferenceCallback) }, "runtimeClassName": { SchemaProps: spec.SchemaProps{ - Description: "RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class This is a beta feature as of Kubernetes v1.14.", + Description: "RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class", Type: []string{"string"}, Format: "", }, @@ -4668,14 +4687,14 @@ func schema_pkg_apis_serving_v1beta1_ExplainerSpec(ref common.ReferenceCallback) }, "preemptionPolicy": { SchemaProps: spec.SchemaProps{ - Description: "PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset. This field is beta-level, gated by the NonPreemptingPriority feature-gate.", + Description: "PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset.", Type: []string{"string"}, Format: "", }, }, "overhead": { SchemaProps: spec.SchemaProps{ - Description: "Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md This field is beta-level as of Kubernetes v1.18, and is only honored by servers that enable the PodOverhead feature.", + Description: "Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md", Type: []string{"object"}, AdditionalProperties: &spec.SchemaOrBool{ Allows: true, @@ -4721,7 +4740,7 @@ func schema_pkg_apis_serving_v1beta1_ExplainerSpec(ref common.ReferenceCallback) }, "os": { SchemaProps: spec.SchemaProps{ - Description: "Specifies the OS of the containers in the pod. Some pod and container fields are restricted if this is set.\n\nIf the OS field is set to linux, the following fields must be unset: -securityContext.windowsOptions\n\nIf the OS field is set to windows, following fields must be unset: - spec.hostPID - spec.hostIPC - spec.securityContext.seLinuxOptions - spec.securityContext.seccompProfile - spec.securityContext.fsGroup - spec.securityContext.fsGroupChangePolicy - spec.securityContext.sysctls - spec.shareProcessNamespace - spec.securityContext.runAsUser - spec.securityContext.runAsGroup - spec.securityContext.supplementalGroups - spec.containers[*].securityContext.seLinuxOptions - spec.containers[*].securityContext.seccompProfile - spec.containers[*].securityContext.capabilities - spec.containers[*].securityContext.readOnlyRootFilesystem - spec.containers[*].securityContext.privileged - spec.containers[*].securityContext.allowPrivilegeEscalation - spec.containers[*].securityContext.procMount - spec.containers[*].securityContext.runAsUser - spec.containers[*].securityContext.runAsGroup This is an alpha field and requires the IdentifyPodOS feature", + Description: "Specifies the OS of the containers in the pod. Some pod and container fields are restricted if this is set.\n\nIf the OS field is set to linux, the following fields must be unset: -securityContext.windowsOptions\n\nIf the OS field is set to windows, following fields must be unset: - spec.hostPID - spec.hostIPC - spec.hostUsers - spec.securityContext.appArmorProfile - spec.securityContext.seLinuxOptions - spec.securityContext.seccompProfile - spec.securityContext.fsGroup - spec.securityContext.fsGroupChangePolicy - spec.securityContext.sysctls - spec.shareProcessNamespace - spec.securityContext.runAsUser - spec.securityContext.runAsGroup - spec.securityContext.supplementalGroups - spec.securityContext.supplementalGroupsPolicy - spec.containers[*].securityContext.appArmorProfile - spec.containers[*].securityContext.seLinuxOptions - spec.containers[*].securityContext.seccompProfile - spec.containers[*].securityContext.capabilities - spec.containers[*].securityContext.readOnlyRootFilesystem - spec.containers[*].securityContext.privileged - spec.containers[*].securityContext.allowPrivilegeEscalation - spec.containers[*].securityContext.procMount - spec.containers[*].securityContext.runAsUser - spec.containers[*].securityContext.runAsGroup", Ref: ref("k8s.io/api/core/v1.PodOS"), }, }, @@ -4744,7 +4763,7 @@ func schema_pkg_apis_serving_v1beta1_ExplainerSpec(ref common.ReferenceCallback) }, }, SchemaProps: spec.SchemaProps{ - Description: "SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod.\n\nSchedulingGates can only be set at pod creation time, and be removed only afterwards.\n\nThis is a beta feature enabled by the PodSchedulingReadiness feature gate.", + Description: "SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod.\n\nSchedulingGates can only be set at pod creation time, and be removed only afterwards.", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ @@ -4780,6 +4799,12 @@ func schema_pkg_apis_serving_v1beta1_ExplainerSpec(ref common.ReferenceCallback) }, }, }, + "resources": { + SchemaProps: spec.SchemaProps{ + Description: "Resources is the total amount of CPU and Memory resources required by all containers in the pod. It supports specifying Requests and Limits for \"cpu\" and \"memory\" resource names only. ResourceClaims are not supported.\n\nThis field enables fine-grained control over resource allocation for the entire pod, allowing resource sharing among containers in a pod.\n\nThis is an alpha field and requires enabling the PodLevelResources feature gate.", + Ref: ref("k8s.io/api/core/v1.ResourceRequirements"), + }, + }, "minReplicas": { SchemaProps: spec.SchemaProps{ Description: "Minimum number of replicas, defaults to 1 but can be set to 0 to enable scale-to-zero.", @@ -4883,7 +4908,7 @@ func schema_pkg_apis_serving_v1beta1_ExplainerSpec(ref common.ReferenceCallback) }, }, Dependencies: []string{ - "github.com/kserve/kserve/pkg/apis/serving/v1beta1.ARTExplainerSpec", "github.com/kserve/kserve/pkg/apis/serving/v1beta1.Batcher", "github.com/kserve/kserve/pkg/apis/serving/v1beta1.LoggerSpec", "k8s.io/api/apps/v1.DeploymentStrategy", "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.Container", "k8s.io/api/core/v1.EphemeralContainer", "k8s.io/api/core/v1.HostAlias", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodDNSConfig", "k8s.io/api/core/v1.PodOS", "k8s.io/api/core/v1.PodReadinessGate", "k8s.io/api/core/v1.PodResourceClaim", "k8s.io/api/core/v1.PodSchedulingGate", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.Toleration", "k8s.io/api/core/v1.TopologySpreadConstraint", "k8s.io/api/core/v1.Volume", "k8s.io/apimachinery/pkg/api/resource.Quantity"}, + "github.com/kserve/kserve/pkg/apis/serving/v1beta1.ARTExplainerSpec", "github.com/kserve/kserve/pkg/apis/serving/v1beta1.Batcher", "github.com/kserve/kserve/pkg/apis/serving/v1beta1.LoggerSpec", "k8s.io/api/apps/v1.DeploymentStrategy", "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.Container", "k8s.io/api/core/v1.EphemeralContainer", "k8s.io/api/core/v1.HostAlias", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodDNSConfig", "k8s.io/api/core/v1.PodOS", "k8s.io/api/core/v1.PodReadinessGate", "k8s.io/api/core/v1.PodResourceClaim", "k8s.io/api/core/v1.PodSchedulingGate", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.ResourceRequirements", "k8s.io/api/core/v1.Toleration", "k8s.io/api/core/v1.TopologySpreadConstraint", "k8s.io/api/core/v1.Volume", "k8s.io/apimachinery/pkg/api/resource.Quantity"}, } } @@ -5529,12 +5554,49 @@ func schema_pkg_apis_serving_v1beta1_InferenceServicesConfig(ref common.Referenc Ref: ref("github.com/kserve/kserve/pkg/apis/serving/v1beta1.ExplainersConfig"), }, }, + "serviceAnnotationDisallowedList": { + SchemaProps: spec.SchemaProps{ + Description: "ServiceAnnotationDisallowedList is a list of annotations that are not allowed to be propagated to Knative revisions", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "serviceLabelDisallowedList": { + SchemaProps: spec.SchemaProps{ + Description: "ServiceLabelDisallowedList is a list of labels that are not allowed to be propagated to Knative revisions", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "resource": { + SchemaProps: spec.SchemaProps{ + Description: "Resource configurations", + Default: map[string]interface{}{}, + Ref: ref("github.com/kserve/kserve/pkg/apis/serving/v1beta1.ResourceConfig"), + }, + }, }, Required: []string{"explainers"}, }, }, Dependencies: []string{ - "github.com/kserve/kserve/pkg/apis/serving/v1beta1.ExplainersConfig"}, + "github.com/kserve/kserve/pkg/apis/serving/v1beta1.ExplainersConfig", "github.com/kserve/kserve/pkg/apis/serving/v1beta1.ResourceConfig"}, } } @@ -5544,6 +5606,18 @@ func schema_pkg_apis_serving_v1beta1_IngressConfig(ref common.ReferenceCallback) SchemaProps: spec.SchemaProps{ Type: []string{"object"}, Properties: map[string]spec.Schema{ + "enableGatewayApi": { + SchemaProps: spec.SchemaProps{ + Type: []string{"boolean"}, + Format: "", + }, + }, + "kserveIngressGateway": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, "ingressGateway": { SchemaProps: spec.SchemaProps{ Type: []string{"string"}, @@ -6000,6 +6074,12 @@ func schema_pkg_apis_serving_v1beta1_LocalModelConfig(ref common.ReferenceCallba Format: "int64", }, }, + "disableVolumeManagement": { + SchemaProps: spec.SchemaProps{ + Type: []string{"boolean"}, + Format: "", + }, + }, }, Required: []string{"enabled", "jobNamespace"}, }, @@ -7524,7 +7604,7 @@ func schema_pkg_apis_serving_v1beta1_PodSpec(ref common.ReferenceCallback) commo }, }, SchemaProps: spec.SchemaProps{ - Description: "List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/", + Description: "List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ @@ -7564,7 +7644,7 @@ func schema_pkg_apis_serving_v1beta1_PodSpec(ref common.ReferenceCallback) commo }, }, SchemaProps: spec.SchemaProps{ - Description: "List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource. This field is beta-level and available on clusters that haven't disabled the EphemeralContainers feature gate.", + Description: "List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource.", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ @@ -7578,7 +7658,7 @@ func schema_pkg_apis_serving_v1beta1_PodSpec(ref common.ReferenceCallback) commo }, "restartPolicy": { SchemaProps: spec.SchemaProps{ - Description: "Restart policy for all containers within the pod. One of Always, OnFailure, Never. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy", + Description: "Restart policy for all containers within the pod. One of Always, OnFailure, Never. In some contexts, only a subset of those values may be permitted. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy", Type: []string{"string"}, Format: "", }, @@ -7634,7 +7714,7 @@ func schema_pkg_apis_serving_v1beta1_PodSpec(ref common.ReferenceCallback) commo }, "serviceAccount": { SchemaProps: spec.SchemaProps{ - Description: "DeprecatedServiceAccount is a depreciated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead.", + Description: "DeprecatedServiceAccount is a deprecated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead.", Type: []string{"string"}, Format: "", }, @@ -7648,7 +7728,7 @@ func schema_pkg_apis_serving_v1beta1_PodSpec(ref common.ReferenceCallback) commo }, "nodeName": { SchemaProps: spec.SchemaProps{ - Description: "NodeName is a request to schedule this pod onto a specific node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits resource requirements.", + Description: "NodeName indicates in which node this pod is scheduled. If empty, this pod is a candidate for scheduling by the scheduler defined in schedulerName. Once this field is set, the kubelet for this node becomes responsible for the lifecycle of this pod. This field should not be used to express a desire for the pod to be scheduled on a specific node. https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodename", Type: []string{"string"}, Format: "", }, @@ -7695,7 +7775,7 @@ func schema_pkg_apis_serving_v1beta1_PodSpec(ref common.ReferenceCallback) commo }, }, SchemaProps: spec.SchemaProps{ - Description: "ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. For example, in the case of docker, only DockerConfig type secrets are honored. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod", + Description: "ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ @@ -7756,7 +7836,7 @@ func schema_pkg_apis_serving_v1beta1_PodSpec(ref common.ReferenceCallback) commo }, }, SchemaProps: spec.SchemaProps{ - Description: "HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. This is only valid for non-hostNetwork pods.", + Description: "HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified.", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ @@ -7804,7 +7884,7 @@ func schema_pkg_apis_serving_v1beta1_PodSpec(ref common.ReferenceCallback) commo }, "runtimeClassName": { SchemaProps: spec.SchemaProps{ - Description: "RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class This is a beta feature as of Kubernetes v1.14.", + Description: "RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class", Type: []string{"string"}, Format: "", }, @@ -7818,14 +7898,14 @@ func schema_pkg_apis_serving_v1beta1_PodSpec(ref common.ReferenceCallback) commo }, "preemptionPolicy": { SchemaProps: spec.SchemaProps{ - Description: "PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset. This field is beta-level, gated by the NonPreemptingPriority feature-gate.", + Description: "PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset.", Type: []string{"string"}, Format: "", }, }, "overhead": { SchemaProps: spec.SchemaProps{ - Description: "Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md This field is beta-level as of Kubernetes v1.18, and is only honored by servers that enable the PodOverhead feature.", + Description: "Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md", Type: []string{"object"}, AdditionalProperties: &spec.SchemaOrBool{ Allows: true, @@ -7871,7 +7951,7 @@ func schema_pkg_apis_serving_v1beta1_PodSpec(ref common.ReferenceCallback) commo }, "os": { SchemaProps: spec.SchemaProps{ - Description: "Specifies the OS of the containers in the pod. Some pod and container fields are restricted if this is set.\n\nIf the OS field is set to linux, the following fields must be unset: -securityContext.windowsOptions\n\nIf the OS field is set to windows, following fields must be unset: - spec.hostPID - spec.hostIPC - spec.securityContext.seLinuxOptions - spec.securityContext.seccompProfile - spec.securityContext.fsGroup - spec.securityContext.fsGroupChangePolicy - spec.securityContext.sysctls - spec.shareProcessNamespace - spec.securityContext.runAsUser - spec.securityContext.runAsGroup - spec.securityContext.supplementalGroups - spec.containers[*].securityContext.seLinuxOptions - spec.containers[*].securityContext.seccompProfile - spec.containers[*].securityContext.capabilities - spec.containers[*].securityContext.readOnlyRootFilesystem - spec.containers[*].securityContext.privileged - spec.containers[*].securityContext.allowPrivilegeEscalation - spec.containers[*].securityContext.procMount - spec.containers[*].securityContext.runAsUser - spec.containers[*].securityContext.runAsGroup This is an alpha field and requires the IdentifyPodOS feature", + Description: "Specifies the OS of the containers in the pod. Some pod and container fields are restricted if this is set.\n\nIf the OS field is set to linux, the following fields must be unset: -securityContext.windowsOptions\n\nIf the OS field is set to windows, following fields must be unset: - spec.hostPID - spec.hostIPC - spec.hostUsers - spec.securityContext.appArmorProfile - spec.securityContext.seLinuxOptions - spec.securityContext.seccompProfile - spec.securityContext.fsGroup - spec.securityContext.fsGroupChangePolicy - spec.securityContext.sysctls - spec.shareProcessNamespace - spec.securityContext.runAsUser - spec.securityContext.runAsGroup - spec.securityContext.supplementalGroups - spec.securityContext.supplementalGroupsPolicy - spec.containers[*].securityContext.appArmorProfile - spec.containers[*].securityContext.seLinuxOptions - spec.containers[*].securityContext.seccompProfile - spec.containers[*].securityContext.capabilities - spec.containers[*].securityContext.readOnlyRootFilesystem - spec.containers[*].securityContext.privileged - spec.containers[*].securityContext.allowPrivilegeEscalation - spec.containers[*].securityContext.procMount - spec.containers[*].securityContext.runAsUser - spec.containers[*].securityContext.runAsGroup", Ref: ref("k8s.io/api/core/v1.PodOS"), }, }, @@ -7894,7 +7974,7 @@ func schema_pkg_apis_serving_v1beta1_PodSpec(ref common.ReferenceCallback) commo }, }, SchemaProps: spec.SchemaProps{ - Description: "SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod.\n\nSchedulingGates can only be set at pod creation time, and be removed only afterwards.\n\nThis is a beta feature enabled by the PodSchedulingReadiness feature gate.", + Description: "SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod.\n\nSchedulingGates can only be set at pod creation time, and be removed only afterwards.", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ @@ -7930,11 +8010,17 @@ func schema_pkg_apis_serving_v1beta1_PodSpec(ref common.ReferenceCallback) commo }, }, }, + "resources": { + SchemaProps: spec.SchemaProps{ + Description: "Resources is the total amount of CPU and Memory resources required by all containers in the pod. It supports specifying Requests and Limits for \"cpu\" and \"memory\" resource names only. ResourceClaims are not supported.\n\nThis field enables fine-grained control over resource allocation for the entire pod, allowing resource sharing among containers in a pod.\n\nThis is an alpha field and requires enabling the PodLevelResources feature gate.", + Ref: ref("k8s.io/api/core/v1.ResourceRequirements"), + }, + }, }, }, }, Dependencies: []string{ - "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.Container", "k8s.io/api/core/v1.EphemeralContainer", "k8s.io/api/core/v1.HostAlias", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodDNSConfig", "k8s.io/api/core/v1.PodOS", "k8s.io/api/core/v1.PodReadinessGate", "k8s.io/api/core/v1.PodResourceClaim", "k8s.io/api/core/v1.PodSchedulingGate", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.Toleration", "k8s.io/api/core/v1.TopologySpreadConstraint", "k8s.io/api/core/v1.Volume", "k8s.io/apimachinery/pkg/api/resource.Quantity"}, + "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.Container", "k8s.io/api/core/v1.EphemeralContainer", "k8s.io/api/core/v1.HostAlias", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodDNSConfig", "k8s.io/api/core/v1.PodOS", "k8s.io/api/core/v1.PodReadinessGate", "k8s.io/api/core/v1.PodResourceClaim", "k8s.io/api/core/v1.PodSchedulingGate", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.ResourceRequirements", "k8s.io/api/core/v1.Toleration", "k8s.io/api/core/v1.TopologySpreadConstraint", "k8s.io/api/core/v1.Volume", "k8s.io/apimachinery/pkg/api/resource.Quantity"}, } } @@ -8371,7 +8457,7 @@ func schema_pkg_apis_serving_v1beta1_PredictorSpec(ref common.ReferenceCallback) }, }, SchemaProps: spec.SchemaProps{ - Description: "List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/", + Description: "List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ @@ -8411,7 +8497,7 @@ func schema_pkg_apis_serving_v1beta1_PredictorSpec(ref common.ReferenceCallback) }, }, SchemaProps: spec.SchemaProps{ - Description: "List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource. This field is beta-level and available on clusters that haven't disabled the EphemeralContainers feature gate.", + Description: "List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource.", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ @@ -8425,7 +8511,7 @@ func schema_pkg_apis_serving_v1beta1_PredictorSpec(ref common.ReferenceCallback) }, "restartPolicy": { SchemaProps: spec.SchemaProps{ - Description: "Restart policy for all containers within the pod. One of Always, OnFailure, Never. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy", + Description: "Restart policy for all containers within the pod. One of Always, OnFailure, Never. In some contexts, only a subset of those values may be permitted. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy", Type: []string{"string"}, Format: "", }, @@ -8481,7 +8567,7 @@ func schema_pkg_apis_serving_v1beta1_PredictorSpec(ref common.ReferenceCallback) }, "serviceAccount": { SchemaProps: spec.SchemaProps{ - Description: "DeprecatedServiceAccount is a depreciated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead.", + Description: "DeprecatedServiceAccount is a deprecated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead.", Type: []string{"string"}, Format: "", }, @@ -8495,7 +8581,7 @@ func schema_pkg_apis_serving_v1beta1_PredictorSpec(ref common.ReferenceCallback) }, "nodeName": { SchemaProps: spec.SchemaProps{ - Description: "NodeName is a request to schedule this pod onto a specific node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits resource requirements.", + Description: "NodeName indicates in which node this pod is scheduled. If empty, this pod is a candidate for scheduling by the scheduler defined in schedulerName. Once this field is set, the kubelet for this node becomes responsible for the lifecycle of this pod. This field should not be used to express a desire for the pod to be scheduled on a specific node. https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodename", Type: []string{"string"}, Format: "", }, @@ -8542,7 +8628,7 @@ func schema_pkg_apis_serving_v1beta1_PredictorSpec(ref common.ReferenceCallback) }, }, SchemaProps: spec.SchemaProps{ - Description: "ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. For example, in the case of docker, only DockerConfig type secrets are honored. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod", + Description: "ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ @@ -8603,7 +8689,7 @@ func schema_pkg_apis_serving_v1beta1_PredictorSpec(ref common.ReferenceCallback) }, }, SchemaProps: spec.SchemaProps{ - Description: "HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. This is only valid for non-hostNetwork pods.", + Description: "HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified.", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ @@ -8651,7 +8737,7 @@ func schema_pkg_apis_serving_v1beta1_PredictorSpec(ref common.ReferenceCallback) }, "runtimeClassName": { SchemaProps: spec.SchemaProps{ - Description: "RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class This is a beta feature as of Kubernetes v1.14.", + Description: "RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class", Type: []string{"string"}, Format: "", }, @@ -8665,14 +8751,14 @@ func schema_pkg_apis_serving_v1beta1_PredictorSpec(ref common.ReferenceCallback) }, "preemptionPolicy": { SchemaProps: spec.SchemaProps{ - Description: "PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset. This field is beta-level, gated by the NonPreemptingPriority feature-gate.", + Description: "PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset.", Type: []string{"string"}, Format: "", }, }, "overhead": { SchemaProps: spec.SchemaProps{ - Description: "Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md This field is beta-level as of Kubernetes v1.18, and is only honored by servers that enable the PodOverhead feature.", + Description: "Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md", Type: []string{"object"}, AdditionalProperties: &spec.SchemaOrBool{ Allows: true, @@ -8718,7 +8804,7 @@ func schema_pkg_apis_serving_v1beta1_PredictorSpec(ref common.ReferenceCallback) }, "os": { SchemaProps: spec.SchemaProps{ - Description: "Specifies the OS of the containers in the pod. Some pod and container fields are restricted if this is set.\n\nIf the OS field is set to linux, the following fields must be unset: -securityContext.windowsOptions\n\nIf the OS field is set to windows, following fields must be unset: - spec.hostPID - spec.hostIPC - spec.securityContext.seLinuxOptions - spec.securityContext.seccompProfile - spec.securityContext.fsGroup - spec.securityContext.fsGroupChangePolicy - spec.securityContext.sysctls - spec.shareProcessNamespace - spec.securityContext.runAsUser - spec.securityContext.runAsGroup - spec.securityContext.supplementalGroups - spec.containers[*].securityContext.seLinuxOptions - spec.containers[*].securityContext.seccompProfile - spec.containers[*].securityContext.capabilities - spec.containers[*].securityContext.readOnlyRootFilesystem - spec.containers[*].securityContext.privileged - spec.containers[*].securityContext.allowPrivilegeEscalation - spec.containers[*].securityContext.procMount - spec.containers[*].securityContext.runAsUser - spec.containers[*].securityContext.runAsGroup This is an alpha field and requires the IdentifyPodOS feature", + Description: "Specifies the OS of the containers in the pod. Some pod and container fields are restricted if this is set.\n\nIf the OS field is set to linux, the following fields must be unset: -securityContext.windowsOptions\n\nIf the OS field is set to windows, following fields must be unset: - spec.hostPID - spec.hostIPC - spec.hostUsers - spec.securityContext.appArmorProfile - spec.securityContext.seLinuxOptions - spec.securityContext.seccompProfile - spec.securityContext.fsGroup - spec.securityContext.fsGroupChangePolicy - spec.securityContext.sysctls - spec.shareProcessNamespace - spec.securityContext.runAsUser - spec.securityContext.runAsGroup - spec.securityContext.supplementalGroups - spec.securityContext.supplementalGroupsPolicy - spec.containers[*].securityContext.appArmorProfile - spec.containers[*].securityContext.seLinuxOptions - spec.containers[*].securityContext.seccompProfile - spec.containers[*].securityContext.capabilities - spec.containers[*].securityContext.readOnlyRootFilesystem - spec.containers[*].securityContext.privileged - spec.containers[*].securityContext.allowPrivilegeEscalation - spec.containers[*].securityContext.procMount - spec.containers[*].securityContext.runAsUser - spec.containers[*].securityContext.runAsGroup", Ref: ref("k8s.io/api/core/v1.PodOS"), }, }, @@ -8741,7 +8827,7 @@ func schema_pkg_apis_serving_v1beta1_PredictorSpec(ref common.ReferenceCallback) }, }, SchemaProps: spec.SchemaProps{ - Description: "SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod.\n\nSchedulingGates can only be set at pod creation time, and be removed only afterwards.\n\nThis is a beta feature enabled by the PodSchedulingReadiness feature gate.", + Description: "SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod.\n\nSchedulingGates can only be set at pod creation time, and be removed only afterwards.", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ @@ -8777,6 +8863,12 @@ func schema_pkg_apis_serving_v1beta1_PredictorSpec(ref common.ReferenceCallback) }, }, }, + "resources": { + SchemaProps: spec.SchemaProps{ + Description: "Resources is the total amount of CPU and Memory resources required by all containers in the pod. It supports specifying Requests and Limits for \"cpu\" and \"memory\" resource names only. ResourceClaims are not supported.\n\nThis field enables fine-grained control over resource allocation for the entire pod, allowing resource sharing among containers in a pod.\n\nThis is an alpha field and requires enabling the PodLevelResources feature gate.", + Ref: ref("k8s.io/api/core/v1.ResourceRequirements"), + }, + }, "minReplicas": { SchemaProps: spec.SchemaProps{ Description: "Minimum number of replicas, defaults to 1 but can be set to 0 to enable scale-to-zero.", @@ -8880,7 +8972,43 @@ func schema_pkg_apis_serving_v1beta1_PredictorSpec(ref common.ReferenceCallback) }, }, Dependencies: []string{ - "github.com/kserve/kserve/pkg/apis/serving/v1beta1.Batcher", "github.com/kserve/kserve/pkg/apis/serving/v1beta1.HuggingFaceRuntimeSpec", "github.com/kserve/kserve/pkg/apis/serving/v1beta1.LightGBMSpec", "github.com/kserve/kserve/pkg/apis/serving/v1beta1.LoggerSpec", "github.com/kserve/kserve/pkg/apis/serving/v1beta1.ModelSpec", "github.com/kserve/kserve/pkg/apis/serving/v1beta1.ONNXRuntimeSpec", "github.com/kserve/kserve/pkg/apis/serving/v1beta1.PMMLSpec", "github.com/kserve/kserve/pkg/apis/serving/v1beta1.PaddleServerSpec", "github.com/kserve/kserve/pkg/apis/serving/v1beta1.SKLearnSpec", "github.com/kserve/kserve/pkg/apis/serving/v1beta1.TFServingSpec", "github.com/kserve/kserve/pkg/apis/serving/v1beta1.TorchServeSpec", "github.com/kserve/kserve/pkg/apis/serving/v1beta1.TritonSpec", "github.com/kserve/kserve/pkg/apis/serving/v1beta1.WorkerSpec", "github.com/kserve/kserve/pkg/apis/serving/v1beta1.XGBoostSpec", "k8s.io/api/apps/v1.DeploymentStrategy", "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.Container", "k8s.io/api/core/v1.EphemeralContainer", "k8s.io/api/core/v1.HostAlias", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodDNSConfig", "k8s.io/api/core/v1.PodOS", "k8s.io/api/core/v1.PodReadinessGate", "k8s.io/api/core/v1.PodResourceClaim", "k8s.io/api/core/v1.PodSchedulingGate", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.Toleration", "k8s.io/api/core/v1.TopologySpreadConstraint", "k8s.io/api/core/v1.Volume", "k8s.io/apimachinery/pkg/api/resource.Quantity"}, + "github.com/kserve/kserve/pkg/apis/serving/v1beta1.Batcher", "github.com/kserve/kserve/pkg/apis/serving/v1beta1.HuggingFaceRuntimeSpec", "github.com/kserve/kserve/pkg/apis/serving/v1beta1.LightGBMSpec", "github.com/kserve/kserve/pkg/apis/serving/v1beta1.LoggerSpec", "github.com/kserve/kserve/pkg/apis/serving/v1beta1.ModelSpec", "github.com/kserve/kserve/pkg/apis/serving/v1beta1.ONNXRuntimeSpec", "github.com/kserve/kserve/pkg/apis/serving/v1beta1.PMMLSpec", "github.com/kserve/kserve/pkg/apis/serving/v1beta1.PaddleServerSpec", "github.com/kserve/kserve/pkg/apis/serving/v1beta1.SKLearnSpec", "github.com/kserve/kserve/pkg/apis/serving/v1beta1.TFServingSpec", "github.com/kserve/kserve/pkg/apis/serving/v1beta1.TorchServeSpec", "github.com/kserve/kserve/pkg/apis/serving/v1beta1.TritonSpec", "github.com/kserve/kserve/pkg/apis/serving/v1beta1.WorkerSpec", "github.com/kserve/kserve/pkg/apis/serving/v1beta1.XGBoostSpec", "k8s.io/api/apps/v1.DeploymentStrategy", "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.Container", "k8s.io/api/core/v1.EphemeralContainer", "k8s.io/api/core/v1.HostAlias", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodDNSConfig", "k8s.io/api/core/v1.PodOS", "k8s.io/api/core/v1.PodReadinessGate", "k8s.io/api/core/v1.PodResourceClaim", "k8s.io/api/core/v1.PodSchedulingGate", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.ResourceRequirements", "k8s.io/api/core/v1.Toleration", "k8s.io/api/core/v1.TopologySpreadConstraint", "k8s.io/api/core/v1.Volume", "k8s.io/apimachinery/pkg/api/resource.Quantity"}, + } +} + +func schema_pkg_apis_serving_v1beta1_ResourceConfig(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "cpuLimit": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "memoryLimit": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "cpuRequest": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "memoryRequest": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, } } @@ -9985,7 +10113,7 @@ func schema_pkg_apis_serving_v1beta1_TransformerSpec(ref common.ReferenceCallbac }, }, SchemaProps: spec.SchemaProps{ - Description: "List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/", + Description: "List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ @@ -10025,7 +10153,7 @@ func schema_pkg_apis_serving_v1beta1_TransformerSpec(ref common.ReferenceCallbac }, }, SchemaProps: spec.SchemaProps{ - Description: "List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource. This field is beta-level and available on clusters that haven't disabled the EphemeralContainers feature gate.", + Description: "List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource.", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ @@ -10039,7 +10167,7 @@ func schema_pkg_apis_serving_v1beta1_TransformerSpec(ref common.ReferenceCallbac }, "restartPolicy": { SchemaProps: spec.SchemaProps{ - Description: "Restart policy for all containers within the pod. One of Always, OnFailure, Never. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy", + Description: "Restart policy for all containers within the pod. One of Always, OnFailure, Never. In some contexts, only a subset of those values may be permitted. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy", Type: []string{"string"}, Format: "", }, @@ -10095,7 +10223,7 @@ func schema_pkg_apis_serving_v1beta1_TransformerSpec(ref common.ReferenceCallbac }, "serviceAccount": { SchemaProps: spec.SchemaProps{ - Description: "DeprecatedServiceAccount is a depreciated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead.", + Description: "DeprecatedServiceAccount is a deprecated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead.", Type: []string{"string"}, Format: "", }, @@ -10109,7 +10237,7 @@ func schema_pkg_apis_serving_v1beta1_TransformerSpec(ref common.ReferenceCallbac }, "nodeName": { SchemaProps: spec.SchemaProps{ - Description: "NodeName is a request to schedule this pod onto a specific node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits resource requirements.", + Description: "NodeName indicates in which node this pod is scheduled. If empty, this pod is a candidate for scheduling by the scheduler defined in schedulerName. Once this field is set, the kubelet for this node becomes responsible for the lifecycle of this pod. This field should not be used to express a desire for the pod to be scheduled on a specific node. https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodename", Type: []string{"string"}, Format: "", }, @@ -10156,7 +10284,7 @@ func schema_pkg_apis_serving_v1beta1_TransformerSpec(ref common.ReferenceCallbac }, }, SchemaProps: spec.SchemaProps{ - Description: "ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. For example, in the case of docker, only DockerConfig type secrets are honored. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod", + Description: "ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ @@ -10217,7 +10345,7 @@ func schema_pkg_apis_serving_v1beta1_TransformerSpec(ref common.ReferenceCallbac }, }, SchemaProps: spec.SchemaProps{ - Description: "HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. This is only valid for non-hostNetwork pods.", + Description: "HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified.", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ @@ -10265,7 +10393,7 @@ func schema_pkg_apis_serving_v1beta1_TransformerSpec(ref common.ReferenceCallbac }, "runtimeClassName": { SchemaProps: spec.SchemaProps{ - Description: "RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class This is a beta feature as of Kubernetes v1.14.", + Description: "RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class", Type: []string{"string"}, Format: "", }, @@ -10279,14 +10407,14 @@ func schema_pkg_apis_serving_v1beta1_TransformerSpec(ref common.ReferenceCallbac }, "preemptionPolicy": { SchemaProps: spec.SchemaProps{ - Description: "PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset. This field is beta-level, gated by the NonPreemptingPriority feature-gate.", + Description: "PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset.", Type: []string{"string"}, Format: "", }, }, "overhead": { SchemaProps: spec.SchemaProps{ - Description: "Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md This field is beta-level as of Kubernetes v1.18, and is only honored by servers that enable the PodOverhead feature.", + Description: "Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md", Type: []string{"object"}, AdditionalProperties: &spec.SchemaOrBool{ Allows: true, @@ -10332,7 +10460,7 @@ func schema_pkg_apis_serving_v1beta1_TransformerSpec(ref common.ReferenceCallbac }, "os": { SchemaProps: spec.SchemaProps{ - Description: "Specifies the OS of the containers in the pod. Some pod and container fields are restricted if this is set.\n\nIf the OS field is set to linux, the following fields must be unset: -securityContext.windowsOptions\n\nIf the OS field is set to windows, following fields must be unset: - spec.hostPID - spec.hostIPC - spec.securityContext.seLinuxOptions - spec.securityContext.seccompProfile - spec.securityContext.fsGroup - spec.securityContext.fsGroupChangePolicy - spec.securityContext.sysctls - spec.shareProcessNamespace - spec.securityContext.runAsUser - spec.securityContext.runAsGroup - spec.securityContext.supplementalGroups - spec.containers[*].securityContext.seLinuxOptions - spec.containers[*].securityContext.seccompProfile - spec.containers[*].securityContext.capabilities - spec.containers[*].securityContext.readOnlyRootFilesystem - spec.containers[*].securityContext.privileged - spec.containers[*].securityContext.allowPrivilegeEscalation - spec.containers[*].securityContext.procMount - spec.containers[*].securityContext.runAsUser - spec.containers[*].securityContext.runAsGroup This is an alpha field and requires the IdentifyPodOS feature", + Description: "Specifies the OS of the containers in the pod. Some pod and container fields are restricted if this is set.\n\nIf the OS field is set to linux, the following fields must be unset: -securityContext.windowsOptions\n\nIf the OS field is set to windows, following fields must be unset: - spec.hostPID - spec.hostIPC - spec.hostUsers - spec.securityContext.appArmorProfile - spec.securityContext.seLinuxOptions - spec.securityContext.seccompProfile - spec.securityContext.fsGroup - spec.securityContext.fsGroupChangePolicy - spec.securityContext.sysctls - spec.shareProcessNamespace - spec.securityContext.runAsUser - spec.securityContext.runAsGroup - spec.securityContext.supplementalGroups - spec.securityContext.supplementalGroupsPolicy - spec.containers[*].securityContext.appArmorProfile - spec.containers[*].securityContext.seLinuxOptions - spec.containers[*].securityContext.seccompProfile - spec.containers[*].securityContext.capabilities - spec.containers[*].securityContext.readOnlyRootFilesystem - spec.containers[*].securityContext.privileged - spec.containers[*].securityContext.allowPrivilegeEscalation - spec.containers[*].securityContext.procMount - spec.containers[*].securityContext.runAsUser - spec.containers[*].securityContext.runAsGroup", Ref: ref("k8s.io/api/core/v1.PodOS"), }, }, @@ -10355,7 +10483,7 @@ func schema_pkg_apis_serving_v1beta1_TransformerSpec(ref common.ReferenceCallbac }, }, SchemaProps: spec.SchemaProps{ - Description: "SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod.\n\nSchedulingGates can only be set at pod creation time, and be removed only afterwards.\n\nThis is a beta feature enabled by the PodSchedulingReadiness feature gate.", + Description: "SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod.\n\nSchedulingGates can only be set at pod creation time, and be removed only afterwards.", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ @@ -10391,6 +10519,12 @@ func schema_pkg_apis_serving_v1beta1_TransformerSpec(ref common.ReferenceCallbac }, }, }, + "resources": { + SchemaProps: spec.SchemaProps{ + Description: "Resources is the total amount of CPU and Memory resources required by all containers in the pod. It supports specifying Requests and Limits for \"cpu\" and \"memory\" resource names only. ResourceClaims are not supported.\n\nThis field enables fine-grained control over resource allocation for the entire pod, allowing resource sharing among containers in a pod.\n\nThis is an alpha field and requires enabling the PodLevelResources feature gate.", + Ref: ref("k8s.io/api/core/v1.ResourceRequirements"), + }, + }, "minReplicas": { SchemaProps: spec.SchemaProps{ Description: "Minimum number of replicas, defaults to 1 but can be set to 0 to enable scale-to-zero.", @@ -10494,7 +10628,7 @@ func schema_pkg_apis_serving_v1beta1_TransformerSpec(ref common.ReferenceCallbac }, }, Dependencies: []string{ - "github.com/kserve/kserve/pkg/apis/serving/v1beta1.Batcher", "github.com/kserve/kserve/pkg/apis/serving/v1beta1.LoggerSpec", "k8s.io/api/apps/v1.DeploymentStrategy", "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.Container", "k8s.io/api/core/v1.EphemeralContainer", "k8s.io/api/core/v1.HostAlias", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodDNSConfig", "k8s.io/api/core/v1.PodOS", "k8s.io/api/core/v1.PodReadinessGate", "k8s.io/api/core/v1.PodResourceClaim", "k8s.io/api/core/v1.PodSchedulingGate", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.Toleration", "k8s.io/api/core/v1.TopologySpreadConstraint", "k8s.io/api/core/v1.Volume", "k8s.io/apimachinery/pkg/api/resource.Quantity"}, + "github.com/kserve/kserve/pkg/apis/serving/v1beta1.Batcher", "github.com/kserve/kserve/pkg/apis/serving/v1beta1.LoggerSpec", "k8s.io/api/apps/v1.DeploymentStrategy", "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.Container", "k8s.io/api/core/v1.EphemeralContainer", "k8s.io/api/core/v1.HostAlias", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodDNSConfig", "k8s.io/api/core/v1.PodOS", "k8s.io/api/core/v1.PodReadinessGate", "k8s.io/api/core/v1.PodResourceClaim", "k8s.io/api/core/v1.PodSchedulingGate", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.ResourceRequirements", "k8s.io/api/core/v1.Toleration", "k8s.io/api/core/v1.TopologySpreadConstraint", "k8s.io/api/core/v1.Volume", "k8s.io/apimachinery/pkg/api/resource.Quantity"}, } } @@ -10858,7 +10992,7 @@ func schema_pkg_apis_serving_v1beta1_WorkerSpec(ref common.ReferenceCallback) co }, }, SchemaProps: spec.SchemaProps{ - Description: "List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/", + Description: "List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ @@ -10898,7 +11032,7 @@ func schema_pkg_apis_serving_v1beta1_WorkerSpec(ref common.ReferenceCallback) co }, }, SchemaProps: spec.SchemaProps{ - Description: "List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource. This field is beta-level and available on clusters that haven't disabled the EphemeralContainers feature gate.", + Description: "List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource.", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ @@ -10912,7 +11046,7 @@ func schema_pkg_apis_serving_v1beta1_WorkerSpec(ref common.ReferenceCallback) co }, "restartPolicy": { SchemaProps: spec.SchemaProps{ - Description: "Restart policy for all containers within the pod. One of Always, OnFailure, Never. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy", + Description: "Restart policy for all containers within the pod. One of Always, OnFailure, Never. In some contexts, only a subset of those values may be permitted. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy", Type: []string{"string"}, Format: "", }, @@ -10968,7 +11102,7 @@ func schema_pkg_apis_serving_v1beta1_WorkerSpec(ref common.ReferenceCallback) co }, "serviceAccount": { SchemaProps: spec.SchemaProps{ - Description: "DeprecatedServiceAccount is a depreciated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead.", + Description: "DeprecatedServiceAccount is a deprecated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead.", Type: []string{"string"}, Format: "", }, @@ -10982,7 +11116,7 @@ func schema_pkg_apis_serving_v1beta1_WorkerSpec(ref common.ReferenceCallback) co }, "nodeName": { SchemaProps: spec.SchemaProps{ - Description: "NodeName is a request to schedule this pod onto a specific node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits resource requirements.", + Description: "NodeName indicates in which node this pod is scheduled. If empty, this pod is a candidate for scheduling by the scheduler defined in schedulerName. Once this field is set, the kubelet for this node becomes responsible for the lifecycle of this pod. This field should not be used to express a desire for the pod to be scheduled on a specific node. https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodename", Type: []string{"string"}, Format: "", }, @@ -11029,7 +11163,7 @@ func schema_pkg_apis_serving_v1beta1_WorkerSpec(ref common.ReferenceCallback) co }, }, SchemaProps: spec.SchemaProps{ - Description: "ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. For example, in the case of docker, only DockerConfig type secrets are honored. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod", + Description: "ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ @@ -11090,7 +11224,7 @@ func schema_pkg_apis_serving_v1beta1_WorkerSpec(ref common.ReferenceCallback) co }, }, SchemaProps: spec.SchemaProps{ - Description: "HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. This is only valid for non-hostNetwork pods.", + Description: "HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified.", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ @@ -11138,7 +11272,7 @@ func schema_pkg_apis_serving_v1beta1_WorkerSpec(ref common.ReferenceCallback) co }, "runtimeClassName": { SchemaProps: spec.SchemaProps{ - Description: "RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class This is a beta feature as of Kubernetes v1.14.", + Description: "RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class", Type: []string{"string"}, Format: "", }, @@ -11152,14 +11286,14 @@ func schema_pkg_apis_serving_v1beta1_WorkerSpec(ref common.ReferenceCallback) co }, "preemptionPolicy": { SchemaProps: spec.SchemaProps{ - Description: "PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset. This field is beta-level, gated by the NonPreemptingPriority feature-gate.", + Description: "PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset.", Type: []string{"string"}, Format: "", }, }, "overhead": { SchemaProps: spec.SchemaProps{ - Description: "Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md This field is beta-level as of Kubernetes v1.18, and is only honored by servers that enable the PodOverhead feature.", + Description: "Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md", Type: []string{"object"}, AdditionalProperties: &spec.SchemaOrBool{ Allows: true, @@ -11205,7 +11339,7 @@ func schema_pkg_apis_serving_v1beta1_WorkerSpec(ref common.ReferenceCallback) co }, "os": { SchemaProps: spec.SchemaProps{ - Description: "Specifies the OS of the containers in the pod. Some pod and container fields are restricted if this is set.\n\nIf the OS field is set to linux, the following fields must be unset: -securityContext.windowsOptions\n\nIf the OS field is set to windows, following fields must be unset: - spec.hostPID - spec.hostIPC - spec.securityContext.seLinuxOptions - spec.securityContext.seccompProfile - spec.securityContext.fsGroup - spec.securityContext.fsGroupChangePolicy - spec.securityContext.sysctls - spec.shareProcessNamespace - spec.securityContext.runAsUser - spec.securityContext.runAsGroup - spec.securityContext.supplementalGroups - spec.containers[*].securityContext.seLinuxOptions - spec.containers[*].securityContext.seccompProfile - spec.containers[*].securityContext.capabilities - spec.containers[*].securityContext.readOnlyRootFilesystem - spec.containers[*].securityContext.privileged - spec.containers[*].securityContext.allowPrivilegeEscalation - spec.containers[*].securityContext.procMount - spec.containers[*].securityContext.runAsUser - spec.containers[*].securityContext.runAsGroup This is an alpha field and requires the IdentifyPodOS feature", + Description: "Specifies the OS of the containers in the pod. Some pod and container fields are restricted if this is set.\n\nIf the OS field is set to linux, the following fields must be unset: -securityContext.windowsOptions\n\nIf the OS field is set to windows, following fields must be unset: - spec.hostPID - spec.hostIPC - spec.hostUsers - spec.securityContext.appArmorProfile - spec.securityContext.seLinuxOptions - spec.securityContext.seccompProfile - spec.securityContext.fsGroup - spec.securityContext.fsGroupChangePolicy - spec.securityContext.sysctls - spec.shareProcessNamespace - spec.securityContext.runAsUser - spec.securityContext.runAsGroup - spec.securityContext.supplementalGroups - spec.securityContext.supplementalGroupsPolicy - spec.containers[*].securityContext.appArmorProfile - spec.containers[*].securityContext.seLinuxOptions - spec.containers[*].securityContext.seccompProfile - spec.containers[*].securityContext.capabilities - spec.containers[*].securityContext.readOnlyRootFilesystem - spec.containers[*].securityContext.privileged - spec.containers[*].securityContext.allowPrivilegeEscalation - spec.containers[*].securityContext.procMount - spec.containers[*].securityContext.runAsUser - spec.containers[*].securityContext.runAsGroup", Ref: ref("k8s.io/api/core/v1.PodOS"), }, }, @@ -11228,7 +11362,7 @@ func schema_pkg_apis_serving_v1beta1_WorkerSpec(ref common.ReferenceCallback) co }, }, SchemaProps: spec.SchemaProps{ - Description: "SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod.\n\nSchedulingGates can only be set at pod creation time, and be removed only afterwards.\n\nThis is a beta feature enabled by the PodSchedulingReadiness feature gate.", + Description: "SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod.\n\nSchedulingGates can only be set at pod creation time, and be removed only afterwards.", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ @@ -11264,6 +11398,12 @@ func schema_pkg_apis_serving_v1beta1_WorkerSpec(ref common.ReferenceCallback) co }, }, }, + "resources": { + SchemaProps: spec.SchemaProps{ + Description: "Resources is the total amount of CPU and Memory resources required by all containers in the pod. It supports specifying Requests and Limits for \"cpu\" and \"memory\" resource names only. ResourceClaims are not supported.\n\nThis field enables fine-grained control over resource allocation for the entire pod, allowing resource sharing among containers in a pod.\n\nThis is an alpha field and requires enabling the PodLevelResources feature gate.", + Ref: ref("k8s.io/api/core/v1.ResourceRequirements"), + }, + }, "pipelineParallelSize": { SchemaProps: spec.SchemaProps{ Description: "PipelineParallelSize defines the number of parallel workers. It also represents the number of replicas in the worker set, where each worker set serves as a scaling unit.", @@ -11282,7 +11422,7 @@ func schema_pkg_apis_serving_v1beta1_WorkerSpec(ref common.ReferenceCallback) co }, }, Dependencies: []string{ - "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.Container", "k8s.io/api/core/v1.EphemeralContainer", "k8s.io/api/core/v1.HostAlias", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodDNSConfig", "k8s.io/api/core/v1.PodOS", "k8s.io/api/core/v1.PodReadinessGate", "k8s.io/api/core/v1.PodResourceClaim", "k8s.io/api/core/v1.PodSchedulingGate", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.Toleration", "k8s.io/api/core/v1.TopologySpreadConstraint", "k8s.io/api/core/v1.Volume", "k8s.io/apimachinery/pkg/api/resource.Quantity"}, + "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.Container", "k8s.io/api/core/v1.EphemeralContainer", "k8s.io/api/core/v1.HostAlias", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodDNSConfig", "k8s.io/api/core/v1.PodOS", "k8s.io/api/core/v1.PodReadinessGate", "k8s.io/api/core/v1.PodResourceClaim", "k8s.io/api/core/v1.PodSchedulingGate", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.ResourceRequirements", "k8s.io/api/core/v1.Toleration", "k8s.io/api/core/v1.TopologySpreadConstraint", "k8s.io/api/core/v1.Volume", "k8s.io/apimachinery/pkg/api/resource.Quantity"}, } } diff --git a/pkg/openapi/swagger.json b/pkg/openapi/swagger.json index 428586dbed8..455bc814951 100644 --- a/pkg/openapi/swagger.json +++ b/pkg/openapi/swagger.json @@ -1475,7 +1475,7 @@ "x-kubernetes-patch-strategy": "merge" }, "nodeName": { - "description": "NodeName is a request to schedule this pod onto a specific node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits resource requirements.", + "description": "NodeName indicates in which node this pod is scheduled. If empty, this pod is a candidate for scheduling by the scheduler defined in schedulerName. Once this field is set, the kubelet for this node becomes responsible for the lifecycle of this pod. This field should not be used to express a desire for the pod to be scheduled on a specific node. https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodename", "type": "string" }, "nodeSelector": { @@ -1488,7 +1488,7 @@ "x-kubernetes-map-type": "atomic" }, "os": { - "description": "Specifies the OS of the containers in the pod. Some pod and container fields are restricted if this is set.\n\nIf the OS field is set to linux, the following fields must be unset: -securityContext.windowsOptions\n\nIf the OS field is set to windows, following fields must be unset: - spec.hostPID - spec.hostIPC - spec.hostUsers - spec.securityContext.appArmorProfile - spec.securityContext.seLinuxOptions - spec.securityContext.seccompProfile - spec.securityContext.fsGroup - spec.securityContext.fsGroupChangePolicy - spec.securityContext.sysctls - spec.shareProcessNamespace - spec.securityContext.runAsUser - spec.securityContext.runAsGroup - spec.securityContext.supplementalGroups - spec.containers[*].securityContext.appArmorProfile - spec.containers[*].securityContext.seLinuxOptions - spec.containers[*].securityContext.seccompProfile - spec.containers[*].securityContext.capabilities - spec.containers[*].securityContext.readOnlyRootFilesystem - spec.containers[*].securityContext.privileged - spec.containers[*].securityContext.allowPrivilegeEscalation - spec.containers[*].securityContext.procMount - spec.containers[*].securityContext.runAsUser - spec.containers[*].securityContext.runAsGroup", + "description": "Specifies the OS of the containers in the pod. Some pod and container fields are restricted if this is set.\n\nIf the OS field is set to linux, the following fields must be unset: -securityContext.windowsOptions\n\nIf the OS field is set to windows, following fields must be unset: - spec.hostPID - spec.hostIPC - spec.hostUsers - spec.securityContext.appArmorProfile - spec.securityContext.seLinuxOptions - spec.securityContext.seccompProfile - spec.securityContext.fsGroup - spec.securityContext.fsGroupChangePolicy - spec.securityContext.sysctls - spec.shareProcessNamespace - spec.securityContext.runAsUser - spec.securityContext.runAsGroup - spec.securityContext.supplementalGroups - spec.securityContext.supplementalGroupsPolicy - spec.containers[*].securityContext.appArmorProfile - spec.containers[*].securityContext.seLinuxOptions - spec.containers[*].securityContext.seccompProfile - spec.containers[*].securityContext.capabilities - spec.containers[*].securityContext.readOnlyRootFilesystem - spec.containers[*].securityContext.privileged - spec.containers[*].securityContext.allowPrivilegeEscalation - spec.containers[*].securityContext.procMount - spec.containers[*].securityContext.runAsUser - spec.containers[*].securityContext.runAsGroup", "$ref": "#/definitions/v1.PodOS" }, "overhead": { @@ -1534,6 +1534,10 @@ "x-kubernetes-patch-merge-key": "name", "x-kubernetes-patch-strategy": "merge,retainKeys" }, + "resources": { + "description": "Resources is the total amount of CPU and Memory resources required by all containers in the pod. It supports specifying Requests and Limits for \"cpu\" and \"memory\" resource names only. ResourceClaims are not supported.\n\nThis field enables fine-grained control over resource allocation for the entire pod, allowing resource sharing among containers in a pod.\n\nThis is an alpha field and requires enabling the PodLevelResources feature gate.", + "$ref": "#/definitions/v1.ResourceRequirements" + }, "restartPolicy": { "description": "Restart policy for all containers within the pod. One of Always, OnFailure, Never. In some contexts, only a subset of those values may be permitted. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy", "type": "string" @@ -1752,7 +1756,7 @@ "x-kubernetes-patch-strategy": "merge" }, "nodeName": { - "description": "NodeName is a request to schedule this pod onto a specific node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits resource requirements.", + "description": "NodeName indicates in which node this pod is scheduled. If empty, this pod is a candidate for scheduling by the scheduler defined in schedulerName. Once this field is set, the kubelet for this node becomes responsible for the lifecycle of this pod. This field should not be used to express a desire for the pod to be scheduled on a specific node. https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodename", "type": "string" }, "nodeSelector": { @@ -1765,7 +1769,7 @@ "x-kubernetes-map-type": "atomic" }, "os": { - "description": "Specifies the OS of the containers in the pod. Some pod and container fields are restricted if this is set.\n\nIf the OS field is set to linux, the following fields must be unset: -securityContext.windowsOptions\n\nIf the OS field is set to windows, following fields must be unset: - spec.hostPID - spec.hostIPC - spec.hostUsers - spec.securityContext.appArmorProfile - spec.securityContext.seLinuxOptions - spec.securityContext.seccompProfile - spec.securityContext.fsGroup - spec.securityContext.fsGroupChangePolicy - spec.securityContext.sysctls - spec.shareProcessNamespace - spec.securityContext.runAsUser - spec.securityContext.runAsGroup - spec.securityContext.supplementalGroups - spec.containers[*].securityContext.appArmorProfile - spec.containers[*].securityContext.seLinuxOptions - spec.containers[*].securityContext.seccompProfile - spec.containers[*].securityContext.capabilities - spec.containers[*].securityContext.readOnlyRootFilesystem - spec.containers[*].securityContext.privileged - spec.containers[*].securityContext.allowPrivilegeEscalation - spec.containers[*].securityContext.procMount - spec.containers[*].securityContext.runAsUser - spec.containers[*].securityContext.runAsGroup", + "description": "Specifies the OS of the containers in the pod. Some pod and container fields are restricted if this is set.\n\nIf the OS field is set to linux, the following fields must be unset: -securityContext.windowsOptions\n\nIf the OS field is set to windows, following fields must be unset: - spec.hostPID - spec.hostIPC - spec.hostUsers - spec.securityContext.appArmorProfile - spec.securityContext.seLinuxOptions - spec.securityContext.seccompProfile - spec.securityContext.fsGroup - spec.securityContext.fsGroupChangePolicy - spec.securityContext.sysctls - spec.shareProcessNamespace - spec.securityContext.runAsUser - spec.securityContext.runAsGroup - spec.securityContext.supplementalGroups - spec.securityContext.supplementalGroupsPolicy - spec.containers[*].securityContext.appArmorProfile - spec.containers[*].securityContext.seLinuxOptions - spec.containers[*].securityContext.seccompProfile - spec.containers[*].securityContext.capabilities - spec.containers[*].securityContext.readOnlyRootFilesystem - spec.containers[*].securityContext.privileged - spec.containers[*].securityContext.allowPrivilegeEscalation - spec.containers[*].securityContext.procMount - spec.containers[*].securityContext.runAsUser - spec.containers[*].securityContext.runAsGroup", "$ref": "#/definitions/v1.PodOS" }, "overhead": { @@ -1811,6 +1815,10 @@ "x-kubernetes-patch-merge-key": "name", "x-kubernetes-patch-strategy": "merge,retainKeys" }, + "resources": { + "description": "Resources is the total amount of CPU and Memory resources required by all containers in the pod. It supports specifying Requests and Limits for \"cpu\" and \"memory\" resource names only. ResourceClaims are not supported.\n\nThis field enables fine-grained control over resource allocation for the entire pod, allowing resource sharing among containers in a pod.\n\nThis is an alpha field and requires enabling the PodLevelResources feature gate.", + "$ref": "#/definitions/v1.ResourceRequirements" + }, "restartPolicy": { "description": "Restart policy for all containers within the pod. One of Always, OnFailure, Never. In some contexts, only a subset of those values may be permitted. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy", "type": "string" @@ -2029,7 +2037,7 @@ "x-kubernetes-patch-strategy": "merge" }, "nodeName": { - "description": "NodeName is a request to schedule this pod onto a specific node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits resource requirements.", + "description": "NodeName indicates in which node this pod is scheduled. If empty, this pod is a candidate for scheduling by the scheduler defined in schedulerName. Once this field is set, the kubelet for this node becomes responsible for the lifecycle of this pod. This field should not be used to express a desire for the pod to be scheduled on a specific node. https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodename", "type": "string" }, "nodeSelector": { @@ -2042,7 +2050,7 @@ "x-kubernetes-map-type": "atomic" }, "os": { - "description": "Specifies the OS of the containers in the pod. Some pod and container fields are restricted if this is set.\n\nIf the OS field is set to linux, the following fields must be unset: -securityContext.windowsOptions\n\nIf the OS field is set to windows, following fields must be unset: - spec.hostPID - spec.hostIPC - spec.hostUsers - spec.securityContext.appArmorProfile - spec.securityContext.seLinuxOptions - spec.securityContext.seccompProfile - spec.securityContext.fsGroup - spec.securityContext.fsGroupChangePolicy - spec.securityContext.sysctls - spec.shareProcessNamespace - spec.securityContext.runAsUser - spec.securityContext.runAsGroup - spec.securityContext.supplementalGroups - spec.containers[*].securityContext.appArmorProfile - spec.containers[*].securityContext.seLinuxOptions - spec.containers[*].securityContext.seccompProfile - spec.containers[*].securityContext.capabilities - spec.containers[*].securityContext.readOnlyRootFilesystem - spec.containers[*].securityContext.privileged - spec.containers[*].securityContext.allowPrivilegeEscalation - spec.containers[*].securityContext.procMount - spec.containers[*].securityContext.runAsUser - spec.containers[*].securityContext.runAsGroup", + "description": "Specifies the OS of the containers in the pod. Some pod and container fields are restricted if this is set.\n\nIf the OS field is set to linux, the following fields must be unset: -securityContext.windowsOptions\n\nIf the OS field is set to windows, following fields must be unset: - spec.hostPID - spec.hostIPC - spec.hostUsers - spec.securityContext.appArmorProfile - spec.securityContext.seLinuxOptions - spec.securityContext.seccompProfile - spec.securityContext.fsGroup - spec.securityContext.fsGroupChangePolicy - spec.securityContext.sysctls - spec.shareProcessNamespace - spec.securityContext.runAsUser - spec.securityContext.runAsGroup - spec.securityContext.supplementalGroups - spec.securityContext.supplementalGroupsPolicy - spec.containers[*].securityContext.appArmorProfile - spec.containers[*].securityContext.seLinuxOptions - spec.containers[*].securityContext.seccompProfile - spec.containers[*].securityContext.capabilities - spec.containers[*].securityContext.readOnlyRootFilesystem - spec.containers[*].securityContext.privileged - spec.containers[*].securityContext.allowPrivilegeEscalation - spec.containers[*].securityContext.procMount - spec.containers[*].securityContext.runAsUser - spec.containers[*].securityContext.runAsGroup", "$ref": "#/definitions/v1.PodOS" }, "overhead": { @@ -2088,6 +2096,10 @@ "x-kubernetes-patch-merge-key": "name", "x-kubernetes-patch-strategy": "merge,retainKeys" }, + "resources": { + "description": "Resources is the total amount of CPU and Memory resources required by all containers in the pod. It supports specifying Requests and Limits for \"cpu\" and \"memory\" resource names only. ResourceClaims are not supported.\n\nThis field enables fine-grained control over resource allocation for the entire pod, allowing resource sharing among containers in a pod.\n\nThis is an alpha field and requires enabling the PodLevelResources feature gate.", + "$ref": "#/definitions/v1.ResourceRequirements" + }, "restartPolicy": { "description": "Restart policy for all containers within the pod. One of Always, OnFailure, Never. In some contexts, only a subset of those values may be permitted. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy", "type": "string" @@ -2465,7 +2477,7 @@ "type": "boolean" }, "ephemeralContainers": { - "description": "List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource. This field is beta-level and available on clusters that haven't disabled the EphemeralContainers feature gate.", + "description": "List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource.", "type": "array", "items": { "default": {}, @@ -2475,7 +2487,7 @@ "x-kubernetes-patch-strategy": "merge" }, "hostAliases": { - "description": "HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. This is only valid for non-hostNetwork pods.", + "description": "HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified.", "type": "array", "items": { "default": {}, @@ -2505,7 +2517,7 @@ "type": "string" }, "imagePullSecrets": { - "description": "ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. For example, in the case of docker, only DockerConfig type secrets are honored. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod", + "description": "ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod", "type": "array", "items": { "default": {}, @@ -2515,7 +2527,7 @@ "x-kubernetes-patch-strategy": "merge" }, "initContainers": { - "description": "List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/", + "description": "List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/", "type": "array", "items": { "default": {}, @@ -2547,7 +2559,7 @@ "format": "int32" }, "nodeName": { - "description": "NodeName is a request to schedule this pod onto a specific node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits resource requirements.", + "description": "NodeName indicates in which node this pod is scheduled. If empty, this pod is a candidate for scheduling by the scheduler defined in schedulerName. Once this field is set, the kubelet for this node becomes responsible for the lifecycle of this pod. This field should not be used to express a desire for the pod to be scheduled on a specific node. https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodename", "type": "string" }, "nodeSelector": { @@ -2560,18 +2572,18 @@ "x-kubernetes-map-type": "atomic" }, "os": { - "description": "Specifies the OS of the containers in the pod. Some pod and container fields are restricted if this is set.\n\nIf the OS field is set to linux, the following fields must be unset: -securityContext.windowsOptions\n\nIf the OS field is set to windows, following fields must be unset: - spec.hostPID - spec.hostIPC - spec.securityContext.seLinuxOptions - spec.securityContext.seccompProfile - spec.securityContext.fsGroup - spec.securityContext.fsGroupChangePolicy - spec.securityContext.sysctls - spec.shareProcessNamespace - spec.securityContext.runAsUser - spec.securityContext.runAsGroup - spec.securityContext.supplementalGroups - spec.containers[*].securityContext.seLinuxOptions - spec.containers[*].securityContext.seccompProfile - spec.containers[*].securityContext.capabilities - spec.containers[*].securityContext.readOnlyRootFilesystem - spec.containers[*].securityContext.privileged - spec.containers[*].securityContext.allowPrivilegeEscalation - spec.containers[*].securityContext.procMount - spec.containers[*].securityContext.runAsUser - spec.containers[*].securityContext.runAsGroup This is an alpha field and requires the IdentifyPodOS feature", + "description": "Specifies the OS of the containers in the pod. Some pod and container fields are restricted if this is set.\n\nIf the OS field is set to linux, the following fields must be unset: -securityContext.windowsOptions\n\nIf the OS field is set to windows, following fields must be unset: - spec.hostPID - spec.hostIPC - spec.hostUsers - spec.securityContext.appArmorProfile - spec.securityContext.seLinuxOptions - spec.securityContext.seccompProfile - spec.securityContext.fsGroup - spec.securityContext.fsGroupChangePolicy - spec.securityContext.sysctls - spec.shareProcessNamespace - spec.securityContext.runAsUser - spec.securityContext.runAsGroup - spec.securityContext.supplementalGroups - spec.securityContext.supplementalGroupsPolicy - spec.containers[*].securityContext.appArmorProfile - spec.containers[*].securityContext.seLinuxOptions - spec.containers[*].securityContext.seccompProfile - spec.containers[*].securityContext.capabilities - spec.containers[*].securityContext.readOnlyRootFilesystem - spec.containers[*].securityContext.privileged - spec.containers[*].securityContext.allowPrivilegeEscalation - spec.containers[*].securityContext.procMount - spec.containers[*].securityContext.runAsUser - spec.containers[*].securityContext.runAsGroup", "$ref": "#/definitions/v1.PodOS" }, "overhead": { - "description": "Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md This field is beta-level as of Kubernetes v1.18, and is only honored by servers that enable the PodOverhead feature.", + "description": "Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md", "type": "object", "additionalProperties": { "$ref": "#/definitions/resource.Quantity" } }, "preemptionPolicy": { - "description": "PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset. This field is beta-level, gated by the NonPreemptingPriority feature-gate.", + "description": "PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset.", "type": "string" }, "priority": { @@ -2605,12 +2617,16 @@ "x-kubernetes-patch-merge-key": "name", "x-kubernetes-patch-strategy": "merge,retainKeys" }, + "resources": { + "description": "Resources is the total amount of CPU and Memory resources required by all containers in the pod. It supports specifying Requests and Limits for \"cpu\" and \"memory\" resource names only. ResourceClaims are not supported.\n\nThis field enables fine-grained control over resource allocation for the entire pod, allowing resource sharing among containers in a pod.\n\nThis is an alpha field and requires enabling the PodLevelResources feature gate.", + "$ref": "#/definitions/v1.ResourceRequirements" + }, "restartPolicy": { - "description": "Restart policy for all containers within the pod. One of Always, OnFailure, Never. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy", + "description": "Restart policy for all containers within the pod. One of Always, OnFailure, Never. In some contexts, only a subset of those values may be permitted. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy", "type": "string" }, "runtimeClassName": { - "description": "RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class This is a beta feature as of Kubernetes v1.14.", + "description": "RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class", "type": "string" }, "scaleMetric": { @@ -2627,7 +2643,7 @@ "type": "string" }, "schedulingGates": { - "description": "SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod.\n\nSchedulingGates can only be set at pod creation time, and be removed only afterwards.\n\nThis is a beta feature enabled by the PodSchedulingReadiness feature gate.", + "description": "SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod.\n\nSchedulingGates can only be set at pod creation time, and be removed only afterwards.", "type": "array", "items": { "default": {}, @@ -2645,7 +2661,7 @@ "$ref": "#/definitions/v1.PodSecurityContext" }, "serviceAccount": { - "description": "DeprecatedServiceAccount is a depreciated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead.", + "description": "DeprecatedServiceAccount is a deprecated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead.", "type": "string" }, "serviceAccountName": { @@ -3066,6 +3082,27 @@ "description": "Explainer configurations", "default": {}, "$ref": "#/definitions/v1beta1.ExplainersConfig" + }, + "resource": { + "description": "Resource configurations", + "default": {}, + "$ref": "#/definitions/v1beta1.ResourceConfig" + }, + "serviceAnnotationDisallowedList": { + "description": "ServiceAnnotationDisallowedList is a list of annotations that are not allowed to be propagated to Knative revisions", + "type": "array", + "items": { + "type": "string", + "default": "" + } + }, + "serviceLabelDisallowedList": { + "description": "ServiceLabelDisallowedList is a list of labels that are not allowed to be propagated to Knative revisions", + "type": "array", + "items": { + "type": "string", + "default": "" + } } } }, @@ -3088,6 +3125,9 @@ "domainTemplate": { "type": "string" }, + "enableGatewayApi": { + "type": "boolean" + }, "ingressClassName": { "type": "string" }, @@ -3100,6 +3140,9 @@ "knativeLocalGatewayService": { "type": "string" }, + "kserveIngressGateway": { + "type": "string" + }, "localGateway": { "type": "string" }, @@ -3305,6 +3348,9 @@ "defaultJobImage": { "type": "string" }, + "disableVolumeManagement": { + "type": "boolean" + }, "enabled": { "type": "boolean", "default": false @@ -4197,7 +4243,7 @@ "type": "boolean" }, "ephemeralContainers": { - "description": "List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource. This field is beta-level and available on clusters that haven't disabled the EphemeralContainers feature gate.", + "description": "List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource.", "type": "array", "items": { "default": {}, @@ -4207,7 +4253,7 @@ "x-kubernetes-patch-strategy": "merge" }, "hostAliases": { - "description": "HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. This is only valid for non-hostNetwork pods.", + "description": "HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified.", "type": "array", "items": { "default": {}, @@ -4237,7 +4283,7 @@ "type": "string" }, "imagePullSecrets": { - "description": "ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. For example, in the case of docker, only DockerConfig type secrets are honored. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod", + "description": "ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod", "type": "array", "items": { "default": {}, @@ -4247,7 +4293,7 @@ "x-kubernetes-patch-strategy": "merge" }, "initContainers": { - "description": "List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/", + "description": "List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/", "type": "array", "items": { "default": {}, @@ -4257,7 +4303,7 @@ "x-kubernetes-patch-strategy": "merge" }, "nodeName": { - "description": "NodeName is a request to schedule this pod onto a specific node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits resource requirements.", + "description": "NodeName indicates in which node this pod is scheduled. If empty, this pod is a candidate for scheduling by the scheduler defined in schedulerName. Once this field is set, the kubelet for this node becomes responsible for the lifecycle of this pod. This field should not be used to express a desire for the pod to be scheduled on a specific node. https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodename", "type": "string" }, "nodeSelector": { @@ -4270,18 +4316,18 @@ "x-kubernetes-map-type": "atomic" }, "os": { - "description": "Specifies the OS of the containers in the pod. Some pod and container fields are restricted if this is set.\n\nIf the OS field is set to linux, the following fields must be unset: -securityContext.windowsOptions\n\nIf the OS field is set to windows, following fields must be unset: - spec.hostPID - spec.hostIPC - spec.securityContext.seLinuxOptions - spec.securityContext.seccompProfile - spec.securityContext.fsGroup - spec.securityContext.fsGroupChangePolicy - spec.securityContext.sysctls - spec.shareProcessNamespace - spec.securityContext.runAsUser - spec.securityContext.runAsGroup - spec.securityContext.supplementalGroups - spec.containers[*].securityContext.seLinuxOptions - spec.containers[*].securityContext.seccompProfile - spec.containers[*].securityContext.capabilities - spec.containers[*].securityContext.readOnlyRootFilesystem - spec.containers[*].securityContext.privileged - spec.containers[*].securityContext.allowPrivilegeEscalation - spec.containers[*].securityContext.procMount - spec.containers[*].securityContext.runAsUser - spec.containers[*].securityContext.runAsGroup This is an alpha field and requires the IdentifyPodOS feature", + "description": "Specifies the OS of the containers in the pod. Some pod and container fields are restricted if this is set.\n\nIf the OS field is set to linux, the following fields must be unset: -securityContext.windowsOptions\n\nIf the OS field is set to windows, following fields must be unset: - spec.hostPID - spec.hostIPC - spec.hostUsers - spec.securityContext.appArmorProfile - spec.securityContext.seLinuxOptions - spec.securityContext.seccompProfile - spec.securityContext.fsGroup - spec.securityContext.fsGroupChangePolicy - spec.securityContext.sysctls - spec.shareProcessNamespace - spec.securityContext.runAsUser - spec.securityContext.runAsGroup - spec.securityContext.supplementalGroups - spec.securityContext.supplementalGroupsPolicy - spec.containers[*].securityContext.appArmorProfile - spec.containers[*].securityContext.seLinuxOptions - spec.containers[*].securityContext.seccompProfile - spec.containers[*].securityContext.capabilities - spec.containers[*].securityContext.readOnlyRootFilesystem - spec.containers[*].securityContext.privileged - spec.containers[*].securityContext.allowPrivilegeEscalation - spec.containers[*].securityContext.procMount - spec.containers[*].securityContext.runAsUser - spec.containers[*].securityContext.runAsGroup", "$ref": "#/definitions/v1.PodOS" }, "overhead": { - "description": "Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md This field is beta-level as of Kubernetes v1.18, and is only honored by servers that enable the PodOverhead feature.", + "description": "Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md", "type": "object", "additionalProperties": { "$ref": "#/definitions/resource.Quantity" } }, "preemptionPolicy": { - "description": "PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset. This field is beta-level, gated by the NonPreemptingPriority feature-gate.", + "description": "PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset.", "type": "string" }, "priority": { @@ -4315,12 +4361,16 @@ "x-kubernetes-patch-merge-key": "name", "x-kubernetes-patch-strategy": "merge,retainKeys" }, + "resources": { + "description": "Resources is the total amount of CPU and Memory resources required by all containers in the pod. It supports specifying Requests and Limits for \"cpu\" and \"memory\" resource names only. ResourceClaims are not supported.\n\nThis field enables fine-grained control over resource allocation for the entire pod, allowing resource sharing among containers in a pod.\n\nThis is an alpha field and requires enabling the PodLevelResources feature gate.", + "$ref": "#/definitions/v1.ResourceRequirements" + }, "restartPolicy": { - "description": "Restart policy for all containers within the pod. One of Always, OnFailure, Never. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy", + "description": "Restart policy for all containers within the pod. One of Always, OnFailure, Never. In some contexts, only a subset of those values may be permitted. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy", "type": "string" }, "runtimeClassName": { - "description": "RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class This is a beta feature as of Kubernetes v1.14.", + "description": "RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class", "type": "string" }, "schedulerName": { @@ -4328,7 +4378,7 @@ "type": "string" }, "schedulingGates": { - "description": "SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod.\n\nSchedulingGates can only be set at pod creation time, and be removed only afterwards.\n\nThis is a beta feature enabled by the PodSchedulingReadiness feature gate.", + "description": "SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod.\n\nSchedulingGates can only be set at pod creation time, and be removed only afterwards.", "type": "array", "items": { "default": {}, @@ -4346,7 +4396,7 @@ "$ref": "#/definitions/v1.PodSecurityContext" }, "serviceAccount": { - "description": "DeprecatedServiceAccount is a depreciated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead.", + "description": "DeprecatedServiceAccount is a deprecated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead.", "type": "string" }, "serviceAccountName": { @@ -4652,7 +4702,7 @@ "type": "boolean" }, "ephemeralContainers": { - "description": "List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource. This field is beta-level and available on clusters that haven't disabled the EphemeralContainers feature gate.", + "description": "List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource.", "type": "array", "items": { "default": {}, @@ -4662,7 +4712,7 @@ "x-kubernetes-patch-strategy": "merge" }, "hostAliases": { - "description": "HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. This is only valid for non-hostNetwork pods.", + "description": "HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified.", "type": "array", "items": { "default": {}, @@ -4696,7 +4746,7 @@ "$ref": "#/definitions/v1beta1.HuggingFaceRuntimeSpec" }, "imagePullSecrets": { - "description": "ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. For example, in the case of docker, only DockerConfig type secrets are honored. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod", + "description": "ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod", "type": "array", "items": { "default": {}, @@ -4706,7 +4756,7 @@ "x-kubernetes-patch-strategy": "merge" }, "initContainers": { - "description": "List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/", + "description": "List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/", "type": "array", "items": { "default": {}, @@ -4746,7 +4796,7 @@ "$ref": "#/definitions/v1beta1.ModelSpec" }, "nodeName": { - "description": "NodeName is a request to schedule this pod onto a specific node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits resource requirements.", + "description": "NodeName indicates in which node this pod is scheduled. If empty, this pod is a candidate for scheduling by the scheduler defined in schedulerName. Once this field is set, the kubelet for this node becomes responsible for the lifecycle of this pod. This field should not be used to express a desire for the pod to be scheduled on a specific node. https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodename", "type": "string" }, "nodeSelector": { @@ -4763,11 +4813,11 @@ "$ref": "#/definitions/v1beta1.ONNXRuntimeSpec" }, "os": { - "description": "Specifies the OS of the containers in the pod. Some pod and container fields are restricted if this is set.\n\nIf the OS field is set to linux, the following fields must be unset: -securityContext.windowsOptions\n\nIf the OS field is set to windows, following fields must be unset: - spec.hostPID - spec.hostIPC - spec.securityContext.seLinuxOptions - spec.securityContext.seccompProfile - spec.securityContext.fsGroup - spec.securityContext.fsGroupChangePolicy - spec.securityContext.sysctls - spec.shareProcessNamespace - spec.securityContext.runAsUser - spec.securityContext.runAsGroup - spec.securityContext.supplementalGroups - spec.containers[*].securityContext.seLinuxOptions - spec.containers[*].securityContext.seccompProfile - spec.containers[*].securityContext.capabilities - spec.containers[*].securityContext.readOnlyRootFilesystem - spec.containers[*].securityContext.privileged - spec.containers[*].securityContext.allowPrivilegeEscalation - spec.containers[*].securityContext.procMount - spec.containers[*].securityContext.runAsUser - spec.containers[*].securityContext.runAsGroup This is an alpha field and requires the IdentifyPodOS feature", + "description": "Specifies the OS of the containers in the pod. Some pod and container fields are restricted if this is set.\n\nIf the OS field is set to linux, the following fields must be unset: -securityContext.windowsOptions\n\nIf the OS field is set to windows, following fields must be unset: - spec.hostPID - spec.hostIPC - spec.hostUsers - spec.securityContext.appArmorProfile - spec.securityContext.seLinuxOptions - spec.securityContext.seccompProfile - spec.securityContext.fsGroup - spec.securityContext.fsGroupChangePolicy - spec.securityContext.sysctls - spec.shareProcessNamespace - spec.securityContext.runAsUser - spec.securityContext.runAsGroup - spec.securityContext.supplementalGroups - spec.securityContext.supplementalGroupsPolicy - spec.containers[*].securityContext.appArmorProfile - spec.containers[*].securityContext.seLinuxOptions - spec.containers[*].securityContext.seccompProfile - spec.containers[*].securityContext.capabilities - spec.containers[*].securityContext.readOnlyRootFilesystem - spec.containers[*].securityContext.privileged - spec.containers[*].securityContext.allowPrivilegeEscalation - spec.containers[*].securityContext.procMount - spec.containers[*].securityContext.runAsUser - spec.containers[*].securityContext.runAsGroup", "$ref": "#/definitions/v1.PodOS" }, "overhead": { - "description": "Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md This field is beta-level as of Kubernetes v1.18, and is only honored by servers that enable the PodOverhead feature.", + "description": "Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md", "type": "object", "additionalProperties": { "$ref": "#/definitions/resource.Quantity" @@ -4782,7 +4832,7 @@ "$ref": "#/definitions/v1beta1.PMMLSpec" }, "preemptionPolicy": { - "description": "PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset. This field is beta-level, gated by the NonPreemptingPriority feature-gate.", + "description": "PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset.", "type": "string" }, "priority": { @@ -4820,12 +4870,16 @@ "x-kubernetes-patch-merge-key": "name", "x-kubernetes-patch-strategy": "merge,retainKeys" }, + "resources": { + "description": "Resources is the total amount of CPU and Memory resources required by all containers in the pod. It supports specifying Requests and Limits for \"cpu\" and \"memory\" resource names only. ResourceClaims are not supported.\n\nThis field enables fine-grained control over resource allocation for the entire pod, allowing resource sharing among containers in a pod.\n\nThis is an alpha field and requires enabling the PodLevelResources feature gate.", + "$ref": "#/definitions/v1.ResourceRequirements" + }, "restartPolicy": { - "description": "Restart policy for all containers within the pod. One of Always, OnFailure, Never. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy", + "description": "Restart policy for all containers within the pod. One of Always, OnFailure, Never. In some contexts, only a subset of those values may be permitted. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy", "type": "string" }, "runtimeClassName": { - "description": "RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class This is a beta feature as of Kubernetes v1.14.", + "description": "RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class", "type": "string" }, "scaleMetric": { @@ -4842,7 +4896,7 @@ "type": "string" }, "schedulingGates": { - "description": "SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod.\n\nSchedulingGates can only be set at pod creation time, and be removed only afterwards.\n\nThis is a beta feature enabled by the PodSchedulingReadiness feature gate.", + "description": "SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod.\n\nSchedulingGates can only be set at pod creation time, and be removed only afterwards.", "type": "array", "items": { "default": {}, @@ -4860,7 +4914,7 @@ "$ref": "#/definitions/v1.PodSecurityContext" }, "serviceAccount": { - "description": "DeprecatedServiceAccount is a depreciated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead.", + "description": "DeprecatedServiceAccount is a deprecated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead.", "type": "string" }, "serviceAccountName": { @@ -4944,6 +4998,23 @@ } } }, + "v1beta1.ResourceConfig": { + "type": "object", + "properties": { + "cpuLimit": { + "type": "string" + }, + "cpuRequest": { + "type": "string" + }, + "memoryLimit": { + "type": "string" + }, + "memoryRequest": { + "type": "string" + } + } + }, "v1beta1.SKLearnSpec": { "description": "SKLearnSpec defines arguments for configuring SKLearn model serving.", "type": "object", @@ -5599,7 +5670,7 @@ "type": "boolean" }, "ephemeralContainers": { - "description": "List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource. This field is beta-level and available on clusters that haven't disabled the EphemeralContainers feature gate.", + "description": "List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource.", "type": "array", "items": { "default": {}, @@ -5609,7 +5680,7 @@ "x-kubernetes-patch-strategy": "merge" }, "hostAliases": { - "description": "HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. This is only valid for non-hostNetwork pods.", + "description": "HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified.", "type": "array", "items": { "default": {}, @@ -5639,7 +5710,7 @@ "type": "string" }, "imagePullSecrets": { - "description": "ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. For example, in the case of docker, only DockerConfig type secrets are honored. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod", + "description": "ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod", "type": "array", "items": { "default": {}, @@ -5649,7 +5720,7 @@ "x-kubernetes-patch-strategy": "merge" }, "initContainers": { - "description": "List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/", + "description": "List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/", "type": "array", "items": { "default": {}, @@ -5681,7 +5752,7 @@ "format": "int32" }, "nodeName": { - "description": "NodeName is a request to schedule this pod onto a specific node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits resource requirements.", + "description": "NodeName indicates in which node this pod is scheduled. If empty, this pod is a candidate for scheduling by the scheduler defined in schedulerName. Once this field is set, the kubelet for this node becomes responsible for the lifecycle of this pod. This field should not be used to express a desire for the pod to be scheduled on a specific node. https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodename", "type": "string" }, "nodeSelector": { @@ -5694,18 +5765,18 @@ "x-kubernetes-map-type": "atomic" }, "os": { - "description": "Specifies the OS of the containers in the pod. Some pod and container fields are restricted if this is set.\n\nIf the OS field is set to linux, the following fields must be unset: -securityContext.windowsOptions\n\nIf the OS field is set to windows, following fields must be unset: - spec.hostPID - spec.hostIPC - spec.securityContext.seLinuxOptions - spec.securityContext.seccompProfile - spec.securityContext.fsGroup - spec.securityContext.fsGroupChangePolicy - spec.securityContext.sysctls - spec.shareProcessNamespace - spec.securityContext.runAsUser - spec.securityContext.runAsGroup - spec.securityContext.supplementalGroups - spec.containers[*].securityContext.seLinuxOptions - spec.containers[*].securityContext.seccompProfile - spec.containers[*].securityContext.capabilities - spec.containers[*].securityContext.readOnlyRootFilesystem - spec.containers[*].securityContext.privileged - spec.containers[*].securityContext.allowPrivilegeEscalation - spec.containers[*].securityContext.procMount - spec.containers[*].securityContext.runAsUser - spec.containers[*].securityContext.runAsGroup This is an alpha field and requires the IdentifyPodOS feature", + "description": "Specifies the OS of the containers in the pod. Some pod and container fields are restricted if this is set.\n\nIf the OS field is set to linux, the following fields must be unset: -securityContext.windowsOptions\n\nIf the OS field is set to windows, following fields must be unset: - spec.hostPID - spec.hostIPC - spec.hostUsers - spec.securityContext.appArmorProfile - spec.securityContext.seLinuxOptions - spec.securityContext.seccompProfile - spec.securityContext.fsGroup - spec.securityContext.fsGroupChangePolicy - spec.securityContext.sysctls - spec.shareProcessNamespace - spec.securityContext.runAsUser - spec.securityContext.runAsGroup - spec.securityContext.supplementalGroups - spec.securityContext.supplementalGroupsPolicy - spec.containers[*].securityContext.appArmorProfile - spec.containers[*].securityContext.seLinuxOptions - spec.containers[*].securityContext.seccompProfile - spec.containers[*].securityContext.capabilities - spec.containers[*].securityContext.readOnlyRootFilesystem - spec.containers[*].securityContext.privileged - spec.containers[*].securityContext.allowPrivilegeEscalation - spec.containers[*].securityContext.procMount - spec.containers[*].securityContext.runAsUser - spec.containers[*].securityContext.runAsGroup", "$ref": "#/definitions/v1.PodOS" }, "overhead": { - "description": "Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md This field is beta-level as of Kubernetes v1.18, and is only honored by servers that enable the PodOverhead feature.", + "description": "Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md", "type": "object", "additionalProperties": { "$ref": "#/definitions/resource.Quantity" } }, "preemptionPolicy": { - "description": "PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset. This field is beta-level, gated by the NonPreemptingPriority feature-gate.", + "description": "PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset.", "type": "string" }, "priority": { @@ -5739,12 +5810,16 @@ "x-kubernetes-patch-merge-key": "name", "x-kubernetes-patch-strategy": "merge,retainKeys" }, + "resources": { + "description": "Resources is the total amount of CPU and Memory resources required by all containers in the pod. It supports specifying Requests and Limits for \"cpu\" and \"memory\" resource names only. ResourceClaims are not supported.\n\nThis field enables fine-grained control over resource allocation for the entire pod, allowing resource sharing among containers in a pod.\n\nThis is an alpha field and requires enabling the PodLevelResources feature gate.", + "$ref": "#/definitions/v1.ResourceRequirements" + }, "restartPolicy": { - "description": "Restart policy for all containers within the pod. One of Always, OnFailure, Never. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy", + "description": "Restart policy for all containers within the pod. One of Always, OnFailure, Never. In some contexts, only a subset of those values may be permitted. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy", "type": "string" }, "runtimeClassName": { - "description": "RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class This is a beta feature as of Kubernetes v1.14.", + "description": "RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class", "type": "string" }, "scaleMetric": { @@ -5761,7 +5836,7 @@ "type": "string" }, "schedulingGates": { - "description": "SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod.\n\nSchedulingGates can only be set at pod creation time, and be removed only afterwards.\n\nThis is a beta feature enabled by the PodSchedulingReadiness feature gate.", + "description": "SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod.\n\nSchedulingGates can only be set at pod creation time, and be removed only afterwards.", "type": "array", "items": { "default": {}, @@ -5779,7 +5854,7 @@ "$ref": "#/definitions/v1.PodSecurityContext" }, "serviceAccount": { - "description": "DeprecatedServiceAccount is a depreciated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead.", + "description": "DeprecatedServiceAccount is a deprecated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead.", "type": "string" }, "serviceAccountName": { @@ -6063,7 +6138,7 @@ "type": "boolean" }, "ephemeralContainers": { - "description": "List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource. This field is beta-level and available on clusters that haven't disabled the EphemeralContainers feature gate.", + "description": "List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource.", "type": "array", "items": { "default": {}, @@ -6073,7 +6148,7 @@ "x-kubernetes-patch-strategy": "merge" }, "hostAliases": { - "description": "HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. This is only valid for non-hostNetwork pods.", + "description": "HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified.", "type": "array", "items": { "default": {}, @@ -6103,7 +6178,7 @@ "type": "string" }, "imagePullSecrets": { - "description": "ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. For example, in the case of docker, only DockerConfig type secrets are honored. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod", + "description": "ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod", "type": "array", "items": { "default": {}, @@ -6113,7 +6188,7 @@ "x-kubernetes-patch-strategy": "merge" }, "initContainers": { - "description": "List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/", + "description": "List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/", "type": "array", "items": { "default": {}, @@ -6123,7 +6198,7 @@ "x-kubernetes-patch-strategy": "merge" }, "nodeName": { - "description": "NodeName is a request to schedule this pod onto a specific node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits resource requirements.", + "description": "NodeName indicates in which node this pod is scheduled. If empty, this pod is a candidate for scheduling by the scheduler defined in schedulerName. Once this field is set, the kubelet for this node becomes responsible for the lifecycle of this pod. This field should not be used to express a desire for the pod to be scheduled on a specific node. https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodename", "type": "string" }, "nodeSelector": { @@ -6136,11 +6211,11 @@ "x-kubernetes-map-type": "atomic" }, "os": { - "description": "Specifies the OS of the containers in the pod. Some pod and container fields are restricted if this is set.\n\nIf the OS field is set to linux, the following fields must be unset: -securityContext.windowsOptions\n\nIf the OS field is set to windows, following fields must be unset: - spec.hostPID - spec.hostIPC - spec.securityContext.seLinuxOptions - spec.securityContext.seccompProfile - spec.securityContext.fsGroup - spec.securityContext.fsGroupChangePolicy - spec.securityContext.sysctls - spec.shareProcessNamespace - spec.securityContext.runAsUser - spec.securityContext.runAsGroup - spec.securityContext.supplementalGroups - spec.containers[*].securityContext.seLinuxOptions - spec.containers[*].securityContext.seccompProfile - spec.containers[*].securityContext.capabilities - spec.containers[*].securityContext.readOnlyRootFilesystem - spec.containers[*].securityContext.privileged - spec.containers[*].securityContext.allowPrivilegeEscalation - spec.containers[*].securityContext.procMount - spec.containers[*].securityContext.runAsUser - spec.containers[*].securityContext.runAsGroup This is an alpha field and requires the IdentifyPodOS feature", + "description": "Specifies the OS of the containers in the pod. Some pod and container fields are restricted if this is set.\n\nIf the OS field is set to linux, the following fields must be unset: -securityContext.windowsOptions\n\nIf the OS field is set to windows, following fields must be unset: - spec.hostPID - spec.hostIPC - spec.hostUsers - spec.securityContext.appArmorProfile - spec.securityContext.seLinuxOptions - spec.securityContext.seccompProfile - spec.securityContext.fsGroup - spec.securityContext.fsGroupChangePolicy - spec.securityContext.sysctls - spec.shareProcessNamespace - spec.securityContext.runAsUser - spec.securityContext.runAsGroup - spec.securityContext.supplementalGroups - spec.securityContext.supplementalGroupsPolicy - spec.containers[*].securityContext.appArmorProfile - spec.containers[*].securityContext.seLinuxOptions - spec.containers[*].securityContext.seccompProfile - spec.containers[*].securityContext.capabilities - spec.containers[*].securityContext.readOnlyRootFilesystem - spec.containers[*].securityContext.privileged - spec.containers[*].securityContext.allowPrivilegeEscalation - spec.containers[*].securityContext.procMount - spec.containers[*].securityContext.runAsUser - spec.containers[*].securityContext.runAsGroup", "$ref": "#/definitions/v1.PodOS" }, "overhead": { - "description": "Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md This field is beta-level as of Kubernetes v1.18, and is only honored by servers that enable the PodOverhead feature.", + "description": "Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md", "type": "object", "additionalProperties": { "$ref": "#/definitions/resource.Quantity" @@ -6152,7 +6227,7 @@ "format": "int32" }, "preemptionPolicy": { - "description": "PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset. This field is beta-level, gated by the NonPreemptingPriority feature-gate.", + "description": "PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset.", "type": "string" }, "priority": { @@ -6186,12 +6261,16 @@ "x-kubernetes-patch-merge-key": "name", "x-kubernetes-patch-strategy": "merge,retainKeys" }, + "resources": { + "description": "Resources is the total amount of CPU and Memory resources required by all containers in the pod. It supports specifying Requests and Limits for \"cpu\" and \"memory\" resource names only. ResourceClaims are not supported.\n\nThis field enables fine-grained control over resource allocation for the entire pod, allowing resource sharing among containers in a pod.\n\nThis is an alpha field and requires enabling the PodLevelResources feature gate.", + "$ref": "#/definitions/v1.ResourceRequirements" + }, "restartPolicy": { - "description": "Restart policy for all containers within the pod. One of Always, OnFailure, Never. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy", + "description": "Restart policy for all containers within the pod. One of Always, OnFailure, Never. In some contexts, only a subset of those values may be permitted. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy", "type": "string" }, "runtimeClassName": { - "description": "RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class This is a beta feature as of Kubernetes v1.14.", + "description": "RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class", "type": "string" }, "schedulerName": { @@ -6199,7 +6278,7 @@ "type": "string" }, "schedulingGates": { - "description": "SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod.\n\nSchedulingGates can only be set at pod creation time, and be removed only afterwards.\n\nThis is a beta feature enabled by the PodSchedulingReadiness feature gate.", + "description": "SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod.\n\nSchedulingGates can only be set at pod creation time, and be removed only afterwards.", "type": "array", "items": { "default": {}, @@ -6217,7 +6296,7 @@ "$ref": "#/definitions/v1.PodSecurityContext" }, "serviceAccount": { - "description": "DeprecatedServiceAccount is a depreciated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead.", + "description": "DeprecatedServiceAccount is a deprecated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead.", "type": "string" }, "serviceAccountName": { diff --git a/pkg/testing/be_sematic_equal_matcher.go b/pkg/testing/be_sematic_equal_matcher.go index 9fef0246a22..8b4e286bc48 100644 --- a/pkg/testing/be_sematic_equal_matcher.go +++ b/pkg/testing/be_sematic_equal_matcher.go @@ -17,7 +17,7 @@ limitations under the License. package testing import ( - "fmt" + "errors" "reflect" "github.com/onsi/gomega/format" @@ -39,7 +39,7 @@ type BeSematicEqualMatcher struct { func (matcher *BeSematicEqualMatcher) Match(actual interface{}) (success bool, err error) { if actual == nil && matcher.Expected == nil { - return false, fmt.Errorf("Both actual and expected must not be nil.") + return false, errors.New("Both actual and expected must not be nil.") } convertedActual := actual diff --git a/pkg/testing/envtest_setup.go b/pkg/testing/envtest_setup.go index ea77d3af18c..47f9c1b307f 100644 --- a/pkg/testing/envtest_setup.go +++ b/pkg/testing/envtest_setup.go @@ -30,6 +30,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/envtest" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/manager" + gatewayapiv1 "sigs.k8s.io/gateway-api/apis/v1" ) var log = logf.Log.WithName("TestingEnvSetup") @@ -54,6 +55,10 @@ func SetupEnvTest(crdDirectoryPaths []string) *envtest.Environment { if err := istioclientv1beta1.SchemeBuilder.AddToScheme(scheme.Scheme); err != nil { log.Error(err, "Failed to add istio scheme") } + + if err := gatewayapiv1.Install(scheme.Scheme); err != nil { + log.Error(err, "Failed to add gateway scheme") + } return t } diff --git a/pkg/utils/types_test.go b/pkg/utils/types_test.go index 4c0b975d2ca..eff6aced0b8 100644 --- a/pkg/utils/types_test.go +++ b/pkg/utils/types_test.go @@ -17,8 +17,9 @@ limitations under the License. package utils import ( - "github.com/google/go-cmp/cmp" "testing" + + "github.com/google/go-cmp/cmp" ) func TestBool(t *testing.T) { diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index 68a9e4a3f52..dda827bc3c0 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -18,14 +18,16 @@ package utils import ( "encoding/json" + "strconv" "strings" - "github.com/kserve/kserve/pkg/constants" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" apierr "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/discovery" "k8s.io/client-go/rest" + + "github.com/kserve/kserve/pkg/constants" ) /* NOTE TO AUTHORS: @@ -36,6 +38,11 @@ import ( var gvResourcesCache map[string]*metav1.APIResourceList +// Errors +const ( + ErrValueExceedsInt32Limit = "value exceeds int32 limit %d" +) + func Filter(origin map[string]string, predicate func(string) bool) map[string]string { result := make(map[string]string) for k, v := range origin { @@ -74,7 +81,7 @@ func IncludesArg(slice []string, arg string) bool { return false } -func AppendVolumeIfNotExists(slice []v1.Volume, volume v1.Volume) []v1.Volume { +func AppendVolumeIfNotExists(slice []corev1.Volume, volume corev1.Volume) []corev1.Volume { for i := range slice { if slice[i].Name == volume.Name { return slice @@ -83,7 +90,7 @@ func AppendVolumeIfNotExists(slice []v1.Volume, volume v1.Volume) []v1.Volume { return append(slice, volume) } -func IsGPUEnabled(requirements v1.ResourceRequirements) bool { +func IsGPUEnabled(requirements corev1.ResourceRequirements) bool { _, ok := requirements.Limits[constants.NvidiaGPUResourceType] return ok } @@ -123,8 +130,8 @@ func IsPrefixSupported(input string, prefixes []string) bool { // 1. If an EnvVar is present in B but not in O, value remains unchanged in the result // 2. If an EnvVar is present in `O` but not in `B`, appends to the result // 3. If an EnvVar is present in both O and B, uses the value from O in the result -func MergeEnvs(baseEnvs []v1.EnvVar, overrideEnvs []v1.EnvVar) []v1.EnvVar { - var extra []v1.EnvVar +func MergeEnvs(baseEnvs []corev1.EnvVar, overrideEnvs []corev1.EnvVar) []corev1.EnvVar { + var extra []corev1.EnvVar for _, override := range overrideEnvs { inBase := false @@ -145,7 +152,7 @@ func MergeEnvs(baseEnvs []v1.EnvVar, overrideEnvs []v1.EnvVar) []v1.EnvVar { return append(baseEnvs, extra...) } -func AppendEnvVarIfNotExists(slice []v1.EnvVar, elems ...v1.EnvVar) []v1.EnvVar { +func AppendEnvVarIfNotExists(slice []corev1.EnvVar, elems ...corev1.EnvVar) []corev1.EnvVar { for _, elem := range elems { isElemExists := false for _, item := range slice { @@ -161,7 +168,7 @@ func AppendEnvVarIfNotExists(slice []v1.EnvVar, elems ...v1.EnvVar) []v1.EnvVar return slice } -func AppendPortIfNotExists(slice []v1.ContainerPort, elems ...v1.ContainerPort) []v1.ContainerPort { +func AppendPortIfNotExists(slice []corev1.ContainerPort, elems ...corev1.ContainerPort) []corev1.ContainerPort { for _, elem := range elems { isElemExists := false for _, item := range slice { @@ -236,7 +243,7 @@ func SetAvailableResourcesForApi(groupVersion string, resources *metav1.APIResou gvResourcesCache[groupVersion] = resources } -func GetEnvVarValue(envVars []v1.EnvVar, key string) (string, bool) { +func GetEnvVarValue(envVars []corev1.EnvVar, key string) (string, bool) { for _, envVar := range envVars { if envVar.Name == key { return envVar.Value, true // if key exist, return value, true @@ -246,18 +253,18 @@ func GetEnvVarValue(envVars []v1.EnvVar, key string) (string, bool) { } // IsUnknownGpuResourceType check if the provided gpu resource type is unknown one -func IsUnknownGpuResourceType(resources v1.ResourceRequirements, customGpuResourceTypes string) bool { - basicResourceTypes := map[v1.ResourceName]struct{}{ - v1.ResourceCPU: {}, - v1.ResourceMemory: {}, - v1.ResourceStorage: {}, - v1.ResourceEphemeralStorage: {}, +func IsUnknownGpuResourceType(resources corev1.ResourceRequirements, customGpuResourceTypes string) bool { + basicResourceTypes := map[corev1.ResourceName]struct{}{ + corev1.ResourceCPU: {}, + corev1.ResourceMemory: {}, + corev1.ResourceStorage: {}, + corev1.ResourceEphemeralStorage: {}, } - possibleGPUResourceType := map[v1.ResourceName]struct{}{} + possibleGPUResourceType := map[corev1.ResourceName]struct{}{} // Helper function to add non-basic resources from the provided ResourceList - addNonBasicResources := func(resources v1.ResourceList) { + addNonBasicResources := func(resources corev1.ResourceList) { for resourceType := range resources { if _, exists := basicResourceTypes[resourceType]; !exists { possibleGPUResourceType[resourceType] = struct{}{} @@ -276,7 +283,7 @@ func IsUnknownGpuResourceType(resources v1.ResourceRequirements, customGpuResour } for _, gpuType := range constants.GPUResourceTypeList { - allowedGPUResourceName := v1.ResourceName(gpuType) + allowedGPUResourceName := corev1.ResourceName(gpuType) delete(possibleGPUResourceType, allowedGPUResourceName) // Remove allowed GPU resource if exists } @@ -310,3 +317,12 @@ func IsValidCustomGPUArray(s string) bool { return true } + +// StringToInt32 converts a given integer to int32. If the number exceeds the int32 limit, it returns an error. +func StringToInt32(number string) (int32, error) { + converted, err := strconv.ParseInt(number, 10, 32) + if err != nil { + return 0, err + } + return int32(converted), err +} diff --git a/pkg/utils/utils_test.go b/pkg/utils/utils_test.go index b2a61122c34..5a156c81f4f 100644 --- a/pkg/utils/utils_test.go +++ b/pkg/utils/utils_test.go @@ -18,16 +18,18 @@ package utils import ( "errors" + "strconv" "testing" - "github.com/kserve/kserve/pkg/constants" - "github.com/kserve/kserve/pkg/credentials/gcs" + "github.com/google/go-cmp/cmp" "github.com/onsi/gomega" "github.com/onsi/gomega/types" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "github.com/google/go-cmp/cmp" + "github.com/kserve/kserve/pkg/constants" + "github.com/kserve/kserve/pkg/credentials/gcs" ) func TestFilterUtil(t *testing.T) { @@ -63,31 +65,47 @@ func TestUnionUtil(t *testing.T) { expected map[string]string }{ "UnionTwoMaps": { - input1: map[string]string{"serving.kserve.io/service": "mnist", - "label1": "value1"}, - input2: map[string]string{"service.knative.dev/service": "mnist", - "label2": "value2"}, - expected: map[string]string{"serving.kserve.io/service": "mnist", - "label1": "value1", "service.knative.dev/service": "mnist", "label2": "value2"}, + input1: map[string]string{ + "serving.kserve.io/service": "mnist", + "label1": "value1", + }, + input2: map[string]string{ + "service.knative.dev/service": "mnist", + "label2": "value2", + }, + expected: map[string]string{ + "serving.kserve.io/service": "mnist", + "label1": "value1", "service.knative.dev/service": "mnist", "label2": "value2", + }, }, "UnionTwoMapsOverwritten": { - input1: map[string]string{"serving.kserve.io/service": "mnist", - "label1": "value1", "label3": "value1"}, - input2: map[string]string{"service.knative.dev/service": "mnist", - "label2": "value2", "label3": "value3"}, - expected: map[string]string{"serving.kserve.io/service": "mnist", - "label1": "value1", "service.knative.dev/service": "mnist", "label2": "value2", "label3": "value3"}, + input1: map[string]string{ + "serving.kserve.io/service": "mnist", + "label1": "value1", "label3": "value1", + }, + input2: map[string]string{ + "service.knative.dev/service": "mnist", + "label2": "value2", "label3": "value3", + }, + expected: map[string]string{ + "serving.kserve.io/service": "mnist", + "label1": "value1", "service.knative.dev/service": "mnist", "label2": "value2", "label3": "value3", + }, }, "UnionWithEmptyMap": { input1: map[string]string{}, - input2: map[string]string{"service.knative.dev/service": "mnist", - "label2": "value2"}, + input2: map[string]string{ + "service.knative.dev/service": "mnist", + "label2": "value2", + }, expected: map[string]string{"service.knative.dev/service": "mnist", "label2": "value2"}, }, "UnionWithNilMap": { input1: nil, - input2: map[string]string{"service.knative.dev/service": "mnist", - "label2": "value2"}, + input2: map[string]string{ + "service.knative.dev/service": "mnist", + "label2": "value2", + }, expected: map[string]string{"service.knative.dev/service": "mnist", "label2": "value2"}, }, "UnionNilMaps": { @@ -136,52 +154,51 @@ func TestContainsUtil(t *testing.T) { } func TestAppendVolumeIfNotExists(t *testing.T) { - scenarios := map[string]struct { - volumes []v1.Volume - volume v1.Volume - expectedVolumes []v1.Volume + volumes []corev1.Volume + volume corev1.Volume + expectedVolumes []corev1.Volume }{ "DuplicateVolume": { - volumes: []v1.Volume{ + volumes: []corev1.Volume{ { Name: gcs.GCSCredentialVolumeName, - VolumeSource: v1.VolumeSource{ - Secret: &v1.SecretVolumeSource{ + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ SecretName: "user-gcp-sa", }, }, }, { Name: "blue", - VolumeSource: v1.VolumeSource{ - Secret: &v1.SecretVolumeSource{ + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ SecretName: "user-gcp-sa", }, }, }, }, - volume: v1.Volume{ + volume: corev1.Volume{ Name: gcs.GCSCredentialVolumeName, - VolumeSource: v1.VolumeSource{ - Secret: &v1.SecretVolumeSource{ + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ SecretName: "user-gcp-sa", }, }, }, - expectedVolumes: []v1.Volume{ + expectedVolumes: []corev1.Volume{ { Name: gcs.GCSCredentialVolumeName, - VolumeSource: v1.VolumeSource{ - Secret: &v1.SecretVolumeSource{ + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ SecretName: "user-gcp-sa", }, }, }, { Name: "blue", - VolumeSource: v1.VolumeSource{ - Secret: &v1.SecretVolumeSource{ + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ SecretName: "user-gcp-sa", }, }, @@ -189,53 +206,53 @@ func TestAppendVolumeIfNotExists(t *testing.T) { }, }, "NotDuplicateVolume": { - volumes: []v1.Volume{ + volumes: []corev1.Volume{ { Name: gcs.GCSCredentialVolumeName, - VolumeSource: v1.VolumeSource{ - Secret: &v1.SecretVolumeSource{ + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ SecretName: "user-gcp-sa", }, }, }, { Name: "blue", - VolumeSource: v1.VolumeSource{ - Secret: &v1.SecretVolumeSource{ + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ SecretName: "user-gcp-sa", }, }, }, }, - volume: v1.Volume{ + volume: corev1.Volume{ Name: "green", - VolumeSource: v1.VolumeSource{ - Secret: &v1.SecretVolumeSource{ + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ SecretName: "user-gcp-sa", }, }, }, - expectedVolumes: []v1.Volume{ + expectedVolumes: []corev1.Volume{ { Name: gcs.GCSCredentialVolumeName, - VolumeSource: v1.VolumeSource{ - Secret: &v1.SecretVolumeSource{ + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ SecretName: "user-gcp-sa", }, }, }, { Name: "blue", - VolumeSource: v1.VolumeSource{ - Secret: &v1.SecretVolumeSource{ + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ SecretName: "user-gcp-sa", }, }, }, { Name: "green", - VolumeSource: v1.VolumeSource{ - Secret: &v1.SecretVolumeSource{ + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ SecretName: "user-gcp-sa", }, }, @@ -254,21 +271,20 @@ func TestAppendVolumeIfNotExists(t *testing.T) { } func TestMergeEnvs(t *testing.T) { - scenarios := map[string]struct { - baseEnvs []v1.EnvVar - overrideEnvs []v1.EnvVar - expectedEnvs []v1.EnvVar + baseEnvs []corev1.EnvVar + overrideEnvs []corev1.EnvVar + expectedEnvs []corev1.EnvVar }{ "EmptyOverrides": { - baseEnvs: []v1.EnvVar{ + baseEnvs: []corev1.EnvVar{ { Name: "name1", Value: "value1", }, }, - overrideEnvs: []v1.EnvVar{}, - expectedEnvs: []v1.EnvVar{ + overrideEnvs: []corev1.EnvVar{}, + expectedEnvs: []corev1.EnvVar{ { Name: "name1", Value: "value1", @@ -276,14 +292,14 @@ func TestMergeEnvs(t *testing.T) { }, }, "EmptyBase": { - baseEnvs: []v1.EnvVar{}, - overrideEnvs: []v1.EnvVar{ + baseEnvs: []corev1.EnvVar{}, + overrideEnvs: []corev1.EnvVar{ { Name: "name1", Value: "value1", }, }, - expectedEnvs: []v1.EnvVar{ + expectedEnvs: []corev1.EnvVar{ { Name: "name1", Value: "value1", @@ -291,19 +307,19 @@ func TestMergeEnvs(t *testing.T) { }, }, "NoOverlap": { - baseEnvs: []v1.EnvVar{ + baseEnvs: []corev1.EnvVar{ { Name: "name1", Value: "value1", }, }, - overrideEnvs: []v1.EnvVar{ + overrideEnvs: []corev1.EnvVar{ { Name: "name2", Value: "value2", }, }, - expectedEnvs: []v1.EnvVar{ + expectedEnvs: []corev1.EnvVar{ { Name: "name1", Value: "value1", @@ -315,19 +331,19 @@ func TestMergeEnvs(t *testing.T) { }, }, "SingleOverlap": { - baseEnvs: []v1.EnvVar{ + baseEnvs: []corev1.EnvVar{ { Name: "name1", Value: "value1", }, }, - overrideEnvs: []v1.EnvVar{ + overrideEnvs: []corev1.EnvVar{ { Name: "name1", Value: "value2", }, }, - expectedEnvs: []v1.EnvVar{ + expectedEnvs: []corev1.EnvVar{ { Name: "name1", Value: "value2", @@ -335,7 +351,7 @@ func TestMergeEnvs(t *testing.T) { }, }, "MultiOverlap": { - baseEnvs: []v1.EnvVar{ + baseEnvs: []corev1.EnvVar{ { Name: "name1", Value: "value1", @@ -349,7 +365,7 @@ func TestMergeEnvs(t *testing.T) { Value: "value3", }, }, - overrideEnvs: []v1.EnvVar{ + overrideEnvs: []corev1.EnvVar{ { Name: "name1", Value: "value3", @@ -363,7 +379,7 @@ func TestMergeEnvs(t *testing.T) { Value: "value4", }, }, - expectedEnvs: []v1.EnvVar{ + expectedEnvs: []corev1.EnvVar{ { Name: "name1", Value: "value3", @@ -422,18 +438,18 @@ func TestIncludesArg(t *testing.T) { func TestIsGpuEnabled(t *testing.T) { g := gomega.NewGomegaWithT(t) scenarios := map[string]struct { - resource v1.ResourceRequirements + resource corev1.ResourceRequirements expected bool }{ "GpuEnabled": { - resource: v1.ResourceRequirements{ - Limits: v1.ResourceList{ + resource: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ "cpu": resource.Quantity{ Format: "100", }, constants.NvidiaGPUResourceType: resource.MustParse("1"), }, - Requests: v1.ResourceList{ + Requests: corev1.ResourceList{ "cpu": resource.Quantity{ Format: "90", }, @@ -443,13 +459,13 @@ func TestIsGpuEnabled(t *testing.T) { expected: true, }, "GPUDisabled": { - resource: v1.ResourceRequirements{ - Limits: v1.ResourceList{ + resource: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ "cpu": resource.Quantity{ Format: "100", }, }, - Requests: v1.ResourceList{ + Requests: corev1.ResourceList{ "cpu": resource.Quantity{ Format: "90", }, @@ -545,13 +561,13 @@ func TestIsPrefixSupported(t *testing.T) { func TestGetEnvVarValue(t *testing.T) { g := gomega.NewGomegaWithT(t) scenarios := map[string]struct { - envList []v1.EnvVar + envList []corev1.EnvVar targetEnvName string expectedEnvValue string expectedExist bool }{ "EnvExist": { - envList: []v1.EnvVar{ + envList: []corev1.EnvVar{ {Name: "test-name", Value: "test-value"}, }, targetEnvName: "test-name", @@ -559,7 +575,7 @@ func TestGetEnvVarValue(t *testing.T) { expectedExist: true, }, "EnvDoesNotExist": { - envList: []v1.EnvVar{ + envList: []corev1.EnvVar{ {Name: "test-name", Value: "test-value"}, }, targetEnvName: "wrong", @@ -581,71 +597,71 @@ func TestIsUnknownGpuResourceType(t *testing.T) { g := gomega.NewGomegaWithT(t) scenarios := map[string]struct { - resources v1.ResourceRequirements + resources corev1.ResourceRequirements expectedUnknown bool }{ "OnlyBasicResources": { - resources: v1.ResourceRequirements{ - Limits: v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("1"), - v1.ResourceMemory: resource.MustParse("1Gi"), + resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("1Gi"), }, - Requests: v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("1"), - v1.ResourceMemory: resource.MustParse("1Gi"), + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("1Gi"), }, }, expectedUnknown: false, }, "ValidGpuResource": { - resources: v1.ResourceRequirements{ - Limits: v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("1"), - v1.ResourceMemory: resource.MustParse("1Gi"), - v1.ResourceName("nvidia.com/gpu"): resource.MustParse("1"), + resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("1Gi"), + corev1.ResourceName("nvidia.com/gpu"): resource.MustParse("1"), }, - Requests: v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("1"), - v1.ResourceMemory: resource.MustParse("1Gi"), - v1.ResourceName("nvidia.com/gpu"): resource.MustParse("1"), + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("1Gi"), + corev1.ResourceName("nvidia.com/gpu"): resource.MustParse("1"), }, }, expectedUnknown: false, }, "UnknownGpuResource": { - resources: v1.ResourceRequirements{ - Limits: v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("1"), - v1.ResourceMemory: resource.MustParse("1Gi"), - v1.ResourceName("unknown.com/gpu"): resource.MustParse("1"), + resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("1Gi"), + corev1.ResourceName("unknown.com/gpu"): resource.MustParse("1"), }, - Requests: v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("1"), - v1.ResourceMemory: resource.MustParse("1Gi"), - v1.ResourceName("unknown.com/gpu"): resource.MustParse("1"), + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("1Gi"), + corev1.ResourceName("unknown.com/gpu"): resource.MustParse("1"), }, }, expectedUnknown: true, }, "MixedResources": { - resources: v1.ResourceRequirements{ - Limits: v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("1"), - v1.ResourceMemory: resource.MustParse("1Gi"), - v1.ResourceName("nvidia.com/gpu"): resource.MustParse("1"), + resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("1Gi"), + corev1.ResourceName("nvidia.com/gpu"): resource.MustParse("1"), }, - Requests: v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("1"), - v1.ResourceMemory: resource.MustParse("1Gi"), - v1.ResourceName("unknown.com/gpu"): resource.MustParse("1"), + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("1Gi"), + corev1.ResourceName("unknown.com/gpu"): resource.MustParse("1"), }, }, expectedUnknown: true, }, "EmptyResources": { - resources: v1.ResourceRequirements{ - Limits: v1.ResourceList{}, - Requests: v1.ResourceList{}, + resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{}, + Requests: corev1.ResourceList{}, }, expectedUnknown: false, }, @@ -689,3 +705,220 @@ func TestIsValidCustomGPUArray(t *testing.T) { }) } } + +func TestAppendEnvVarIfNotExists(t *testing.T) { + scenarios := map[string]struct { + initialEnvs []corev1.EnvVar + newEnvs []corev1.EnvVar + expectedEnvs []corev1.EnvVar + }{ + "EnvVarAlreadyExists": { + initialEnvs: []corev1.EnvVar{ + {Name: "ENV1", Value: "value1"}, + {Name: "ENV2", Value: "value2"}, + }, + newEnvs: []corev1.EnvVar{ + {Name: "ENV1", Value: "new_value1"}, + }, + expectedEnvs: []corev1.EnvVar{ + {Name: "ENV1", Value: "value1"}, + {Name: "ENV2", Value: "value2"}, + }, + }, + "EnvVarDoesNotExist": { + initialEnvs: []corev1.EnvVar{ + {Name: "ENV1", Value: "value1"}, + }, + newEnvs: []corev1.EnvVar{ + {Name: "ENV2", Value: "value2"}, + }, + expectedEnvs: []corev1.EnvVar{ + {Name: "ENV1", Value: "value1"}, + {Name: "ENV2", Value: "value2"}, + }, + }, + "MultipleNewEnvVars": { + initialEnvs: []corev1.EnvVar{ + {Name: "ENV1", Value: "value1"}, + }, + newEnvs: []corev1.EnvVar{ + {Name: "ENV2", Value: "value2"}, + {Name: "ENV3", Value: "value3"}, + }, + expectedEnvs: []corev1.EnvVar{ + {Name: "ENV1", Value: "value1"}, + {Name: "ENV2", Value: "value2"}, + {Name: "ENV3", Value: "value3"}, + }, + }, + "NoInitialEnvVars": { + initialEnvs: []corev1.EnvVar{}, + newEnvs: []corev1.EnvVar{ + {Name: "ENV1", Value: "value1"}, + }, + expectedEnvs: []corev1.EnvVar{ + {Name: "ENV1", Value: "value1"}, + }, + }, + } + + for name, scenario := range scenarios { + t.Run(name, func(t *testing.T) { + result := AppendEnvVarIfNotExists(scenario.initialEnvs, scenario.newEnvs...) + if diff := cmp.Diff(scenario.expectedEnvs, result); diff != "" { + t.Errorf("Test %q unexpected result (-want +got): %v", name, diff) + } + }) + } +} + +func TestAppendPortIfNotExists(t *testing.T) { + scenarios := map[string]struct { + initialPorts []corev1.ContainerPort + newPorts []corev1.ContainerPort + expectedPorts []corev1.ContainerPort + }{ + "PortAlreadyExists": { + initialPorts: []corev1.ContainerPort{ + {Name: "port1", ContainerPort: 8080}, + {Name: "port2", ContainerPort: 9090}, + }, + newPorts: []corev1.ContainerPort{ + {Name: "port1", ContainerPort: 8081}, + }, + expectedPorts: []corev1.ContainerPort{ + {Name: "port1", ContainerPort: 8080}, + {Name: "port2", ContainerPort: 9090}, + }, + }, + "PortDoesNotExist": { + initialPorts: []corev1.ContainerPort{ + {Name: "port1", ContainerPort: 8080}, + }, + newPorts: []corev1.ContainerPort{ + {Name: "port2", ContainerPort: 9090}, + }, + expectedPorts: []corev1.ContainerPort{ + {Name: "port1", ContainerPort: 8080}, + {Name: "port2", ContainerPort: 9090}, + }, + }, + "MultipleNewPorts": { + initialPorts: []corev1.ContainerPort{ + {Name: "port1", ContainerPort: 8080}, + }, + newPorts: []corev1.ContainerPort{ + {Name: "port2", ContainerPort: 9090}, + {Name: "port3", ContainerPort: 10090}, + }, + expectedPorts: []corev1.ContainerPort{ + {Name: "port1", ContainerPort: 8080}, + {Name: "port2", ContainerPort: 9090}, + {Name: "port3", ContainerPort: 10090}, + }, + }, + "NoInitialPorts": { + initialPorts: []corev1.ContainerPort{}, + newPorts: []corev1.ContainerPort{ + {Name: "port1", ContainerPort: 8080}, + }, + expectedPorts: []corev1.ContainerPort{ + {Name: "port1", ContainerPort: 8080}, + }, + }, + } + + for name, scenario := range scenarios { + t.Run(name, func(t *testing.T) { + result := AppendPortIfNotExists(scenario.initialPorts, scenario.newPorts...) + if diff := cmp.Diff(scenario.expectedPorts, result); diff != "" { + t.Errorf("Test %q unexpected result (-want +got): %v", name, diff) + } + }) + } +} + +func TestSetAvailableResourcesForApi(t *testing.T) { + g := gomega.NewGomegaWithT(t) + + scenarios := map[string]struct { + groupVersion string + resources *metav1.APIResourceList + }{ + "SetResourcesInEmptyCache": { + groupVersion: "v1", + resources: &metav1.APIResourceList{ + GroupVersion: "v1", + APIResources: []metav1.APIResource{ + {Name: "pods", Kind: "Pod"}, + }, + }, + }, + "SetResourcesInNonEmptyCache": { + groupVersion: "v1", + resources: &metav1.APIResourceList{ + GroupVersion: "v1", + APIResources: []metav1.APIResource{ + {Name: "services", Kind: "Service"}, + }, + }, + }, + } + + for name, scenario := range scenarios { + t.Run(name, func(t *testing.T) { + // Reset the cache before each test + gvResourcesCache = nil + + SetAvailableResourcesForApi(scenario.groupVersion, scenario.resources) + + g.Expect(gvResourcesCache).ToNot(gomega.BeNil()) + g.Expect(gvResourcesCache[scenario.groupVersion]).To(gomega.BeComparableTo(scenario.resources)) + }) + } +} + +func TestStringToInt32(t *testing.T) { + scenarios := map[string]struct { + input string + expected int32 + err error + }{ + "ValidInt32": { + input: "123", + expected: 123, + err: nil, + }, + "ValidNegativeInt32": { + input: "-123", + expected: -123, + err: nil, + }, + "ExceedsInt32Limit": { + input: "2147483648", // int32 max is 2147483647 + expected: 0, + err: &strconv.NumError{Func: "ParseInt", Num: "2147483648", Err: strconv.ErrRange}, + }, + "InvalidNumber": { + input: "abc", + expected: 0, + err: &strconv.NumError{Func: "ParseInt", Num: "abc", Err: strconv.ErrSyntax}, + }, + } + + for name, scenario := range scenarios { + t.Run(name, func(t *testing.T) { + result, err := StringToInt32(scenario.input) + if result != scenario.expected { + t.Errorf("Test %q unexpected result: got (%d), want (%d)", name, result, scenario.expected) + } + if err != nil && scenario.err != nil { + if err.Error() != scenario.err.Error() { + t.Errorf("Test %q unexpected error: got (%v), want (%v)", name, err, scenario.err) + } + } else if !errors.Is(err, scenario.err) { + t.Errorf("got %v, want %v", err, scenario.err) + } + }) + } +} diff --git a/pkg/webhook/admission/localmodelcache/local_model_cache_validation.go b/pkg/webhook/admission/localmodelcache/local_model_cache_validation.go index a9d36b87cc5..1aec14d3074 100644 --- a/pkg/webhook/admission/localmodelcache/local_model_cache_validation.go +++ b/pkg/webhook/admission/localmodelcache/local_model_cache_validation.go @@ -20,21 +20,20 @@ import ( "context" "fmt" - "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" - "github.com/kserve/kserve/pkg/apis/serving/v1beta1" - "github.com/kserve/kserve/pkg/constants" "k8s.io/apimachinery/pkg/runtime" "sigs.k8s.io/controller-runtime/pkg/client" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/webhook" "sigs.k8s.io/controller-runtime/pkg/webhook/admission" -) -var ( - // logger for the validation webhook. - localModelCacheValidatorLogger = logf.Log.WithName("localmodelcache-v1alpha1-validation-webhook") + "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" + "github.com/kserve/kserve/pkg/apis/serving/v1beta1" + "github.com/kserve/kserve/pkg/constants" ) +// logger for the validation webhook. +var localModelCacheValidatorLogger = logf.Log.WithName("localmodelcache-v1alpha1-validation-webhook") + // +kubebuilder:object:generate=false // +k8s:openapi-gen=false // LocalModelCacheValidator is responsible for validating the LocalModelCache resource diff --git a/pkg/webhook/admission/localmodelcache/local_model_cache_validation_test.go b/pkg/webhook/admission/localmodelcache/local_model_cache_validation_test.go index 60d0cc0fd98..5a4ecf38788 100644 --- a/pkg/webhook/admission/localmodelcache/local_model_cache_validation_test.go +++ b/pkg/webhook/admission/localmodelcache/local_model_cache_validation_test.go @@ -20,21 +20,20 @@ import ( "fmt" "testing" - "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" - "github.com/kserve/kserve/pkg/apis/serving/v1beta1" - "github.com/kserve/kserve/pkg/constants" "github.com/onsi/gomega" "google.golang.org/protobuf/proto" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "sigs.k8s.io/controller-runtime/pkg/client/fake" -) -var ( - storageURI = "gs://testbucket/testmodel" + "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" + "github.com/kserve/kserve/pkg/apis/serving/v1beta1" + "github.com/kserve/kserve/pkg/constants" ) +var storageURI = "gs://testbucket/testmodel" + func makeTestInferenceService() v1beta1.InferenceService { inferenceservice := v1beta1.InferenceService{ ObjectMeta: metav1.ObjectMeta{ diff --git a/pkg/webhook/admission/pod/accelerator_injector.go b/pkg/webhook/admission/pod/accelerator_injector.go index 205c1edd12f..be65036142b 100644 --- a/pkg/webhook/admission/pod/accelerator_injector.go +++ b/pkg/webhook/admission/pod/accelerator_injector.go @@ -17,9 +17,10 @@ limitations under the License. package pod import ( + corev1 "k8s.io/api/core/v1" + "github.com/kserve/kserve/pkg/constants" "github.com/kserve/kserve/pkg/utils" - v1 "k8s.io/api/core/v1" ) // These constants are used for detecting and applying GPU selectors @@ -28,7 +29,7 @@ const ( NvidiaGPUTaintValue = "present" ) -func InjectGKEAcceleratorSelector(pod *v1.Pod) error { +func InjectGKEAcceleratorSelector(pod *corev1.Pod) error { gpuEnabled := false for _, container := range pod.Spec.Containers { if _, ok := container.Resources.Limits[constants.NvidiaGPUResourceType]; ok { diff --git a/pkg/webhook/admission/pod/accelerator_injector_test.go b/pkg/webhook/admission/pod/accelerator_injector_test.go index 7db7aeb1bc8..f07ddd0599d 100644 --- a/pkg/webhook/admission/pod/accelerator_injector_test.go +++ b/pkg/webhook/admission/pod/accelerator_injector_test.go @@ -19,73 +19,74 @@ import ( "testing" "github.com/google/go-cmp/cmp" - "github.com/kserve/kserve/pkg/constants" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/kserve/kserve/pkg/constants" ) func TestAcceleratorInjector(t *testing.T) { scenarios := map[string]struct { - original *v1.Pod - expected *v1.Pod + original *corev1.Pod + expected *corev1.Pod }{ "AddGPUSelector": { - original: &v1.Pod{ + original: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "deployment", Annotations: map[string]string{ constants.InferenceServiceGKEAcceleratorAnnotationKey: "nvidia-tesla-v100", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{{ - Resources: v1.ResourceRequirements{ - Limits: v1.ResourceList{constants.NvidiaGPUResourceType: resource.MustParse("1")}, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{{ + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{constants.NvidiaGPUResourceType: resource.MustParse("1")}, }, }}, }, }, - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "deployment", Annotations: map[string]string{ constants.InferenceServiceGKEAcceleratorAnnotationKey: "nvidia-tesla-v100", }, }, - Spec: v1.PodSpec{ + Spec: corev1.PodSpec{ NodeSelector: map[string]string{ GkeAcceleratorNodeSelector: "nvidia-tesla-v100", }, - Containers: []v1.Container{{ - Resources: v1.ResourceRequirements{ - Limits: v1.ResourceList{constants.NvidiaGPUResourceType: resource.MustParse("1")}, + Containers: []corev1.Container{{ + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{constants.NvidiaGPUResourceType: resource.MustParse("1")}, }, }}, }, }, }, "DoNotAddGPUSelector": { - original: &v1.Pod{ + original: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "deployment", }, - Spec: v1.PodSpec{ - Containers: []v1.Container{{ - Resources: v1.ResourceRequirements{ - Limits: v1.ResourceList{constants.NvidiaGPUResourceType: resource.MustParse("1")}, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{{ + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{constants.NvidiaGPUResourceType: resource.MustParse("1")}, }, }}, }, }, - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "deployment", }, - Spec: v1.PodSpec{ - Containers: []v1.Container{{ - Resources: v1.ResourceRequirements{ - Limits: v1.ResourceList{constants.NvidiaGPUResourceType: resource.MustParse("1")}, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{{ + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{constants.NvidiaGPUResourceType: resource.MustParse("1")}, }, }}, }, diff --git a/pkg/webhook/admission/pod/agent_injector.go b/pkg/webhook/admission/pod/agent_injector.go index 1be13294763..5e6bd39ff45 100644 --- a/pkg/webhook/admission/pod/agent_injector.go +++ b/pkg/webhook/admission/pod/agent_injector.go @@ -25,11 +25,12 @@ import ( "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/klog/v2" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + "github.com/kserve/kserve/pkg/apis/serving/v1beta1" "github.com/kserve/kserve/pkg/constants" "github.com/kserve/kserve/pkg/credentials" - v1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/resource" ) const ( @@ -74,7 +75,7 @@ type AgentInjector struct { } // TODO agent config -func getAgentConfigs(configMap *v1.ConfigMap) (*AgentConfig, error) { +func getAgentConfigs(configMap *corev1.ConfigMap) (*AgentConfig, error) { agentConfig := &AgentConfig{} if agentConfigValue, ok := configMap.Data[constants.AgentConfigMapKeyName]; ok { err := json.Unmarshal([]byte(agentConfigValue), &agentConfig) @@ -84,10 +85,12 @@ func getAgentConfigs(configMap *v1.ConfigMap) (*AgentConfig, error) { } // Ensure that we set proper values - resourceDefaults := []string{agentConfig.MemoryRequest, + resourceDefaults := []string{ + agentConfig.MemoryRequest, agentConfig.MemoryLimit, agentConfig.CpuRequest, - agentConfig.CpuLimit} + agentConfig.CpuLimit, + } for _, key := range resourceDefaults { _, err := resource.ParseQuantity(key) if err != nil { @@ -99,7 +102,7 @@ func getAgentConfigs(configMap *v1.ConfigMap) (*AgentConfig, error) { return agentConfig, nil } -func getLoggerConfigs(configMap *v1.ConfigMap) (*LoggerConfig, error) { +func getLoggerConfigs(configMap *corev1.ConfigMap) (*LoggerConfig, error) { loggerConfig := &LoggerConfig{} if loggerConfigValue, ok := configMap.Data[LoggerConfigMapKeyName]; ok { err := json.Unmarshal([]byte(loggerConfigValue), &loggerConfig) @@ -109,10 +112,12 @@ func getLoggerConfigs(configMap *v1.ConfigMap) (*LoggerConfig, error) { } // Ensure that we set proper values for CPU/Memory Limit/Request - resourceDefaults := []string{loggerConfig.MemoryRequest, + resourceDefaults := []string{ + loggerConfig.MemoryRequest, loggerConfig.MemoryLimit, loggerConfig.CpuRequest, - loggerConfig.CpuLimit} + loggerConfig.CpuLimit, + } for _, key := range resourceDefaults { _, err := resource.ParseQuantity(key) if err != nil { @@ -122,7 +127,7 @@ func getLoggerConfigs(configMap *v1.ConfigMap) (*LoggerConfig, error) { return loggerConfig, nil } -func (ag *AgentInjector) InjectAgent(pod *v1.Pod) error { +func (ag *AgentInjector) InjectAgent(pod *corev1.Pod) error { // Only inject the model agent sidecar if the required annotations are set _, injectLogger := pod.ObjectMeta.Annotations[constants.LoggerInternalAnnotationKey] _, injectPuller := pod.ObjectMeta.Annotations[constants.AgentShouldInjectAnnotationKey] @@ -217,13 +222,13 @@ func (ag *AgentInjector) InjectAgent(pod *v1.Pod) error { args = append(args, LoggerArgumentTlsSkipVerify, strconv.FormatBool(ag.loggerConfig.TlsSkipVerify)) } - var queueProxyEnvs []v1.EnvVar - var agentEnvs []v1.EnvVar + var queueProxyEnvs []corev1.EnvVar + var agentEnvs []corev1.EnvVar queueProxyAvailable := false for _, container := range pod.Spec.Containers { if container.Name == "queue-proxy" { - agentEnvs = make([]v1.EnvVar, len(container.Env)) - copy(agentEnvs, container.Env) + agentEnvs = make([]corev1.EnvVar, 0, len(container.Env)) + agentEnvs = append(agentEnvs, container.Env...) queueProxyEnvs = container.Env queueProxyAvailable = true } @@ -231,7 +236,7 @@ func (ag *AgentInjector) InjectAgent(pod *v1.Pod) error { if container.Name == "kserve-container" { containerPort := constants.InferenceServiceDefaultHttpPort if len(container.Ports) > 0 { - containerPort = fmt.Sprint(container.Ports[0].ContainerPort) + containerPort = strconv.Itoa(int(container.Ports[0].ContainerPort)) } args = append(args, "--component-port", containerPort) @@ -255,7 +260,7 @@ func (ag *AgentInjector) InjectAgent(pod *v1.Pod) error { klog.Infof("Readiness probe marshaled and added as environment variable for pod %s/%s", pod.Namespace, pod.Name) // Append the marshaled readiness probe as an environment variable for the agent container - agentEnvs = append(agentEnvs, v1.EnvVar{Name: "SERVING_READINESS_PROBE", Value: string(readinessProbeJson)}) + agentEnvs = append(agentEnvs, corev1.EnvVar{Name: "SERVING_READINESS_PROBE", Value: string(readinessProbeJson)}) } else if readinessProbe.Exec != nil { // Log the skipping of ExecAction readiness probes klog.Infof("Exec readiness probe skipped for pod %s/%s", pod.Namespace, pod.Name) @@ -275,21 +280,21 @@ func (ag *AgentInjector) InjectAgent(pod *v1.Pod) error { // Make sure securityContext is initialized and valid securityContext := pod.Spec.Containers[0].SecurityContext.DeepCopy() - agentContainer := &v1.Container{ + agentContainer := &corev1.Container{ Name: constants.AgentContainerName, Image: ag.agentConfig.Image, Args: args, - Resources: v1.ResourceRequirements{ - Limits: map[v1.ResourceName]resource.Quantity{ - v1.ResourceCPU: resource.MustParse(ag.agentConfig.CpuLimit), - v1.ResourceMemory: resource.MustParse(ag.agentConfig.MemoryLimit), + Resources: corev1.ResourceRequirements{ + Limits: map[corev1.ResourceName]resource.Quantity{ + corev1.ResourceCPU: resource.MustParse(ag.agentConfig.CpuLimit), + corev1.ResourceMemory: resource.MustParse(ag.agentConfig.MemoryLimit), }, - Requests: map[v1.ResourceName]resource.Quantity{ - v1.ResourceCPU: resource.MustParse(ag.agentConfig.CpuRequest), - v1.ResourceMemory: resource.MustParse(ag.agentConfig.MemoryRequest), + Requests: map[corev1.ResourceName]resource.Quantity{ + corev1.ResourceCPU: resource.MustParse(ag.agentConfig.CpuRequest), + corev1.ResourceMemory: resource.MustParse(ag.agentConfig.MemoryRequest), }, }, - Ports: []v1.ContainerPort{ + Ports: []corev1.ContainerPort{ { Name: "agent-port", ContainerPort: constants.InferenceServiceDefaultAgentPort, @@ -298,10 +303,10 @@ func (ag *AgentInjector) InjectAgent(pod *v1.Pod) error { }, SecurityContext: securityContext, Env: agentEnvs, - ReadinessProbe: &v1.Probe{ - ProbeHandler: v1.ProbeHandler{ - HTTPGet: &v1.HTTPGetAction{ - HTTPHeaders: []v1.HTTPHeader{ + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + HTTPHeaders: []corev1.HTTPHeader{ { Name: "K-Network-Probe", Value: "queue", @@ -319,21 +324,21 @@ func (ag *AgentInjector) InjectAgent(pod *v1.Pod) error { if injectLogger && ag.loggerConfig.CaBundle != "" { // Optional. If the ConfigMap is not found, this will not make the Pod fail optionalVolume := true - configMapVolume := v1.VolumeSource{ - ConfigMap: &v1.ConfigMapVolumeSource{ - LocalObjectReference: v1.LocalObjectReference{ + configMapVolume := corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ Name: ag.loggerConfig.CaBundle, }, Optional: &optionalVolume, }, } - pod.Spec.Volumes = append(pod.Spec.Volumes, v1.Volume{ + pod.Spec.Volumes = append(pod.Spec.Volumes, corev1.Volume{ Name: constants.LoggerCaBundleVolume, VolumeSource: configMapVolume, }) - agentContainer.VolumeMounts = append(agentContainer.VolumeMounts, v1.VolumeMount{ + agentContainer.VolumeMounts = append(agentContainer.VolumeMounts, corev1.VolumeMount{ Name: constants.LoggerCaBundleVolume, MountPath: constants.LoggerCaCertMountPath, ReadOnly: true, @@ -370,12 +375,12 @@ func (ag *AgentInjector) InjectAgent(pod *v1.Pod) error { return nil } -func mountModelDir(pod *v1.Pod) error { +func mountModelDir(pod *corev1.Pod) error { if _, ok := pod.ObjectMeta.Annotations[constants.AgentModelDirAnnotationKey]; ok { - modelDirVolume := v1.Volume{ + modelDirVolume := corev1.Volume{ Name: constants.ModelDirVolumeName, - VolumeSource: v1.VolumeSource{ - EmptyDir: &v1.EmptyDirVolumeSource{}, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, }, } // Mount the model dir into agent container @@ -387,13 +392,13 @@ func mountModelDir(pod *v1.Pod) error { return fmt.Errorf("can not find %v label", constants.AgentModelConfigVolumeNameAnnotationKey) } -func mountModelConfig(pod *v1.Pod) error { +func mountModelConfig(pod *corev1.Pod) error { if modelConfigName, ok := pod.ObjectMeta.Annotations[constants.AgentModelConfigVolumeNameAnnotationKey]; ok { - modelConfigVolume := v1.Volume{ + modelConfigVolume := corev1.Volume{ Name: constants.ModelConfigVolumeName, - VolumeSource: v1.VolumeSource{ - ConfigMap: &v1.ConfigMapVolumeSource{ - LocalObjectReference: v1.LocalObjectReference{ + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ Name: modelConfigName, }, }, @@ -405,15 +410,15 @@ func mountModelConfig(pod *v1.Pod) error { return fmt.Errorf("can not find %v label", constants.AgentModelConfigVolumeNameAnnotationKey) } -func mountVolumeToContainer(containerName string, pod *v1.Pod, additionalVolume v1.Volume, mountPath string) { +func mountVolumeToContainer(containerName string, pod *corev1.Pod, additionalVolume corev1.Volume, mountPath string) { pod.Spec.Volumes = appendVolume(pod.Spec.Volumes, additionalVolume) - mountedContainers := make([]v1.Container, 0, len(pod.Spec.Containers)) + mountedContainers := make([]corev1.Container, 0, len(pod.Spec.Containers)) for _, container := range pod.Spec.Containers { if container.Name == containerName { if container.VolumeMounts == nil { - container.VolumeMounts = []v1.VolumeMount{} + container.VolumeMounts = []corev1.VolumeMount{} } - container.VolumeMounts = append(container.VolumeMounts, v1.VolumeMount{ + container.VolumeMounts = append(container.VolumeMounts, corev1.VolumeMount{ Name: additionalVolume.Name, ReadOnly: false, MountPath: mountPath, @@ -424,9 +429,9 @@ func mountVolumeToContainer(containerName string, pod *v1.Pod, additionalVolume pod.Spec.Containers = mountedContainers } -func appendVolume(existingVolumes []v1.Volume, additionalVolume v1.Volume) []v1.Volume { +func appendVolume(existingVolumes []corev1.Volume, additionalVolume corev1.Volume) []corev1.Volume { if existingVolumes == nil { - existingVolumes = []v1.Volume{} + existingVolumes = []corev1.Volume{} } for _, volume := range existingVolumes { if volume.Name == additionalVolume.Name { diff --git a/pkg/webhook/admission/pod/agent_injector_test.go b/pkg/webhook/admission/pod/agent_injector_test.go index 3c57b2217bc..685148d315e 100644 --- a/pkg/webhook/admission/pod/agent_injector_test.go +++ b/pkg/webhook/admission/pod/agent_injector_test.go @@ -17,26 +17,25 @@ limitations under the License. package pod import ( - "k8s.io/utils/ptr" + "encoding/json" "strconv" "testing" - fakeclientset "k8s.io/client-go/kubernetes/fake" - - "github.com/kserve/kserve/pkg/apis/serving/v1beta1" - "github.com/kserve/kserve/pkg/credentials" "github.com/onsi/gomega" "github.com/onsi/gomega/types" "k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/util/intstr" - + fakeclientset "k8s.io/client-go/kubernetes/fake" + "k8s.io/utils/ptr" "knative.dev/pkg/kmp" - "encoding/json" + "github.com/kserve/kserve/pkg/apis/serving/v1beta1" + "github.com/kserve/kserve/pkg/credentials" - "github.com/kserve/kserve/pkg/constants" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/kserve/kserve/pkg/constants" ) const ( @@ -69,25 +68,25 @@ var ( batcherTestConfig = &BatcherConfig{ Image: "gcr.io/kfserving/batcher:latest", } - agentResourceRequirement = v1.ResourceRequirements{ - Limits: map[v1.ResourceName]resource.Quantity{ - v1.ResourceCPU: resource.MustParse(AgentDefaultCPULimit), - v1.ResourceMemory: resource.MustParse(AgentDefaultMemoryLimit), + agentResourceRequirement = corev1.ResourceRequirements{ + Limits: map[corev1.ResourceName]resource.Quantity{ + corev1.ResourceCPU: resource.MustParse(AgentDefaultCPULimit), + corev1.ResourceMemory: resource.MustParse(AgentDefaultMemoryLimit), }, - Requests: map[v1.ResourceName]resource.Quantity{ - v1.ResourceCPU: resource.MustParse(AgentDefaultCPURequest), - v1.ResourceMemory: resource.MustParse(AgentDefaultMemoryRequest), + Requests: map[corev1.ResourceName]resource.Quantity{ + corev1.ResourceCPU: resource.MustParse(AgentDefaultCPURequest), + corev1.ResourceMemory: resource.MustParse(AgentDefaultMemoryRequest), }, } ) func TestAgentInjector(t *testing.T) { scenarios := map[string]struct { - original *v1.Pod - expected *v1.Pod + original *corev1.Pod + expected *corev1.Pod }{ "AddAgent": { - original: &v1.Pod{ + original: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "deployment", Namespace: "default", @@ -104,14 +103,14 @@ func TestAgentInjector(t *testing.T) { constants.KServiceComponentLabel: "predictor", }, }, - Spec: v1.PodSpec{ + Spec: corev1.PodSpec{ ServiceAccountName: "sa", - Containers: []v1.Container{ + Containers: []corev1.Container{ { Name: "sklearn", - ReadinessProbe: &v1.Probe{ - ProbeHandler: v1.ProbeHandler{ - TCPSocket: &v1.TCPSocketAction{ + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + TCPSocket: &corev1.TCPSocketAction{ Port: intstr.IntOrString{ IntVal: 8080, }, @@ -127,21 +126,21 @@ func TestAgentInjector(t *testing.T) { }, }, }, - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "deployment", Annotations: map[string]string{ constants.AgentShouldInjectAnnotationKey: "true", }, }, - Spec: v1.PodSpec{ + Spec: corev1.PodSpec{ ServiceAccountName: "sa", - Containers: []v1.Container{ + Containers: []corev1.Container{ { Name: "sklearn", - ReadinessProbe: &v1.Probe{ - ProbeHandler: v1.ProbeHandler{ - TCPSocket: &v1.TCPSocketAction{ + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + TCPSocket: &corev1.TCPSocketAction{ Port: intstr.IntOrString{ IntVal: 8080, }, @@ -158,7 +157,7 @@ func TestAgentInjector(t *testing.T) { Name: constants.AgentContainerName, Image: agentConfig.Image, Resources: agentResourceRequirement, - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: constants.ModelDirVolumeName, ReadOnly: false, @@ -171,18 +170,18 @@ func TestAgentInjector(t *testing.T) { }, }, Args: []string{"--enable-puller", "--config-dir", "/mnt/configs", "--model-dir", "/mnt/models"}, - Ports: []v1.ContainerPort{ + Ports: []corev1.ContainerPort{ { Name: "agent-port", ContainerPort: constants.InferenceServiceDefaultAgentPort, Protocol: "TCP", }, }, - Env: []v1.EnvVar{{Name: "SERVING_READINESS_PROBE", Value: "{\"tcpSocket\":{\"port\":8080},\"timeoutSeconds\":1,\"periodSeconds\":10,\"successThreshold\":1,\"failureThreshold\":3}"}}, - ReadinessProbe: &v1.Probe{ - ProbeHandler: v1.ProbeHandler{ - HTTPGet: &v1.HTTPGetAction{ - HTTPHeaders: []v1.HTTPHeader{ + Env: []corev1.EnvVar{{Name: "SERVING_READINESS_PROBE", Value: "{\"tcpSocket\":{\"port\":8080},\"timeoutSeconds\":1,\"periodSeconds\":10,\"successThreshold\":1,\"failureThreshold\":3}"}}, + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + HTTPHeaders: []corev1.HTTPHeader{ { Name: "K-Network-Probe", Value: "queue", @@ -196,18 +195,18 @@ func TestAgentInjector(t *testing.T) { }, }, }, - Volumes: []v1.Volume{ + Volumes: []corev1.Volume{ { Name: "model-dir", - VolumeSource: v1.VolumeSource{ - EmptyDir: &v1.EmptyDirVolumeSource{}, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, }, }, { Name: "model-config", - VolumeSource: v1.VolumeSource{ - ConfigMap: &v1.ConfigMapVolumeSource{ - LocalObjectReference: v1.LocalObjectReference{ + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "modelconfig-deployment-0", }, }, @@ -218,29 +217,29 @@ func TestAgentInjector(t *testing.T) { }, }, "DoNotAddAgent": { - original: &v1.Pod{ + original: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "deployment", }, - Spec: v1.PodSpec{ - Containers: []v1.Container{{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{{ Name: "sklearn", }}, }, }, - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "deployment", }, - Spec: v1.PodSpec{ - Containers: []v1.Container{{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{{ Name: "sklearn", }}, }, }, }, "AddLogger": { - original: &v1.Pod{ + original: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "deployment", Namespace: "default", @@ -256,13 +255,13 @@ func TestAgentInjector(t *testing.T) { constants.KServiceComponentLabel: "predictor", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: "sklearn", - ReadinessProbe: &v1.Probe{ - ProbeHandler: v1.ProbeHandler{ - TCPSocket: &v1.TCPSocketAction{ + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + TCPSocket: &corev1.TCPSocketAction{ Port: intstr.IntOrString{ IntVal: 8080, }, @@ -277,12 +276,12 @@ func TestAgentInjector(t *testing.T) { }, { Name: "queue-proxy", - Env: []v1.EnvVar{{Name: "SERVING_READINESS_PROBE", Value: "{\"tcpSocket\":{\"port\":8080},\"timeoutSeconds\":1,\"periodSeconds\":10,\"successThreshold\":1,\"failureThreshold\":3}"}}, + Env: []corev1.EnvVar{{Name: "SERVING_READINESS_PROBE", Value: "{\"tcpSocket\":{\"port\":8080},\"timeoutSeconds\":1,\"periodSeconds\":10,\"successThreshold\":1,\"failureThreshold\":3}"}}, }, }, }, }, - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "deployment", Annotations: map[string]string{ @@ -291,13 +290,13 @@ func TestAgentInjector(t *testing.T) { constants.LoggerModeInternalAnnotationKey: string(v1beta1.LogAll), }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: "sklearn", - ReadinessProbe: &v1.Probe{ - ProbeHandler: v1.ProbeHandler{ - TCPSocket: &v1.TCPSocketAction{ + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + TCPSocket: &corev1.TCPSocketAction{ Port: intstr.IntOrString{ IntVal: 8080, }, @@ -312,7 +311,7 @@ func TestAgentInjector(t *testing.T) { }, { Name: "queue-proxy", - Env: []v1.EnvVar{{Name: "SERVING_READINESS_PROBE", Value: "{\"tcpSocket\":{\"port\":8080},\"timeoutSeconds\":1,\"periodSeconds\":10,\"successThreshold\":1,\"failureThreshold\":3}"}}, + Env: []corev1.EnvVar{{Name: "SERVING_READINESS_PROBE", Value: "{\"tcpSocket\":{\"port\":8080},\"timeoutSeconds\":1,\"periodSeconds\":10,\"successThreshold\":1,\"failureThreshold\":3}"}}, }, { Name: constants.AgentContainerName, @@ -335,19 +334,19 @@ func TestAgentInjector(t *testing.T) { LoggerArgumentTlsSkipVerify, "false", }, - Ports: []v1.ContainerPort{ + Ports: []corev1.ContainerPort{ { Name: "agent-port", ContainerPort: constants.InferenceServiceDefaultAgentPort, Protocol: "TCP", }, }, - Env: []v1.EnvVar{{Name: "SERVING_READINESS_PROBE", Value: "{\"tcpSocket\":{\"port\":8080},\"timeoutSeconds\":1,\"periodSeconds\":10,\"successThreshold\":1,\"failureThreshold\":3}"}}, + Env: []corev1.EnvVar{{Name: "SERVING_READINESS_PROBE", Value: "{\"tcpSocket\":{\"port\":8080},\"timeoutSeconds\":1,\"periodSeconds\":10,\"successThreshold\":1,\"failureThreshold\":3}"}}, Resources: agentResourceRequirement, - ReadinessProbe: &v1.Probe{ - ProbeHandler: v1.ProbeHandler{ - HTTPGet: &v1.HTTPGetAction{ - HTTPHeaders: []v1.HTTPHeader{ + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + HTTPHeaders: []corev1.HTTPHeader{ { Name: "K-Network-Probe", Value: "queue", @@ -365,7 +364,7 @@ func TestAgentInjector(t *testing.T) { }, }, "AddLoggerWithMetadata": { - original: &v1.Pod{ + original: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "deployment", Namespace: "default", @@ -382,13 +381,13 @@ func TestAgentInjector(t *testing.T) { constants.KServiceComponentLabel: "predictor", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: "sklearn", - ReadinessProbe: &v1.Probe{ - ProbeHandler: v1.ProbeHandler{ - TCPSocket: &v1.TCPSocketAction{ + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + TCPSocket: &corev1.TCPSocketAction{ Port: intstr.IntOrString{ IntVal: 8080, }, @@ -403,12 +402,12 @@ func TestAgentInjector(t *testing.T) { }, { Name: "queue-proxy", - Env: []v1.EnvVar{{Name: "SERVING_READINESS_PROBE", Value: "{\"tcpSocket\":{\"port\":8080},\"timeoutSeconds\":1,\"periodSeconds\":10,\"successThreshold\":1,\"failureThreshold\":3}"}}, + Env: []corev1.EnvVar{{Name: "SERVING_READINESS_PROBE", Value: "{\"tcpSocket\":{\"port\":8080},\"timeoutSeconds\":1,\"periodSeconds\":10,\"successThreshold\":1,\"failureThreshold\":3}"}}, }, }, }, }, - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "deployment", Annotations: map[string]string{ @@ -417,13 +416,13 @@ func TestAgentInjector(t *testing.T) { constants.LoggerModeInternalAnnotationKey: string(v1beta1.LogAll), }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: "sklearn", - ReadinessProbe: &v1.Probe{ - ProbeHandler: v1.ProbeHandler{ - TCPSocket: &v1.TCPSocketAction{ + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + TCPSocket: &corev1.TCPSocketAction{ Port: intstr.IntOrString{ IntVal: 8080, }, @@ -438,7 +437,7 @@ func TestAgentInjector(t *testing.T) { }, { Name: "queue-proxy", - Env: []v1.EnvVar{{Name: "SERVING_READINESS_PROBE", Value: "{\"tcpSocket\":{\"port\":8080},\"timeoutSeconds\":1,\"periodSeconds\":10,\"successThreshold\":1,\"failureThreshold\":3}"}}, + Env: []corev1.EnvVar{{Name: "SERVING_READINESS_PROBE", Value: "{\"tcpSocket\":{\"port\":8080},\"timeoutSeconds\":1,\"periodSeconds\":10,\"successThreshold\":1,\"failureThreshold\":3}"}}, }, { Name: constants.AgentContainerName, @@ -463,19 +462,19 @@ func TestAgentInjector(t *testing.T) { LoggerArgumentTlsSkipVerify, "false", }, - Ports: []v1.ContainerPort{ + Ports: []corev1.ContainerPort{ { Name: "agent-port", ContainerPort: constants.InferenceServiceDefaultAgentPort, Protocol: "TCP", }, }, - Env: []v1.EnvVar{{Name: "SERVING_READINESS_PROBE", Value: "{\"tcpSocket\":{\"port\":8080},\"timeoutSeconds\":1,\"periodSeconds\":10,\"successThreshold\":1,\"failureThreshold\":3}"}}, + Env: []corev1.EnvVar{{Name: "SERVING_READINESS_PROBE", Value: "{\"tcpSocket\":{\"port\":8080},\"timeoutSeconds\":1,\"periodSeconds\":10,\"successThreshold\":1,\"failureThreshold\":3}"}}, Resources: agentResourceRequirement, - ReadinessProbe: &v1.Probe{ - ProbeHandler: v1.ProbeHandler{ - HTTPGet: &v1.HTTPGetAction{ - HTTPHeaders: []v1.HTTPHeader{ + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + HTTPHeaders: []corev1.HTTPHeader{ { Name: "K-Network-Probe", Value: "queue", @@ -493,29 +492,29 @@ func TestAgentInjector(t *testing.T) { }, }, "DoNotAddLogger": { - original: &v1.Pod{ + original: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "deployment", }, - Spec: v1.PodSpec{ - Containers: []v1.Container{{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{{ Name: "sklearn", }}, }, }, - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "deployment", }, - Spec: v1.PodSpec{ - Containers: []v1.Container{{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{{ Name: "sklearn", }}, }, }, }, "AddBatcher": { - original: &v1.Pod{ + original: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "deployment", Namespace: "default", @@ -531,13 +530,13 @@ func TestAgentInjector(t *testing.T) { constants.KServiceComponentLabel: "predictor", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: "sklearn", - ReadinessProbe: &v1.Probe{ - ProbeHandler: v1.ProbeHandler{ - TCPSocket: &v1.TCPSocketAction{ + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + TCPSocket: &corev1.TCPSocketAction{ Port: intstr.IntOrString{ IntVal: 8080, }, @@ -552,12 +551,12 @@ func TestAgentInjector(t *testing.T) { }, { Name: "queue-proxy", - Env: []v1.EnvVar{{Name: "SERVING_READINESS_PROBE", Value: "{\"tcpSocket\":{\"port\":8080},\"timeoutSeconds\":1,\"periodSeconds\":10,\"successThreshold\":1,\"failureThreshold\":3}"}}, + Env: []corev1.EnvVar{{Name: "SERVING_READINESS_PROBE", Value: "{\"tcpSocket\":{\"port\":8080},\"timeoutSeconds\":1,\"periodSeconds\":10,\"successThreshold\":1,\"failureThreshold\":3}"}}, }, }, }, }, - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "deployment", Annotations: map[string]string{ @@ -566,13 +565,13 @@ func TestAgentInjector(t *testing.T) { constants.BatcherMaxBatchSizeInternalAnnotationKey: "30", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: "sklearn", - ReadinessProbe: &v1.Probe{ - ProbeHandler: v1.ProbeHandler{ - TCPSocket: &v1.TCPSocketAction{ + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + TCPSocket: &corev1.TCPSocketAction{ Port: intstr.IntOrString{ IntVal: 8080, }, @@ -587,7 +586,7 @@ func TestAgentInjector(t *testing.T) { }, { Name: "queue-proxy", - Env: []v1.EnvVar{{Name: "SERVING_READINESS_PROBE", Value: "{\"tcpSocket\":{\"port\":8080},\"timeoutSeconds\":1,\"periodSeconds\":10,\"successThreshold\":1,\"failureThreshold\":3}"}}, + Env: []corev1.EnvVar{{Name: "SERVING_READINESS_PROBE", Value: "{\"tcpSocket\":{\"port\":8080},\"timeoutSeconds\":1,\"periodSeconds\":10,\"successThreshold\":1,\"failureThreshold\":3}"}}, }, { Name: constants.AgentContainerName, @@ -599,19 +598,19 @@ func TestAgentInjector(t *testing.T) { BatcherArgumentMaxLatency, "100", }, - Ports: []v1.ContainerPort{ + Ports: []corev1.ContainerPort{ { Name: "agent-port", ContainerPort: constants.InferenceServiceDefaultAgentPort, Protocol: "TCP", }, }, - Env: []v1.EnvVar{{Name: "SERVING_READINESS_PROBE", Value: "{\"tcpSocket\":{\"port\":8080},\"timeoutSeconds\":1,\"periodSeconds\":10,\"successThreshold\":1,\"failureThreshold\":3}"}}, + Env: []corev1.EnvVar{{Name: "SERVING_READINESS_PROBE", Value: "{\"tcpSocket\":{\"port\":8080},\"timeoutSeconds\":1,\"periodSeconds\":10,\"successThreshold\":1,\"failureThreshold\":3}"}}, Resources: agentResourceRequirement, - ReadinessProbe: &v1.Probe{ - ProbeHandler: v1.ProbeHandler{ - HTTPGet: &v1.HTTPGetAction{ - HTTPHeaders: []v1.HTTPHeader{ + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + HTTPHeaders: []corev1.HTTPHeader{ { Name: "K-Network-Probe", Value: "queue", @@ -629,29 +628,29 @@ func TestAgentInjector(t *testing.T) { }, }, "DoNotAddBatcher": { - original: &v1.Pod{ + original: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "deployment", }, - Spec: v1.PodSpec{ - Containers: []v1.Container{{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{{ Name: "sklearn", }}, }, }, - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "deployment", }, - Spec: v1.PodSpec{ - Containers: []v1.Container{{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{{ Name: "sklearn", }}, }, }, }, "AgentAlreadyInjected": { - original: &v1.Pod{ + original: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "deployment", Namespace: "default", @@ -668,14 +667,14 @@ func TestAgentInjector(t *testing.T) { constants.KServiceComponentLabel: "predictor", }, }, - Spec: v1.PodSpec{ + Spec: corev1.PodSpec{ ServiceAccountName: "sa", - Containers: []v1.Container{ + Containers: []corev1.Container{ { Name: "sklearn", - ReadinessProbe: &v1.Probe{ - ProbeHandler: v1.ProbeHandler{ - TCPSocket: &v1.TCPSocketAction{ + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + TCPSocket: &corev1.TCPSocketAction{ Port: intstr.IntOrString{ IntVal: 8080, }, @@ -692,7 +691,7 @@ func TestAgentInjector(t *testing.T) { Name: constants.AgentContainerName, Image: agentConfig.Image, Resources: agentResourceRequirement, - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: constants.ModelDirVolumeName, ReadOnly: false, @@ -705,18 +704,18 @@ func TestAgentInjector(t *testing.T) { }, }, Args: []string{"--enable-puller", "--config-dir", "/mnt/configs", "--model-dir", "/mnt/models"}, - Ports: []v1.ContainerPort{ + Ports: []corev1.ContainerPort{ { Name: "agent-port", ContainerPort: constants.InferenceServiceDefaultAgentPort, Protocol: "TCP", }, }, - Env: []v1.EnvVar{{Name: "SERVING_READINESS_PROBE", Value: "{\"tcpSocket\":{\"port\":8080},\"timeoutSeconds\":1,\"periodSeconds\":10,\"successThreshold\":1,\"failureThreshold\":3}"}}, - ReadinessProbe: &v1.Probe{ - ProbeHandler: v1.ProbeHandler{ - HTTPGet: &v1.HTTPGetAction{ - HTTPHeaders: []v1.HTTPHeader{ + Env: []corev1.EnvVar{{Name: "SERVING_READINESS_PROBE", Value: "{\"tcpSocket\":{\"port\":8080},\"timeoutSeconds\":1,\"periodSeconds\":10,\"successThreshold\":1,\"failureThreshold\":3}"}}, + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + HTTPHeaders: []corev1.HTTPHeader{ { Name: "K-Network-Probe", Value: "queue", @@ -730,18 +729,18 @@ func TestAgentInjector(t *testing.T) { }, }, }, - Volumes: []v1.Volume{ + Volumes: []corev1.Volume{ { Name: "model-dir", - VolumeSource: v1.VolumeSource{ - EmptyDir: &v1.EmptyDirVolumeSource{}, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, }, }, { Name: "model-config", - VolumeSource: v1.VolumeSource{ - ConfigMap: &v1.ConfigMapVolumeSource{ - LocalObjectReference: v1.LocalObjectReference{ + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "modelconfig-deployment-0", }, }, @@ -750,21 +749,21 @@ func TestAgentInjector(t *testing.T) { }, }, }, - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "deployment", Annotations: map[string]string{ constants.AgentShouldInjectAnnotationKey: "true", }, }, - Spec: v1.PodSpec{ + Spec: corev1.PodSpec{ ServiceAccountName: "sa", - Containers: []v1.Container{ + Containers: []corev1.Container{ { Name: "sklearn", - ReadinessProbe: &v1.Probe{ - ProbeHandler: v1.ProbeHandler{ - TCPSocket: &v1.TCPSocketAction{ + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + TCPSocket: &corev1.TCPSocketAction{ Port: intstr.IntOrString{ IntVal: 8080, }, @@ -781,7 +780,7 @@ func TestAgentInjector(t *testing.T) { Name: constants.AgentContainerName, Image: agentConfig.Image, Resources: agentResourceRequirement, - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: constants.ModelDirVolumeName, ReadOnly: false, @@ -794,19 +793,20 @@ func TestAgentInjector(t *testing.T) { }, }, Args: []string{"--enable-puller", "--config-dir", "/mnt/configs", "--model-dir", "/mnt/models"}, - Ports: []v1.ContainerPort{ + Ports: []corev1.ContainerPort{ { Name: "agent-port", ContainerPort: constants.InferenceServiceDefaultAgentPort, Protocol: "TCP", }, }, - Env: []v1.EnvVar{ - {Name: "SERVING_READINESS_PROBE", Value: "{\"tcpSocket\":{\"port\":8080},\"timeoutSeconds\":1,\"periodSeconds\":10,\"successThreshold\":1,\"failureThreshold\":3}"}}, - ReadinessProbe: &v1.Probe{ - ProbeHandler: v1.ProbeHandler{ - HTTPGet: &v1.HTTPGetAction{ - HTTPHeaders: []v1.HTTPHeader{ + Env: []corev1.EnvVar{ + {Name: "SERVING_READINESS_PROBE", Value: "{\"tcpSocket\":{\"port\":8080},\"timeoutSeconds\":1,\"periodSeconds\":10,\"successThreshold\":1,\"failureThreshold\":3}"}, + }, + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + HTTPHeaders: []corev1.HTTPHeader{ { Name: "K-Network-Probe", Value: "queue", @@ -820,18 +820,18 @@ func TestAgentInjector(t *testing.T) { }, }, }, - Volumes: []v1.Volume{ + Volumes: []corev1.Volume{ { Name: "model-dir", - VolumeSource: v1.VolumeSource{ - EmptyDir: &v1.EmptyDirVolumeSource{}, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, }, }, { Name: "model-config", - VolumeSource: v1.VolumeSource{ - ConfigMap: &v1.ConfigMapVolumeSource{ - LocalObjectReference: v1.LocalObjectReference{ + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "modelconfig-deployment-0", }, }, @@ -842,7 +842,7 @@ func TestAgentInjector(t *testing.T) { }, }, "DefaultLoggerConfig": { - original: &v1.Pod{ + original: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "deployment", Namespace: "default", @@ -856,13 +856,13 @@ func TestAgentInjector(t *testing.T) { constants.KServiceComponentLabel: "predictor", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: "kserve-container", - ReadinessProbe: &v1.Probe{ - ProbeHandler: v1.ProbeHandler{ - TCPSocket: &v1.TCPSocketAction{ + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + TCPSocket: &corev1.TCPSocketAction{ Port: intstr.IntOrString{ IntVal: 8080, }, @@ -877,12 +877,12 @@ func TestAgentInjector(t *testing.T) { }, { Name: "queue-proxy", - Env: []v1.EnvVar{{Name: "SERVING_READINESS_PROBE", Value: "{\"tcpSocket\":{\"port\":8080},\"timeoutSeconds\":1,\"periodSeconds\":10,\"successThreshold\":1,\"failureThreshold\":3}"}}, + Env: []corev1.EnvVar{{Name: "SERVING_READINESS_PROBE", Value: "{\"tcpSocket\":{\"port\":8080},\"timeoutSeconds\":1,\"periodSeconds\":10,\"successThreshold\":1,\"failureThreshold\":3}"}}, }, }, }, }, - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "deployment", Annotations: map[string]string{ @@ -891,13 +891,13 @@ func TestAgentInjector(t *testing.T) { constants.LoggerModeInternalAnnotationKey: string(v1beta1.LogAll), }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: "kserve-container", - ReadinessProbe: &v1.Probe{ - ProbeHandler: v1.ProbeHandler{ - TCPSocket: &v1.TCPSocketAction{ + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + TCPSocket: &corev1.TCPSocketAction{ Port: intstr.IntOrString{ IntVal: 8080, }, @@ -912,7 +912,7 @@ func TestAgentInjector(t *testing.T) { }, { Name: "queue-proxy", - Env: []v1.EnvVar{{Name: "SERVING_READINESS_PROBE", Value: "{\"tcpSocket\":{\"port\":8080},\"timeoutSeconds\":1,\"periodSeconds\":10,\"successThreshold\":1,\"failureThreshold\":3}"}}, + Env: []corev1.EnvVar{{Name: "SERVING_READINESS_PROBE", Value: "{\"tcpSocket\":{\"port\":8080},\"timeoutSeconds\":1,\"periodSeconds\":10,\"successThreshold\":1,\"failureThreshold\":3}"}}, }, { Name: constants.AgentContainerName, @@ -937,19 +937,19 @@ func TestAgentInjector(t *testing.T) { "--component-port", constants.InferenceServiceDefaultHttpPort, }, - Ports: []v1.ContainerPort{ + Ports: []corev1.ContainerPort{ { Name: "agent-port", ContainerPort: constants.InferenceServiceDefaultAgentPort, Protocol: "TCP", }, }, - Env: []v1.EnvVar{{Name: "SERVING_READINESS_PROBE", Value: "{\"tcpSocket\":{\"port\":8080},\"timeoutSeconds\":1,\"periodSeconds\":10,\"successThreshold\":1,\"failureThreshold\":3}"}}, + Env: []corev1.EnvVar{{Name: "SERVING_READINESS_PROBE", Value: "{\"tcpSocket\":{\"port\":8080},\"timeoutSeconds\":1,\"periodSeconds\":10,\"successThreshold\":1,\"failureThreshold\":3}"}}, Resources: agentResourceRequirement, - ReadinessProbe: &v1.Probe{ - ProbeHandler: v1.ProbeHandler{ - HTTPGet: &v1.HTTPGetAction{ - HTTPHeaders: []v1.HTTPHeader{ + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + HTTPHeaders: []corev1.HTTPHeader{ { Name: "K-Network-Probe", Value: "queue", @@ -967,7 +967,7 @@ func TestAgentInjector(t *testing.T) { }, }, "QueueProxyUserPortProvided": { - original: &v1.Pod{ + original: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "deployment", Namespace: "default", @@ -981,13 +981,13 @@ func TestAgentInjector(t *testing.T) { constants.KServiceComponentLabel: "predictor", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: "kserve-container", - ReadinessProbe: &v1.Probe{ - ProbeHandler: v1.ProbeHandler{ - TCPSocket: &v1.TCPSocketAction{ + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + TCPSocket: &corev1.TCPSocketAction{ Port: intstr.IntOrString{ IntVal: 8080, }, @@ -1002,7 +1002,7 @@ func TestAgentInjector(t *testing.T) { }, { Name: "queue-proxy", - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "SERVING_READINESS_PROBE", Value: "{\"tcpSocket\":{\"port\":8080},\"timeoutSeconds\":1,\"periodSeconds\":10,\"successThreshold\":1,\"failureThreshold\":3}"}, {Name: "USER_PORT", Value: "8080"}, }, @@ -1010,7 +1010,7 @@ func TestAgentInjector(t *testing.T) { }, }, }, - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "deployment", Annotations: map[string]string{ @@ -1019,13 +1019,13 @@ func TestAgentInjector(t *testing.T) { constants.LoggerModeInternalAnnotationKey: string(v1beta1.LogAll), }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: "kserve-container", - ReadinessProbe: &v1.Probe{ - ProbeHandler: v1.ProbeHandler{ - TCPSocket: &v1.TCPSocketAction{ + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + TCPSocket: &corev1.TCPSocketAction{ Port: intstr.IntOrString{ IntVal: 8080, }, @@ -1040,7 +1040,7 @@ func TestAgentInjector(t *testing.T) { }, { Name: "queue-proxy", - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "SERVING_READINESS_PROBE", Value: "{\"tcpSocket\":{\"port\":8080},\"timeoutSeconds\":1,\"periodSeconds\":10,\"successThreshold\":1,\"failureThreshold\":3}"}, {Name: "USER_PORT", Value: constants.InferenceServiceDefaultAgentPortStr}, }, @@ -1068,22 +1068,22 @@ func TestAgentInjector(t *testing.T) { "--component-port", constants.InferenceServiceDefaultHttpPort, }, - Ports: []v1.ContainerPort{ + Ports: []corev1.ContainerPort{ { Name: "agent-port", ContainerPort: constants.InferenceServiceDefaultAgentPort, Protocol: "TCP", }, }, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "SERVING_READINESS_PROBE", Value: "{\"tcpSocket\":{\"port\":8080},\"timeoutSeconds\":1,\"periodSeconds\":10,\"successThreshold\":1,\"failureThreshold\":3}"}, {Name: "USER_PORT", Value: "8080"}, }, Resources: agentResourceRequirement, - ReadinessProbe: &v1.Probe{ - ProbeHandler: v1.ProbeHandler{ - HTTPGet: &v1.HTTPGetAction{ - HTTPHeaders: []v1.HTTPHeader{ + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + HTTPHeaders: []corev1.HTTPHeader{ { Name: "K-Network-Probe", Value: "queue", @@ -1101,7 +1101,7 @@ func TestAgentInjector(t *testing.T) { }, }, "KserveContainer has port": { - original: &v1.Pod{ + original: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "deployment", Namespace: "default", @@ -1118,14 +1118,14 @@ func TestAgentInjector(t *testing.T) { constants.KServiceComponentLabel: "predictor", }, }, - Spec: v1.PodSpec{ + Spec: corev1.PodSpec{ ServiceAccountName: "sa", - Containers: []v1.Container{ + Containers: []corev1.Container{ { Name: "kserve-container", - ReadinessProbe: &v1.Probe{ - ProbeHandler: v1.ProbeHandler{ - TCPSocket: &v1.TCPSocketAction{ + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + TCPSocket: &corev1.TCPSocketAction{ Port: intstr.IntOrString{ IntVal: 8080, }, @@ -1137,7 +1137,7 @@ func TestAgentInjector(t *testing.T) { SuccessThreshold: 1, FailureThreshold: 3, }, - Ports: []v1.ContainerPort{ + Ports: []corev1.ContainerPort{ { Name: "serving-port", ContainerPort: 80, @@ -1147,21 +1147,21 @@ func TestAgentInjector(t *testing.T) { }, }, }, - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "deployment", Annotations: map[string]string{ constants.AgentShouldInjectAnnotationKey: "true", }, }, - Spec: v1.PodSpec{ + Spec: corev1.PodSpec{ ServiceAccountName: "sa", - Containers: []v1.Container{ + Containers: []corev1.Container{ { Name: "kserve-container", - ReadinessProbe: &v1.Probe{ - ProbeHandler: v1.ProbeHandler{ - TCPSocket: &v1.TCPSocketAction{ + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + TCPSocket: &corev1.TCPSocketAction{ Port: intstr.IntOrString{ IntVal: 8080, }, @@ -1173,13 +1173,13 @@ func TestAgentInjector(t *testing.T) { SuccessThreshold: 1, FailureThreshold: 3, }, - Ports: []v1.ContainerPort{ + Ports: []corev1.ContainerPort{ { Name: "serving-port", ContainerPort: 80, }, }, - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ {Name: "model-dir", MountPath: "/mnt/models"}, }, }, @@ -1187,7 +1187,7 @@ func TestAgentInjector(t *testing.T) { Name: constants.AgentContainerName, Image: agentConfig.Image, Resources: agentResourceRequirement, - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: constants.ModelDirVolumeName, ReadOnly: false, @@ -1200,18 +1200,18 @@ func TestAgentInjector(t *testing.T) { }, }, Args: []string{"--enable-puller", "--config-dir", "/mnt/configs", "--model-dir", "/mnt/models", "--component-port", "80"}, - Ports: []v1.ContainerPort{ + Ports: []corev1.ContainerPort{ { Name: "agent-port", ContainerPort: constants.InferenceServiceDefaultAgentPort, Protocol: "TCP", }, }, - Env: []v1.EnvVar{{Name: "SERVING_READINESS_PROBE", Value: "{\"tcpSocket\":{\"port\":8080},\"timeoutSeconds\":1,\"periodSeconds\":10,\"successThreshold\":1,\"failureThreshold\":3}"}}, - ReadinessProbe: &v1.Probe{ - ProbeHandler: v1.ProbeHandler{ - HTTPGet: &v1.HTTPGetAction{ - HTTPHeaders: []v1.HTTPHeader{ + Env: []corev1.EnvVar{{Name: "SERVING_READINESS_PROBE", Value: "{\"tcpSocket\":{\"port\":8080},\"timeoutSeconds\":1,\"periodSeconds\":10,\"successThreshold\":1,\"failureThreshold\":3}"}}, + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + HTTPHeaders: []corev1.HTTPHeader{ { Name: "K-Network-Probe", Value: "queue", @@ -1225,18 +1225,18 @@ func TestAgentInjector(t *testing.T) { }, }, }, - Volumes: []v1.Volume{ + Volumes: []corev1.Volume{ { Name: "model-dir", - VolumeSource: v1.VolumeSource{ - EmptyDir: &v1.EmptyDirVolumeSource{}, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, }, }, { Name: "model-config", - VolumeSource: v1.VolumeSource{ - ConfigMap: &v1.ConfigMapVolumeSource{ - LocalObjectReference: v1.LocalObjectReference{ + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "modelconfig-deployment-0", }, }, @@ -1248,11 +1248,11 @@ func TestAgentInjector(t *testing.T) { }, } scenariosTls := map[string]struct { - original *v1.Pod - expected *v1.Pod + original *corev1.Pod + expected *corev1.Pod }{ "AddLogger": { - original: &v1.Pod{ + original: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "deployment", Namespace: "default", @@ -1268,13 +1268,13 @@ func TestAgentInjector(t *testing.T) { constants.KServiceComponentLabel: "predictor", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: "sklearn", - ReadinessProbe: &v1.Probe{ - ProbeHandler: v1.ProbeHandler{ - TCPSocket: &v1.TCPSocketAction{ + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + TCPSocket: &corev1.TCPSocketAction{ Port: intstr.IntOrString{ IntVal: 8080, }, @@ -1289,12 +1289,12 @@ func TestAgentInjector(t *testing.T) { }, { Name: "queue-proxy", - Env: []v1.EnvVar{{Name: "SERVING_READINESS_PROBE", Value: "{\"tcpSocket\":{\"port\":8080},\"timeoutSeconds\":1,\"periodSeconds\":10,\"successThreshold\":1,\"failureThreshold\":3}"}}, + Env: []corev1.EnvVar{{Name: "SERVING_READINESS_PROBE", Value: "{\"tcpSocket\":{\"port\":8080},\"timeoutSeconds\":1,\"periodSeconds\":10,\"successThreshold\":1,\"failureThreshold\":3}"}}, }, }, }, }, - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "deployment", Annotations: map[string]string{ @@ -1303,13 +1303,13 @@ func TestAgentInjector(t *testing.T) { constants.LoggerModeInternalAnnotationKey: string(v1beta1.LogAll), }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: "sklearn", - ReadinessProbe: &v1.Probe{ - ProbeHandler: v1.ProbeHandler{ - TCPSocket: &v1.TCPSocketAction{ + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + TCPSocket: &corev1.TCPSocketAction{ Port: intstr.IntOrString{ IntVal: 8080, }, @@ -1324,7 +1324,7 @@ func TestAgentInjector(t *testing.T) { }, { Name: "queue-proxy", - Env: []v1.EnvVar{{Name: "SERVING_READINESS_PROBE", Value: "{\"tcpSocket\":{\"port\":8080},\"timeoutSeconds\":1,\"periodSeconds\":10,\"successThreshold\":1,\"failureThreshold\":3}"}}, + Env: []corev1.EnvVar{{Name: "SERVING_READINESS_PROBE", Value: "{\"tcpSocket\":{\"port\":8080},\"timeoutSeconds\":1,\"periodSeconds\":10,\"successThreshold\":1,\"failureThreshold\":3}"}}, }, { Name: constants.AgentContainerName, @@ -1349,19 +1349,19 @@ func TestAgentInjector(t *testing.T) { LoggerArgumentTlsSkipVerify, strconv.FormatBool(loggerTLSConfig.TlsSkipVerify), }, - Ports: []v1.ContainerPort{ + Ports: []corev1.ContainerPort{ { Name: "agent-port", ContainerPort: constants.InferenceServiceDefaultAgentPort, Protocol: "TCP", }, }, - Env: []v1.EnvVar{{Name: "SERVING_READINESS_PROBE", Value: "{\"tcpSocket\":{\"port\":8080},\"timeoutSeconds\":1,\"periodSeconds\":10,\"successThreshold\":1,\"failureThreshold\":3}"}}, + Env: []corev1.EnvVar{{Name: "SERVING_READINESS_PROBE", Value: "{\"tcpSocket\":{\"port\":8080},\"timeoutSeconds\":1,\"periodSeconds\":10,\"successThreshold\":1,\"failureThreshold\":3}"}}, Resources: agentResourceRequirement, - ReadinessProbe: &v1.Probe{ - ProbeHandler: v1.ProbeHandler{ - HTTPGet: &v1.HTTPGetAction{ - HTTPHeaders: []v1.HTTPHeader{ + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + HTTPHeaders: []corev1.HTTPHeader{ { Name: "K-Network-Probe", Value: "queue", @@ -1373,7 +1373,7 @@ func TestAgentInjector(t *testing.T) { }, }, }, - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: constants.LoggerCaBundleVolume, ReadOnly: true, @@ -1382,12 +1382,12 @@ func TestAgentInjector(t *testing.T) { }, }, }, - Volumes: []v1.Volume{ + Volumes: []corev1.Volume{ { Name: constants.LoggerCaBundleVolume, - VolumeSource: v1.VolumeSource{ - ConfigMap: &v1.ConfigMapVolumeSource{ - LocalObjectReference: v1.LocalObjectReference{ + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ Name: loggerTLSConfig.CaBundle, }, Optional: ptr.To(true), @@ -1400,7 +1400,7 @@ func TestAgentInjector(t *testing.T) { }, } clientset := fakeclientset.NewSimpleClientset() - credentialBuilder := credentials.NewCredentialBuilder(c, clientset, &v1.ConfigMap{ + credentialBuilder := credentials.NewCredentialBuilder(c, clientset, &corev1.ConfigMap{ Data: map[string]string{}, }) @@ -1448,12 +1448,12 @@ func TestGetLoggerConfigs(t *testing.T) { g := gomega.NewGomegaWithT(t) cases := []struct { name string - configMap *v1.ConfigMap + configMap *corev1.ConfigMap matchers []types.GomegaMatcher }{ { name: "Valid Logger Config", - configMap: &v1.ConfigMap{ + configMap: &corev1.ConfigMap{ TypeMeta: metav1.TypeMeta{}, ObjectMeta: metav1.ObjectMeta{}, Data: map[string]string{ @@ -1480,7 +1480,7 @@ func TestGetLoggerConfigs(t *testing.T) { }, { name: "Invalid Resource Value", - configMap: &v1.ConfigMap{ + configMap: &corev1.ConfigMap{ TypeMeta: metav1.TypeMeta{}, ObjectMeta: metav1.ObjectMeta{}, Data: map[string]string{ @@ -1518,12 +1518,12 @@ func TestGetAgentConfigs(t *testing.T) { g := gomega.NewGomegaWithT(t) cases := []struct { name string - configMap *v1.ConfigMap + configMap *corev1.ConfigMap matchers []types.GomegaMatcher }{ { name: "Valid Agent Config", - configMap: &v1.ConfigMap{ + configMap: &corev1.ConfigMap{ TypeMeta: metav1.TypeMeta{}, ObjectMeta: metav1.ObjectMeta{}, Data: map[string]string{ @@ -1550,7 +1550,7 @@ func TestGetAgentConfigs(t *testing.T) { }, { name: "Invalid Resource Value", - configMap: &v1.ConfigMap{ + configMap: &corev1.ConfigMap{ TypeMeta: metav1.TypeMeta{}, ObjectMeta: metav1.ObjectMeta{}, Data: map[string]string{ @@ -1587,16 +1587,16 @@ func TestGetAgentConfigs(t *testing.T) { func TestReadinessProbeInheritance(t *testing.T) { tests := []struct { name string - readinessProbe *v1.Probe + readinessProbe *corev1.Probe queueProxyAvailable bool expectEnvVar bool expectedProbeJson string }{ { name: "HTTPGet Readiness Probe", - readinessProbe: &v1.Probe{ - ProbeHandler: v1.ProbeHandler{ - HTTPGet: &v1.HTTPGetAction{ + readinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ Path: "/ready", Port: intstr.FromInt(8080), Scheme: "HTTP", @@ -1613,9 +1613,9 @@ func TestReadinessProbeInheritance(t *testing.T) { }, { name: "TCPSocket Readiness Probe", - readinessProbe: &v1.Probe{ - ProbeHandler: v1.ProbeHandler{ - TCPSocket: &v1.TCPSocketAction{ + readinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + TCPSocket: &corev1.TCPSocketAction{ Port: intstr.FromInt(8080), }, }, @@ -1630,9 +1630,9 @@ func TestReadinessProbeInheritance(t *testing.T) { }, { name: "Exec Readiness Probe", - readinessProbe: &v1.Probe{ - ProbeHandler: v1.ProbeHandler{ - Exec: &v1.ExecAction{ + readinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + Exec: &corev1.ExecAction{ Command: []string{"echo", "hello"}, }, }, @@ -1645,13 +1645,13 @@ func TestReadinessProbeInheritance(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Prepare the pod with the given readiness probe - pod := &v1.Pod{ + pod := &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "test-pod", Namespace: "default", }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: "test-container", ReadinessProbe: tt.readinessProbe, @@ -1660,7 +1660,7 @@ func TestReadinessProbeInheritance(t *testing.T) { }, } - var agentEnvs []v1.EnvVar + var agentEnvs []corev1.EnvVar if !tt.queueProxyAvailable { readinessProbe := pod.Spec.Containers[0].ReadinessProbe @@ -1671,7 +1671,7 @@ func TestReadinessProbeInheritance(t *testing.T) { if err != nil { t.Errorf("failed to marshal readiness probe: %v", err) } else { - agentEnvs = append(agentEnvs, v1.EnvVar{Name: "SERVING_READINESS_PROBE", Value: readinessProbeJson}) + agentEnvs = append(agentEnvs, corev1.EnvVar{Name: "SERVING_READINESS_PROBE", Value: readinessProbeJson}) } } else if readinessProbe.Exec != nil { // Exec probes are skipped; log the information @@ -1702,19 +1702,19 @@ func TestReadinessProbeInheritance(t *testing.T) { } } -func marshalReadinessProbe(probe *v1.Probe) (string, error) { +func marshalReadinessProbe(probe *corev1.Probe) (string, error) { if probe == nil { return "", nil } // Create a custom struct to ensure all fields are included type ReadinessProbe struct { - HTTPGet *v1.HTTPGetAction `json:"httpGet,omitempty"` - TCPSocket *v1.TCPSocketAction `json:"tcpSocket,omitempty"` - TimeoutSeconds int32 `json:"timeoutSeconds"` - PeriodSeconds int32 `json:"periodSeconds"` - SuccessThreshold int32 `json:"successThreshold"` - FailureThreshold int32 `json:"failureThreshold"` + HTTPGet *corev1.HTTPGetAction `json:"httpGet,omitempty"` + TCPSocket *corev1.TCPSocketAction `json:"tcpSocket,omitempty"` + TimeoutSeconds int32 `json:"timeoutSeconds"` + PeriodSeconds int32 `json:"periodSeconds"` + SuccessThreshold int32 `json:"successThreshold"` + FailureThreshold int32 `json:"failureThreshold"` } readinessProbe := ReadinessProbe{ diff --git a/pkg/webhook/admission/pod/batcher_injector.go b/pkg/webhook/admission/pod/batcher_injector.go index 70c8f841b52..3cfffb55e25 100644 --- a/pkg/webhook/admission/pod/batcher_injector.go +++ b/pkg/webhook/admission/pod/batcher_injector.go @@ -21,9 +21,10 @@ import ( "fmt" "strings" - "github.com/kserve/kserve/pkg/constants" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" + + "github.com/kserve/kserve/pkg/constants" ) const ( @@ -48,7 +49,7 @@ type BatcherInjector struct { config *BatcherConfig } -func getBatcherConfigs(configMap *v1.ConfigMap) (*BatcherConfig, error) { +func getBatcherConfigs(configMap *corev1.ConfigMap) (*BatcherConfig, error) { batcherConfig := &BatcherConfig{} if batcherConfigValue, ok := configMap.Data[BatcherConfigMapKeyName]; ok { err := json.Unmarshal([]byte(batcherConfigValue), &batcherConfig) @@ -58,10 +59,12 @@ func getBatcherConfigs(configMap *v1.ConfigMap) (*BatcherConfig, error) { } // Ensure that we set proper values for CPU/Memory Limit/Request - resourceDefaults := []string{batcherConfig.MemoryRequest, + resourceDefaults := []string{ + batcherConfig.MemoryRequest, batcherConfig.MemoryLimit, batcherConfig.CpuRequest, - batcherConfig.CpuLimit} + batcherConfig.CpuLimit, + } for _, key := range resourceDefaults { _, err := resource.ParseQuantity(key) if err != nil { @@ -73,7 +76,7 @@ func getBatcherConfigs(configMap *v1.ConfigMap) (*BatcherConfig, error) { return batcherConfig, nil } -func (il *BatcherInjector) InjectBatcher(pod *v1.Pod) error { +func (il *BatcherInjector) InjectBatcher(pod *corev1.Pod) error { // Only inject if the required annotations are set _, ok := pod.ObjectMeta.Annotations[constants.BatcherInternalAnnotationKey] if !ok { @@ -110,18 +113,18 @@ func (il *BatcherInjector) InjectBatcher(pod *v1.Pod) error { // Make sure securityContext is initialized and valid securityContext := pod.Spec.Containers[0].SecurityContext.DeepCopy() - batcherContainer := &v1.Container{ + batcherContainer := &corev1.Container{ Name: BatcherContainerName, Image: il.config.Image, Args: args, - Resources: v1.ResourceRequirements{ - Limits: map[v1.ResourceName]resource.Quantity{ - v1.ResourceCPU: resource.MustParse(il.config.CpuLimit), - v1.ResourceMemory: resource.MustParse(il.config.MemoryLimit), + Resources: corev1.ResourceRequirements{ + Limits: map[corev1.ResourceName]resource.Quantity{ + corev1.ResourceCPU: resource.MustParse(il.config.CpuLimit), + corev1.ResourceMemory: resource.MustParse(il.config.MemoryLimit), }, - Requests: map[v1.ResourceName]resource.Quantity{ - v1.ResourceCPU: resource.MustParse(il.config.CpuRequest), - v1.ResourceMemory: resource.MustParse(il.config.MemoryRequest), + Requests: map[corev1.ResourceName]resource.Quantity{ + corev1.ResourceCPU: resource.MustParse(il.config.CpuRequest), + corev1.ResourceMemory: resource.MustParse(il.config.MemoryRequest), }, }, SecurityContext: securityContext, diff --git a/pkg/webhook/admission/pod/batcher_injector_test.go b/pkg/webhook/admission/pod/batcher_injector_test.go index 3b3f97ca6e5..49b400cf24d 100644 --- a/pkg/webhook/admission/pod/batcher_injector_test.go +++ b/pkg/webhook/admission/pod/batcher_injector_test.go @@ -21,7 +21,7 @@ import ( "github.com/onsi/gomega" "github.com/onsi/gomega/types" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "knative.dev/pkg/kmp" @@ -49,25 +49,25 @@ var ( MaxLatency: BatcherDefaultMaxLatency, } - batcherResourceRequirement = v1.ResourceRequirements{ - Limits: map[v1.ResourceName]resource.Quantity{ - v1.ResourceCPU: resource.MustParse(BatcherDefaultCPULimit), - v1.ResourceMemory: resource.MustParse(BatcherDefaultMemoryLimit), + batcherResourceRequirement = corev1.ResourceRequirements{ + Limits: map[corev1.ResourceName]resource.Quantity{ + corev1.ResourceCPU: resource.MustParse(BatcherDefaultCPULimit), + corev1.ResourceMemory: resource.MustParse(BatcherDefaultMemoryLimit), }, - Requests: map[v1.ResourceName]resource.Quantity{ - v1.ResourceCPU: resource.MustParse(BatcherDefaultCPURequest), - v1.ResourceMemory: resource.MustParse(BatcherDefaultMemoryRequest), + Requests: map[corev1.ResourceName]resource.Quantity{ + corev1.ResourceCPU: resource.MustParse(BatcherDefaultCPURequest), + corev1.ResourceMemory: resource.MustParse(BatcherDefaultMemoryRequest), }, } ) func TestBatcherInjector(t *testing.T) { scenarios := map[string]struct { - original *v1.Pod - expected *v1.Pod + original *corev1.Pod + expected *corev1.Pod }{ "AddBatcher": { - original: &v1.Pod{ + original: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "deployment", Namespace: "default", @@ -83,13 +83,13 @@ func TestBatcherInjector(t *testing.T) { constants.KServiceComponentLabel: "predictor", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{{ Name: "sklearn", }}, }, }, - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "deployment", Annotations: map[string]string{ @@ -98,8 +98,8 @@ func TestBatcherInjector(t *testing.T) { constants.BatcherMaxLatencyInternalAnnotationKey: "5000", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: "sklearn", }, @@ -119,7 +119,7 @@ func TestBatcherInjector(t *testing.T) { }, }, "AddDefaultBatcherConfig": { - original: &v1.Pod{ + original: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "deployment", Namespace: "default", @@ -133,13 +133,13 @@ func TestBatcherInjector(t *testing.T) { constants.KServiceComponentLabel: "predictor", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{{ Name: "sklearn", }}, }, }, - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "deployment", Annotations: map[string]string{ @@ -148,8 +148,8 @@ func TestBatcherInjector(t *testing.T) { constants.BatcherMaxLatencyInternalAnnotationKey: BatcherDefaultMaxLatency, }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: "sklearn", }, @@ -169,22 +169,22 @@ func TestBatcherInjector(t *testing.T) { }, }, "DoNotAddBatcher": { - original: &v1.Pod{ + original: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "deployment", }, - Spec: v1.PodSpec{ - Containers: []v1.Container{{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{{ Name: "sklearn", }}, }, }, - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "deployment", }, - Spec: v1.PodSpec{ - Containers: []v1.Container{{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{{ Name: "sklearn", }}, }, @@ -207,12 +207,12 @@ func TestGetBatcherConfigs(t *testing.T) { g := gomega.NewGomegaWithT(t) cases := []struct { name string - configMap *v1.ConfigMap + configMap *corev1.ConfigMap matchers []types.GomegaMatcher }{ { name: "Valid Batcher Config", - configMap: &v1.ConfigMap{ + configMap: &corev1.ConfigMap{ TypeMeta: metav1.TypeMeta{}, ObjectMeta: metav1.ObjectMeta{}, Data: map[string]string{ @@ -239,7 +239,7 @@ func TestGetBatcherConfigs(t *testing.T) { }, { name: "Default Batcher Config", - configMap: &v1.ConfigMap{ + configMap: &corev1.ConfigMap{ TypeMeta: metav1.TypeMeta{}, ObjectMeta: metav1.ObjectMeta{}, Data: map[string]string{ @@ -270,7 +270,7 @@ func TestGetBatcherConfigs(t *testing.T) { }, { name: "Invalid Resource Value", - configMap: &v1.ConfigMap{ + configMap: &corev1.ConfigMap{ TypeMeta: metav1.TypeMeta{}, ObjectMeta: metav1.ObjectMeta{}, Data: map[string]string{ diff --git a/pkg/webhook/admission/pod/metrics_aggregate_injector.go b/pkg/webhook/admission/pod/metrics_aggregate_injector.go index 9fe98c9afab..286cbb3b626 100644 --- a/pkg/webhook/admission/pod/metrics_aggregate_injector.go +++ b/pkg/webhook/admission/pod/metrics_aggregate_injector.go @@ -19,11 +19,11 @@ package pod import ( "encoding/json" "fmt" - "strconv" + + corev1 "k8s.io/api/core/v1" "github.com/kserve/kserve/pkg/constants" "github.com/kserve/kserve/pkg/utils" - v1 "k8s.io/api/core/v1" ) const ( @@ -36,7 +36,7 @@ type MetricsAggregator struct { EnablePrometheusScraping string `json:"enablePrometheusScraping"` } -func newMetricsAggregator(configMap *v1.ConfigMap) (*MetricsAggregator, error) { //nolint:unparam +func newMetricsAggregator(configMap *corev1.ConfigMap) *MetricsAggregator { ma := &MetricsAggregator{} if maConfigVal, ok := configMap.Data[MetricsAggregatorConfigMapKeyName]; ok { @@ -46,10 +46,10 @@ func newMetricsAggregator(configMap *v1.ConfigMap) (*MetricsAggregator, error) { } } - return ma, nil + return ma } -func setMetricAggregationEnvVarsAndPorts(pod *v1.Pod) { +func setMetricAggregationEnvVarsAndPorts(pod *corev1.Pod) error { for i, container := range pod.Spec.Containers { if container.Name == "queue-proxy" { // The kserve-container prometheus port/path is inherited from the ClusterServingRuntime YAML. @@ -66,24 +66,31 @@ func setMetricAggregationEnvVarsAndPorts(pod *v1.Pod) { // The kserve container port/path is set as an EnvVar in the queue-proxy container // so that it knows which port/path to scrape from the kserve-container. - pod.Spec.Containers[i].Env = append(pod.Spec.Containers[i].Env, v1.EnvVar{Name: constants.KServeContainerPrometheusMetricsPortEnvVarKey, Value: kserveContainerPromPort}) - pod.Spec.Containers[i].Env = append(pod.Spec.Containers[i].Env, v1.EnvVar{Name: constants.KServeContainerPrometheusMetricsPathEnvVarKey, Value: kserveContainerPromPath}) + pod.Spec.Containers[i].Env = append(pod.Spec.Containers[i].Env, corev1.EnvVar{Name: constants.KServeContainerPrometheusMetricsPortEnvVarKey, Value: kserveContainerPromPort}) + pod.Spec.Containers[i].Env = append(pod.Spec.Containers[i].Env, corev1.EnvVar{Name: constants.KServeContainerPrometheusMetricsPathEnvVarKey, Value: kserveContainerPromPath}) // Set the port that queue-proxy will use to expose the aggregate metrics. - pod.Spec.Containers[i].Env = append(pod.Spec.Containers[i].Env, v1.EnvVar{Name: constants.QueueProxyAggregatePrometheusMetricsPortEnvVarKey, Value: strconv.Itoa(constants.QueueProxyAggregatePrometheusMetricsPort)}) - - pod.Spec.Containers[i].Ports = utils.AppendPortIfNotExists(pod.Spec.Containers[i].Ports, v1.ContainerPort{ + pod.Spec.Containers[i].Env = append(pod.Spec.Containers[i].Env, corev1.EnvVar{ + Name: constants.QueueProxyAggregatePrometheusMetricsPortEnvVarKey, + Value: constants.QueueProxyAggregatePrometheusMetricsPort, + }) + aggrPort, err := utils.StringToInt32(constants.QueueProxyAggregatePrometheusMetricsPort) + if err != nil { + return err + } + pod.Spec.Containers[i].Ports = utils.AppendPortIfNotExists(pod.Spec.Containers[i].Ports, corev1.ContainerPort{ Name: constants.AggregateMetricsPortName, - ContainerPort: int32(constants.QueueProxyAggregatePrometheusMetricsPort), + ContainerPort: aggrPort, Protocol: "TCP", }) } } + return nil } // InjectMetricsAggregator looks for the annotations to enable aggregate kserve-container and queue-proxy metrics and // if specified, sets port-related EnvVars in queue-proxy and the aggregate prometheus annotation. -func (ma *MetricsAggregator) InjectMetricsAggregator(pod *v1.Pod) error { +func (ma *MetricsAggregator) InjectMetricsAggregator(pod *corev1.Pod) error { // Only set metric configs if the required annotations are set enableMetricAggregation, ok := pod.ObjectMeta.Annotations[constants.EnableMetricAggregation] if !ok { @@ -94,7 +101,10 @@ func (ma *MetricsAggregator) InjectMetricsAggregator(pod *v1.Pod) error { enableMetricAggregation = ma.EnableMetricAggregation } if enableMetricAggregation == "true" { - setMetricAggregationEnvVarsAndPorts(pod) + err := setMetricAggregationEnvVarsAndPorts(pod) + if err != nil { + return err + } } // Handle setting the pod prometheus annotations @@ -108,7 +118,7 @@ func (ma *MetricsAggregator) InjectMetricsAggregator(pod *v1.Pod) error { // If enableMetricAggregation is true, set it as the queue proxy metrics aggregation port. podPromPort := constants.DefaultPodPrometheusPort if enableMetricAggregation == "true" { - podPromPort = strconv.Itoa(constants.QueueProxyAggregatePrometheusMetricsPort) + podPromPort = constants.QueueProxyAggregatePrometheusMetricsPort } pod.ObjectMeta.Annotations[constants.PrometheusPortAnnotationKey] = podPromPort pod.ObjectMeta.Annotations[constants.PrometheusPathAnnotationKey] = constants.DefaultPrometheusPath diff --git a/pkg/webhook/admission/pod/metrics_aggregate_injector_test.go b/pkg/webhook/admission/pod/metrics_aggregate_injector_test.go index dc542672503..d939e7ab5b8 100644 --- a/pkg/webhook/admission/pod/metrics_aggregate_injector_test.go +++ b/pkg/webhook/admission/pod/metrics_aggregate_injector_test.go @@ -16,24 +16,29 @@ limitations under the License. package pod import ( - "github.com/kserve/kserve/pkg/constants" - v1 "k8s.io/api/core/v1" + "testing" + + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "knative.dev/pkg/kmp" - "strconv" - "testing" + "github.com/kserve/kserve/pkg/constants" + "github.com/kserve/kserve/pkg/utils" ) const sklearnPrometheusPort = "8080" func TestInjectMetricsAggregator(t *testing.T) { + qpextAggregateMetricsPort, err := utils.StringToInt32(constants.QueueProxyAggregatePrometheusMetricsPort) + if err != nil { + t.Errorf("Error converting string to int32 %v", err) + } scenarios := map[string]struct { - original *v1.Pod - expected *v1.Pod + original *corev1.Pod + expected *corev1.Pod }{ "EnableMetricAggTrue": { - original: &v1.Pod{ + original: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "deployment", Namespace: "default", @@ -41,18 +46,19 @@ func TestInjectMetricsAggregator(t *testing.T) { constants.EnableMetricAggregation: "true", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{{ - Name: "sklearn", - }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "sklearn", + }, { Name: "queue-proxy", - Ports: []v1.ContainerPort{{Name: "http-usermetric", ContainerPort: 9091, Protocol: "TCP"}}, + Ports: []corev1.ContainerPort{{Name: "http-usermetric", ContainerPort: 9091, Protocol: "TCP"}}, }, }, }, }, - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "deployment", Namespace: "default", @@ -60,20 +66,21 @@ func TestInjectMetricsAggregator(t *testing.T) { constants.EnableMetricAggregation: "true", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{{ - Name: "sklearn", - }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "sklearn", + }, { Name: "queue-proxy", - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ {Name: constants.KServeContainerPrometheusMetricsPortEnvVarKey, Value: sklearnPrometheusPort}, {Name: constants.KServeContainerPrometheusMetricsPathEnvVarKey, Value: constants.DefaultPrometheusPath}, - {Name: constants.QueueProxyAggregatePrometheusMetricsPortEnvVarKey, Value: strconv.Itoa(constants.QueueProxyAggregatePrometheusMetricsPort)}, + {Name: constants.QueueProxyAggregatePrometheusMetricsPortEnvVarKey, Value: constants.QueueProxyAggregatePrometheusMetricsPort}, }, - Ports: []v1.ContainerPort{ + Ports: []corev1.ContainerPort{ {Name: "http-usermetric", ContainerPort: 9091, Protocol: "TCP"}, - {Name: constants.AggregateMetricsPortName, ContainerPort: int32(constants.QueueProxyAggregatePrometheusMetricsPort), Protocol: "TCP"}, + {Name: constants.AggregateMetricsPortName, ContainerPort: qpextAggregateMetricsPort, Protocol: "TCP"}, }, }, }, @@ -81,23 +88,24 @@ func TestInjectMetricsAggregator(t *testing.T) { }, }, "EnableMetricAggNotSet": { - original: &v1.Pod{ + original: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "deployment", Namespace: "default", }, - Spec: v1.PodSpec{ - Containers: []v1.Container{{ - Name: "sklearn", - }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "sklearn", + }, { Name: "queue-proxy", - Ports: []v1.ContainerPort{{Name: "http-usermetric", ContainerPort: 9091, Protocol: "TCP"}}, + Ports: []corev1.ContainerPort{{Name: "http-usermetric", ContainerPort: 9091, Protocol: "TCP"}}, }, }, }, }, - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "deployment", Namespace: "default", @@ -105,20 +113,21 @@ func TestInjectMetricsAggregator(t *testing.T) { constants.EnableMetricAggregation: "false", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{{ - Name: "sklearn", - }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "sklearn", + }, { Name: "queue-proxy", - Ports: []v1.ContainerPort{{Name: "http-usermetric", ContainerPort: 9091, Protocol: "TCP"}}, + Ports: []corev1.ContainerPort{{Name: "http-usermetric", ContainerPort: 9091, Protocol: "TCP"}}, }, }, }, }, }, "EnableMetricAggFalse": { - original: &v1.Pod{ + original: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "deployment", Namespace: "default", @@ -126,18 +135,19 @@ func TestInjectMetricsAggregator(t *testing.T) { constants.EnableMetricAggregation: "false", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{{ - Name: "sklearn", - }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "sklearn", + }, { Name: "queue-proxy", - Ports: []v1.ContainerPort{{Name: "http-usermetric", ContainerPort: 9091, Protocol: "TCP"}}, + Ports: []corev1.ContainerPort{{Name: "http-usermetric", ContainerPort: 9091, Protocol: "TCP"}}, }, }, }, }, - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "deployment", Namespace: "default", @@ -145,20 +155,21 @@ func TestInjectMetricsAggregator(t *testing.T) { constants.EnableMetricAggregation: "true", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{{ - Name: "sklearn", - }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "sklearn", + }, { Name: "queue-proxy", - Ports: []v1.ContainerPort{{Name: "http-usermetric", ContainerPort: 9091, Protocol: "TCP"}}, + Ports: []corev1.ContainerPort{{Name: "http-usermetric", ContainerPort: 9091, Protocol: "TCP"}}, }, }, }, }, }, "setPromAnnotationTrueWithAggTrue": { - original: &v1.Pod{ + original: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "deployment", Namespace: "default", @@ -167,42 +178,44 @@ func TestInjectMetricsAggregator(t *testing.T) { constants.SetPrometheusAnnotation: "true", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{{ - Name: "sklearn", - }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "sklearn", + }, { Name: "queue-proxy", - Ports: []v1.ContainerPort{{Name: "http-usermetric", ContainerPort: 9091, Protocol: "TCP"}}, + Ports: []corev1.ContainerPort{{Name: "http-usermetric", ContainerPort: 9091, Protocol: "TCP"}}, }, }, }, }, - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "deployment", Namespace: "default", Annotations: map[string]string{ constants.EnableMetricAggregation: "true", constants.SetPrometheusAnnotation: "true", - constants.PrometheusPortAnnotationKey: strconv.Itoa(constants.QueueProxyAggregatePrometheusMetricsPort), + constants.PrometheusPortAnnotationKey: constants.QueueProxyAggregatePrometheusMetricsPort, constants.PrometheusPathAnnotationKey: constants.DefaultPrometheusPath, }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{{ - Name: "sklearn", - }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "sklearn", + }, { Name: "queue-proxy", - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ {Name: constants.KServeContainerPrometheusMetricsPortEnvVarKey, Value: sklearnPrometheusPort}, {Name: constants.KServeContainerPrometheusMetricsPathEnvVarKey, Value: constants.DefaultPrometheusPath}, - {Name: constants.QueueProxyAggregatePrometheusMetricsPortEnvVarKey, Value: strconv.Itoa(constants.QueueProxyAggregatePrometheusMetricsPort)}, + {Name: constants.QueueProxyAggregatePrometheusMetricsPortEnvVarKey, Value: constants.QueueProxyAggregatePrometheusMetricsPort}, }, - Ports: []v1.ContainerPort{ + Ports: []corev1.ContainerPort{ {Name: "http-usermetric", ContainerPort: 9091, Protocol: "TCP"}, - {Name: constants.AggregateMetricsPortName, ContainerPort: int32(constants.QueueProxyAggregatePrometheusMetricsPort), Protocol: "TCP"}, + {Name: constants.AggregateMetricsPortName, ContainerPort: qpextAggregateMetricsPort, Protocol: "TCP"}, }, }, }, @@ -210,7 +223,7 @@ func TestInjectMetricsAggregator(t *testing.T) { }, }, "setPromAnnotationTrueWithAggFalse": { - original: &v1.Pod{ + original: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "deployment", Namespace: "default", @@ -219,18 +232,19 @@ func TestInjectMetricsAggregator(t *testing.T) { constants.SetPrometheusAnnotation: "true", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{{ - Name: "sklearn", - }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "sklearn", + }, { Name: "queue-proxy", - Ports: []v1.ContainerPort{{Name: "http-usermetric", ContainerPort: 9091, Protocol: "TCP"}}, + Ports: []corev1.ContainerPort{{Name: "http-usermetric", ContainerPort: 9091, Protocol: "TCP"}}, }, }, }, }, - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "deployment", Namespace: "default", @@ -241,20 +255,21 @@ func TestInjectMetricsAggregator(t *testing.T) { constants.PrometheusPathAnnotationKey: constants.DefaultPrometheusPath, }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{{ - Name: "sklearn", - }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "sklearn", + }, { Name: "queue-proxy", - Ports: []v1.ContainerPort{{Name: "http-usermetric", ContainerPort: 9091, Protocol: "TCP"}}, + Ports: []corev1.ContainerPort{{Name: "http-usermetric", ContainerPort: 9091, Protocol: "TCP"}}, }, }, }, }, }, "SetPromAnnotationFalse": { - original: &v1.Pod{ + original: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "deployment", Namespace: "default", @@ -263,18 +278,19 @@ func TestInjectMetricsAggregator(t *testing.T) { constants.SetPrometheusAnnotation: "false", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{{ - Name: "sklearn", - }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "sklearn", + }, { Name: "queue-proxy", - Ports: []v1.ContainerPort{{Name: "http-usermetric", ContainerPort: 9091, Protocol: "TCP"}}, + Ports: []corev1.ContainerPort{{Name: "http-usermetric", ContainerPort: 9091, Protocol: "TCP"}}, }, }, }, }, - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "deployment", Namespace: "default", @@ -283,20 +299,21 @@ func TestInjectMetricsAggregator(t *testing.T) { constants.SetPrometheusAnnotation: "false", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{{ - Name: "sklearn", - }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "sklearn", + }, { Name: "queue-proxy", - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ {Name: constants.KServeContainerPrometheusMetricsPortEnvVarKey, Value: sklearnPrometheusPort}, {Name: constants.KServeContainerPrometheusMetricsPathEnvVarKey, Value: constants.DefaultPrometheusPath}, - {Name: constants.QueueProxyAggregatePrometheusMetricsPortEnvVarKey, Value: strconv.Itoa(constants.QueueProxyAggregatePrometheusMetricsPort)}, + {Name: constants.QueueProxyAggregatePrometheusMetricsPortEnvVarKey, Value: constants.QueueProxyAggregatePrometheusMetricsPort}, }, - Ports: []v1.ContainerPort{ + Ports: []corev1.ContainerPort{ {Name: "http-usermetric", ContainerPort: 9091, Protocol: "TCP"}, - {Name: constants.AggregateMetricsPortName, ContainerPort: int32(constants.QueueProxyAggregatePrometheusMetricsPort), Protocol: "TCP"}, + {Name: constants.AggregateMetricsPortName, ContainerPort: qpextAggregateMetricsPort, Protocol: "TCP"}, }, }, }, @@ -305,11 +322,8 @@ func TestInjectMetricsAggregator(t *testing.T) { }, } - cfgMap := v1.ConfigMap{Data: map[string]string{"enableMetricAggregation": "false", "enablePrometheusScraping": "false"}} - ma, err := newMetricsAggregator(&cfgMap) - if err != nil { - t.Errorf("Error creating the metrics aggregator %v", err) - } + cfgMap := corev1.ConfigMap{Data: map[string]string{"enableMetricAggregation": "false", "enablePrometheusScraping": "false"}} + ma := newMetricsAggregator(&cfgMap) for name, scenario := range scenarios { ma.InjectMetricsAggregator(scenario.original) diff --git a/pkg/webhook/admission/pod/mutator.go b/pkg/webhook/admission/pod/mutator.go index 4f398732d06..589a7a57526 100644 --- a/pkg/webhook/admission/pod/mutator.go +++ b/pkg/webhook/admission/pod/mutator.go @@ -20,13 +20,13 @@ import ( "encoding/json" "net/http" - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + corev1 "k8s.io/api/core/v1" "k8s.io/client-go/kubernetes" "sigs.k8s.io/controller-runtime/pkg/client" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/webhook/admission" + "github.com/kserve/kserve/pkg/apis/serving/v1beta1" "github.com/kserve/kserve/pkg/constants" "github.com/kserve/kserve/pkg/credentials" ) @@ -43,7 +43,7 @@ type Mutator struct { // Handle decodes the incoming Pod and executes mutation logic. func (mutator *Mutator) Handle(ctx context.Context, req admission.Request) admission.Response { - pod := &v1.Pod{} + pod := &corev1.Pod{} if err := mutator.Decoder.Decode(req, pod); err != nil { log.Error(err, "Failed to decode pod", "name", pod.Labels[constants.InferenceServicePodLabelKey]) @@ -54,8 +54,7 @@ func (mutator *Mutator) Handle(ctx context.Context, req admission.Request) admis return admission.ValidationResponse(true, "") } - configMap, err := mutator.Clientset.CoreV1().ConfigMaps(constants.KServeNamespace).Get(context.TODO(), - constants.InferenceServiceConfigMapName, metav1.GetOptions{}) + configMap, err := v1beta1.GetInferenceServiceConfigMap(ctx, mutator.Clientset) if err != nil { log.Error(err, "Failed to find config map", "name", constants.InferenceServiceConfigMapName) return admission.Errored(http.StatusInternalServerError, err) @@ -78,7 +77,7 @@ func (mutator *Mutator) Handle(ctx context.Context, req admission.Request) admis return admission.PatchResponseFromRaw(req.AdmissionRequest.Object.Raw, patch) } -func (mutator *Mutator) mutate(pod *v1.Pod, configMap *v1.ConfigMap) error { +func (mutator *Mutator) mutate(pod *corev1.Pod, configMap *corev1.ConfigMap) error { credentialBuilder := credentials.NewCredentialBuilder(mutator.Client, mutator.Clientset, configMap) storageInitializerConfig, err := getStorageInitializerConfigs(configMap) @@ -114,12 +113,9 @@ func (mutator *Mutator) mutate(pod *v1.Pod, configMap *v1.ConfigMap) error { batcherConfig: batcherConfig, } - metricsAggregator, err := newMetricsAggregator(configMap) - if err != nil { - return err - } + metricsAggregator := newMetricsAggregator(configMap) - mutators := []func(pod *v1.Pod) error{ + mutators := []func(pod *corev1.Pod) error{ InjectGKEAcceleratorSelector, storageInitializer.InjectStorageInitializer, storageInitializer.SetIstioCniSecurityContext, @@ -140,7 +136,7 @@ func (mutator *Mutator) mutate(pod *v1.Pod, configMap *v1.ConfigMap) error { return nil } -func needMutate(pod *v1.Pod) bool { +func needMutate(pod *corev1.Pod) bool { // Skip webhook if pod not managed by kserve _, ok := pod.Labels[constants.InferenceServicePodLabelKey] return ok diff --git a/pkg/webhook/admission/pod/mutator_test.go b/pkg/webhook/admission/pod/mutator_test.go index 4d7084f7698..6a153f6407b 100644 --- a/pkg/webhook/admission/pod/mutator_test.go +++ b/pkg/webhook/admission/pod/mutator_test.go @@ -19,25 +19,27 @@ package pod import ( "context" "encoding/json" + "sort" + "testing" + "github.com/google/uuid" - "github.com/kserve/kserve/pkg/constants" "github.com/onsi/gomega" gomegaTypes "github.com/onsi/gomega/types" "gomodules.xyz/jsonpatch/v2" "google.golang.org/protobuf/proto" admissionv1 "k8s.io/api/admission/v1" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/webhook/admission" - "sort" - "testing" + + "github.com/kserve/kserve/pkg/constants" ) func TestMutator_Handle(t *testing.T) { g := gomega.NewGomegaWithT(t) - kserveNamespace := v1.Namespace{ + kserveNamespace := corev1.Namespace{ TypeMeta: metav1.TypeMeta{ Kind: "Namespace", APIVersion: "v1", @@ -45,8 +47,8 @@ func TestMutator_Handle(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ Name: constants.KServeNamespace, }, - Spec: v1.NamespaceSpec{}, - Status: v1.NamespaceStatus{}, + Spec: corev1.NamespaceSpec{}, + Status: corev1.NamespaceStatus{}, } if err := c.Create(context.TODO(), &kserveNamespace); err != nil { @@ -55,13 +57,13 @@ func TestMutator_Handle(t *testing.T) { mutator := Mutator{Client: c, Clientset: clientset, Decoder: admission.NewDecoder(c.Scheme())} cases := map[string]struct { - configMap v1.ConfigMap + configMap corev1.ConfigMap request admission.Request - pod v1.Pod + pod corev1.Pod matcher gomegaTypes.GomegaMatcher }{ "should not mutate non isvc pods": { - configMap: v1.ConfigMap{ + configMap: corev1.ConfigMap{ TypeMeta: metav1.TypeMeta{ Kind: "ConfigMap", APIVersion: "v1", @@ -139,7 +141,7 @@ func TestMutator_Handle(t *testing.T) { Options: runtime.RawExtension{}, }, }, - pod: v1.Pod{ + pod: corev1.Pod{ TypeMeta: metav1.TypeMeta{ Kind: "Pod", APIVersion: "v1", @@ -167,7 +169,7 @@ func TestMutator_Handle(t *testing.T) { }), }, "should mutate isvc pods": { - configMap: v1.ConfigMap{ + configMap: corev1.ConfigMap{ TypeMeta: metav1.TypeMeta{ Kind: "ConfigMap", APIVersion: "v1", @@ -245,7 +247,7 @@ func TestMutator_Handle(t *testing.T) { Options: runtime.RawExtension{}, }, }, - pod: v1.Pod{ + pod: corev1.Pod{ TypeMeta: metav1.TypeMeta{ Kind: "Pod", APIVersion: "v1", @@ -255,8 +257,8 @@ func TestMutator_Handle(t *testing.T) { constants.InferenceServicePodLabelKey: "", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, }, diff --git a/pkg/webhook/admission/pod/storage_initializer_injector.go b/pkg/webhook/admission/pod/storage_initializer_injector.go index 070fa2d9d6e..cc763337484 100644 --- a/pkg/webhook/admission/pod/storage_initializer_injector.go +++ b/pkg/webhook/admission/pod/storage_initializer_injector.go @@ -19,6 +19,7 @@ package pod import ( "context" "encoding/json" + "errors" "fmt" "path/filepath" "strconv" @@ -27,13 +28,14 @@ import ( "k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/util/strategicpatch" + corev1 "k8s.io/api/core/v1" + "knative.dev/pkg/ptr" + "sigs.k8s.io/controller-runtime/pkg/client" + "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" "github.com/kserve/kserve/pkg/constants" "github.com/kserve/kserve/pkg/credentials" "github.com/kserve/kserve/pkg/credentials/s3" - v1 "k8s.io/api/core/v1" - "knative.dev/pkg/ptr" - "sigs.k8s.io/controller-runtime/pkg/client" ) const ( @@ -75,7 +77,7 @@ type StorageInitializerInjector struct { client client.Client } -func getStorageInitializerConfigs(configMap *v1.ConfigMap) (*StorageInitializerConfig, error) { +func getStorageInitializerConfigs(configMap *corev1.ConfigMap) (*StorageInitializerConfig, error) { storageInitializerConfig := &StorageInitializerConfig{} if initializerConfig, ok := configMap.Data[StorageInitializerConfigMapKeyName]; ok { err := json.Unmarshal([]byte(initializerConfig), &storageInitializerConfig) @@ -84,10 +86,12 @@ func getStorageInitializerConfigs(configMap *v1.ConfigMap) (*StorageInitializerC } } // Ensure that we set proper values for CPU/Memory Limit/Request - resourceDefaults := []string{storageInitializerConfig.MemoryRequest, + resourceDefaults := []string{ + storageInitializerConfig.MemoryRequest, storageInitializerConfig.MemoryLimit, storageInitializerConfig.CpuRequest, - storageInitializerConfig.CpuLimit} + storageInitializerConfig.CpuLimit, + } for _, key := range resourceDefaults { _, err := resource.ParseQuantity(key) if err != nil { @@ -98,9 +102,9 @@ func getStorageInitializerConfigs(configMap *v1.ConfigMap) (*StorageInitializerC return storageInitializerConfig, nil } -func GetContainerSpecForStorageUri(storageUri string, client client.Client) (*v1.Container, error) { +func GetContainerSpecForStorageUri(ctx context.Context, storageUri string, client client.Client) (*corev1.Container, error) { storageContainers := &v1alpha1.ClusterStorageContainerList{} - if err := client.List(context.TODO(), storageContainers); err != nil { + if err := client.List(ctx, storageContainers); err != nil { return nil, err } @@ -128,7 +132,7 @@ func GetContainerSpecForStorageUri(storageUri string, client client.Client) (*v1 // via the proc filesystem (possible when `shareProcessNamespace` is enabled in the Pod spec). // This method is idempotent so can be called multiple times like it happens when the // webhook is configured with `reinvocationPolicy: IfNeeded` -func (mi *StorageInitializerInjector) InjectModelcar(pod *v1.Pod) error { +func (mi *StorageInitializerInjector) InjectModelcar(pod *corev1.Pod) error { srcURI, ok := pod.ObjectMeta.Annotations[constants.StorageInitializerSourceUriInternalAnnotationKey] if !ok { return nil @@ -166,7 +170,7 @@ func (mi *StorageInitializerInjector) InjectModelcar(pod *v1.Pod) error { // of Kubernetes where sharing the filesystem via the process namespace only works // when both containers are running as root if mi.config.UidModelcar != nil { - userContainer.SecurityContext = &v1.SecurityContext{ + userContainer.SecurityContext = &corev1.SecurityContext{ RunAsUser: mi.config.UidModelcar, } } @@ -195,7 +199,7 @@ func (mi *StorageInitializerInjector) InjectModelcar(pod *v1.Pod) error { // for the serving container in a unified way across storage tech by injecting // a provisioning INIT container. This is a workaround because KNative does not // support INIT containers: https://github.com/knative/serving/issues/4307 -func (mi *StorageInitializerInjector) InjectStorageInitializer(pod *v1.Pod) error { +func (mi *StorageInitializerInjector) InjectStorageInitializer(pod *corev1.Pod) error { // Only inject if the required annotations are set srcURI, ok := pod.ObjectMeta.Annotations[constants.StorageInitializerSourceUriInternalAnnotationKey] if !ok { @@ -254,8 +258,8 @@ func (mi *StorageInitializerInjector) InjectStorageInitializer(pod *v1.Pod) erro } } - podVolumes := []v1.Volume{} - storageInitializerMounts := []v1.VolumeMount{} + podVolumes := []corev1.Volume{} + storageInitializerMounts := []corev1.VolumeMount{} // For PVC source URIs we need to mount the source to be able to access it // See design and discussion here: https://github.com/kserve/kserve/issues/148 @@ -266,10 +270,10 @@ func (mi *StorageInitializerInjector) InjectStorageInitializer(pod *v1.Pod) erro } // add the PVC volume on the pod - pvcSourceVolume := v1.Volume{ + pvcSourceVolume := corev1.Volume{ Name: PvcSourceMountName, - VolumeSource: v1.VolumeSource{ - PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{ + VolumeSource: corev1.VolumeSource{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ ClaimName: pvcName, }, }, @@ -283,7 +287,7 @@ func (mi *StorageInitializerInjector) InjectStorageInitializer(pod *v1.Pod) erro // pvc will be mount to /mnt/models rather than /mnt/pvc // pvcPath will be injected via SubPath, pvcPath must be a root or Dir // it is user responsibility to ensure it is a root or Dir - pvcSourceVolumeMount := v1.VolumeMount{ + pvcSourceVolumeMount := corev1.VolumeMount{ Name: PvcSourceMountName, MountPath: constants.DefaultModelLocalMountPath, // only path to volume's root ("") or folder is supported @@ -331,7 +335,7 @@ func (mi *StorageInitializerInjector) InjectStorageInitializer(pod *v1.Pod) erro // below use storage initializer to handle the pvc // add a corresponding PVC volume mount to the INIT container - pvcSourceVolumeMount := v1.VolumeMount{ + pvcSourceVolumeMount := corev1.VolumeMount{ Name: PvcSourceMountName, MountPath: PvcSourceMountPath, ReadOnly: isvcReadonlyStringFlag, @@ -348,16 +352,16 @@ func (mi *StorageInitializerInjector) InjectStorageInitializer(pod *v1.Pod) erro } // Create a volume that is shared between the storage-initializer and kserve-container - sharedVolume := v1.Volume{ + sharedVolume := corev1.Volume{ Name: StorageInitializerVolumeName, - VolumeSource: v1.VolumeSource{ - EmptyDir: &v1.EmptyDirVolumeSource{}, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, }, } podVolumes = append(podVolumes, sharedVolume) // Create a write mount into the shared volume - sharedVolumeWriteMount := v1.VolumeMount{ + sharedVolumeWriteMount := corev1.VolumeMount{ Name: StorageInitializerVolumeName, MountPath: constants.DefaultModelLocalMountPath, ReadOnly: false, @@ -370,29 +374,29 @@ func (mi *StorageInitializerInjector) InjectStorageInitializer(pod *v1.Pod) erro } // Add an init container to run provisioning logic to the PodSpec - initContainer := &v1.Container{ + initContainer := &corev1.Container{ Name: StorageInitializerContainerName, Image: storageInitializerImage, Args: []string{ srcURI, constants.DefaultModelLocalMountPath, }, - TerminationMessagePolicy: v1.TerminationMessageFallbackToLogsOnError, + TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, VolumeMounts: storageInitializerMounts, - Resources: v1.ResourceRequirements{ - Limits: map[v1.ResourceName]resource.Quantity{ - v1.ResourceCPU: resource.MustParse(mi.config.CpuLimit), - v1.ResourceMemory: resource.MustParse(mi.config.MemoryLimit), + Resources: corev1.ResourceRequirements{ + Limits: map[corev1.ResourceName]resource.Quantity{ + corev1.ResourceCPU: resource.MustParse(mi.config.CpuLimit), + corev1.ResourceMemory: resource.MustParse(mi.config.MemoryLimit), }, - Requests: map[v1.ResourceName]resource.Quantity{ - v1.ResourceCPU: resource.MustParse(mi.config.CpuRequest), - v1.ResourceMemory: resource.MustParse(mi.config.MemoryRequest), + Requests: map[corev1.ResourceName]resource.Quantity{ + corev1.ResourceCPU: resource.MustParse(mi.config.CpuRequest), + corev1.ResourceMemory: resource.MustParse(mi.config.MemoryRequest), }, }, } // Add a mount the shared volume on the kserve-container, update the PodSpec - sharedVolumeReadMount := v1.VolumeMount{ + sharedVolumeReadMount := corev1.VolumeMount{ Name: StorageInitializerVolumeName, MountPath: constants.DefaultModelLocalMountPath, ReadOnly: isvcReadonlyStringFlag, @@ -468,28 +472,28 @@ func (mi *StorageInitializerInjector) InjectStorageInitializer(pod *v1.Pod) erro } } - initContainer.Env = append(initContainer.Env, v1.EnvVar{ + initContainer.Env = append(initContainer.Env, corev1.EnvVar{ Name: constants.CaBundleConfigMapNameEnvVarKey, Value: caBundleConfigMapName, }) - initContainer.Env = append(initContainer.Env, v1.EnvVar{ + initContainer.Env = append(initContainer.Env, corev1.EnvVar{ Name: constants.CaBundleVolumeMountPathEnvVarKey, Value: caBundleVolumeMountPath, }) - caBundleVolume := v1.Volume{ + caBundleVolume := corev1.Volume{ Name: CaBundleVolumeName, - VolumeSource: v1.VolumeSource{ - ConfigMap: &v1.ConfigMapVolumeSource{ - LocalObjectReference: v1.LocalObjectReference{ + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ Name: caBundleConfigMapName, }, }, }, } - caBundleVolumeMount := v1.VolumeMount{ + caBundleVolumeMount := corev1.VolumeMount{ Name: CaBundleVolumeName, MountPath: caBundleVolumeMountPath, ReadOnly: true, @@ -502,7 +506,7 @@ func (mi *StorageInitializerInjector) InjectStorageInitializer(pod *v1.Pod) erro // Update initContainer (container spec) from a storage container CR if there is a match, // otherwise initContainer is not updated. // Priority: CR > configMap - storageContainerSpec, err := GetContainerSpecForStorageUri(srcURI, mi.client) + storageContainerSpec, err := GetContainerSpecForStorageUri(context.Background(), srcURI, mi.client) if err != nil { return err } @@ -522,9 +526,9 @@ func (mi *StorageInitializerInjector) InjectStorageInitializer(pod *v1.Pod) erro // SetIstioCniSecurityContext determines if Istio is installed in using the CNI plugin. If so, // the UserID of the storage initializer is changed to match the UserID of the Istio sidecar. // This is to ensure that the storage initializer can access the network. -func (mi *StorageInitializerInjector) SetIstioCniSecurityContext(pod *v1.Pod) error { +func (mi *StorageInitializerInjector) SetIstioCniSecurityContext(pod *corev1.Pod) error { // Find storage initializer container - var storageInitializerContainer *v1.Container + var storageInitializerContainer *corev1.Container for idx, c := range pod.Spec.InitContainers { if c.Name == StorageInitializerContainerName { storageInitializerContainer = &pod.Spec.InitContainers[idx] @@ -541,7 +545,7 @@ func (mi *StorageInitializerInjector) SetIstioCniSecurityContext(pod *v1.Pod) er if value, ok := pod.GetAnnotations()[constants.IstioSidecarUIDAnnotationKey]; ok { if uid, err := strconv.ParseInt(value, 10, 64); err == nil { if storageInitializerContainer.SecurityContext == nil { - storageInitializerContainer.SecurityContext = &v1.SecurityContext{} + storageInitializerContainer.SecurityContext = &corev1.SecurityContext{} } storageInitializerContainer.SecurityContext.RunAsUser = ptr.Int64(uid) } @@ -600,7 +604,7 @@ func (mi *StorageInitializerInjector) SetIstioCniSecurityContext(pod *v1.Pod) er } // Find the Istio sidecar container in the pod. - var istioSidecarContainer *v1.Container + var istioSidecarContainer *corev1.Container for idx, container := range pod.Spec.Containers { if container.Name == istioSidecarContainerName { istioSidecarContainer = &pod.Spec.Containers[idx] @@ -611,7 +615,7 @@ func (mi *StorageInitializerInjector) SetIstioCniSecurityContext(pod *v1.Pod) er // Set the UserID of the storage initializer to the same as the Istio sidecar if istioSidecarContainer != nil { if storageInitializerContainer.SecurityContext == nil { - storageInitializerContainer.SecurityContext = &v1.SecurityContext{} + storageInitializerContainer.SecurityContext = &corev1.SecurityContext{} } if istioSidecarContainer.SecurityContext == nil || istioSidecarContainer.SecurityContext.RunAsUser == nil { // If the Istio sidecar does not explicitly have a UID set, use 1337 which is the @@ -638,7 +642,7 @@ func (mi *StorageInitializerInjector) SetIstioCniSecurityContext(pod *v1.Pod) er return nil } -func getContainerWithName(pod *v1.Pod, name string) *v1.Container { +func getContainerWithName(pod *corev1.Pod, name string) *corev1.Container { for idx, container := range pod.Spec.Containers { if strings.Compare(container.Name, name) == 0 { return &pod.Spec.Containers[idx] @@ -650,9 +654,9 @@ func getContainerWithName(pod *v1.Pod, name string) *v1.Container { // Add an environment variable with the given value to the environments // variables of the given container, potentially replacing an env var that already exists // with this name -func addOrReplaceEnv(container *v1.Container, envKey string, envValue string) { +func addOrReplaceEnv(container *corev1.Container, envKey string, envValue string) { if container.Env == nil { - container.Env = []v1.EnvVar{} + container.Env = []corev1.EnvVar{} } for i, envVar := range container.Env { @@ -662,13 +666,13 @@ func addOrReplaceEnv(container *v1.Container, envKey string, envValue string) { } } - container.Env = append(container.Env, v1.EnvVar{ + container.Env = append(container.Env, corev1.EnvVar{ Name: envKey, Value: envValue, }) } -func (mi *StorageInitializerInjector) createModelContainer(image string, modelPath string) *v1.Container { +func (mi *StorageInitializerInjector) createModelContainer(image string, modelPath string) *corev1.Container { cpu := mi.config.CpuModelcar if cpu == "" { cpu = CpuModelcarDefault @@ -678,10 +682,10 @@ func (mi *StorageInitializerInjector) createModelContainer(image string, modelPa memory = MemoryModelcarDefault } - modelContainer := &v1.Container{ + modelContainer := &corev1.Container{ Name: ModelcarContainerName, Image: image, - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: StorageInitializerVolumeName, MountPath: getParentDirectory(modelPath), @@ -694,22 +698,22 @@ func (mi *StorageInitializerInjector) createModelContainer(image string, modelPa // $$$$ gets escaped by YAML to $$, which is the current PID fmt.Sprintf("ln -s /proc/$$$$/root/models %s && sleep infinity", modelPath), }, - Resources: v1.ResourceRequirements{ - Limits: map[v1.ResourceName]resource.Quantity{ + Resources: corev1.ResourceRequirements{ + Limits: map[corev1.ResourceName]resource.Quantity{ // Could possibly be reduced to even less - v1.ResourceCPU: resource.MustParse(cpu), - v1.ResourceMemory: resource.MustParse(memory), + corev1.ResourceCPU: resource.MustParse(cpu), + corev1.ResourceMemory: resource.MustParse(memory), }, - Requests: map[v1.ResourceName]resource.Quantity{ - v1.ResourceCPU: resource.MustParse(cpu), - v1.ResourceMemory: resource.MustParse(memory), + Requests: map[corev1.ResourceName]resource.Quantity{ + corev1.ResourceCPU: resource.MustParse(cpu), + corev1.ResourceMemory: resource.MustParse(memory), }, }, - TerminationMessagePolicy: v1.TerminationMessageFallbackToLogsOnError, + TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, } if mi.config.UidModelcar != nil { - modelContainer.SecurityContext = &v1.SecurityContext{ + modelContainer.SecurityContext = &corev1.SecurityContext{ RunAsUser: mi.config.UidModelcar, } } @@ -717,7 +721,7 @@ func (mi *StorageInitializerInjector) createModelContainer(image string, modelPa return modelContainer } -func (mi *StorageInitializerInjector) createModelInitContainer(image string) *v1.Container { +func (mi *StorageInitializerInjector) createModelInitContainer(image string) *corev1.Container { cpu := mi.config.CpuModelcar if cpu == "" { cpu = CpuModelcarDefault @@ -727,7 +731,7 @@ func (mi *StorageInitializerInjector) createModelInitContainer(image string) *v1 memory = MemoryModelcarDefault } - modelContainer := &v1.Container{ + modelContainer := &corev1.Container{ Name: ModelcarInitContainerName, Image: image, Args: []string{ @@ -736,18 +740,18 @@ func (mi *StorageInitializerInjector) createModelInitContainer(image string) *v1 // Check that the expected models directory exists "echo 'Pre-fetching modelcar " + image + ": ' && [ -d /models ] && [ \"$$(ls -A /models)\" ] && echo 'OK ... Prefetched and valid (/models exists)' || (echo 'NOK ... Prefetched but modelcar is invalid (/models does not exist or is empty)' && exit 1)", }, - Resources: v1.ResourceRequirements{ - Limits: map[v1.ResourceName]resource.Quantity{ + Resources: corev1.ResourceRequirements{ + Limits: map[corev1.ResourceName]resource.Quantity{ // Could possibly be reduced to even less - v1.ResourceCPU: resource.MustParse(cpu), - v1.ResourceMemory: resource.MustParse(memory), + corev1.ResourceCPU: resource.MustParse(cpu), + corev1.ResourceMemory: resource.MustParse(memory), }, - Requests: map[v1.ResourceName]resource.Quantity{ - v1.ResourceCPU: resource.MustParse(cpu), - v1.ResourceMemory: resource.MustParse(memory), + Requests: map[corev1.ResourceName]resource.Quantity{ + corev1.ResourceCPU: resource.MustParse(cpu), + corev1.ResourceMemory: resource.MustParse(memory), }, }, - TerminationMessagePolicy: v1.TerminationMessageFallbackToLogsOnError, + TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, } return modelContainer @@ -755,29 +759,29 @@ func (mi *StorageInitializerInjector) createModelInitContainer(image string) *v1 // addEmptyDirVolumeIfNotPresent adds an emptyDir volume only if not present in the // list. pod and pod.Spec must not be nil -func addEmptyDirVolumeIfNotPresent(pod *v1.Pod, name string) { +func addEmptyDirVolumeIfNotPresent(pod *corev1.Pod, name string) { for _, v := range pod.Spec.Volumes { if v.Name == name { return } } - pod.Spec.Volumes = append(pod.Spec.Volumes, v1.Volume{ + pod.Spec.Volumes = append(pod.Spec.Volumes, corev1.Volume{ Name: name, - VolumeSource: v1.VolumeSource{ - EmptyDir: &v1.EmptyDirVolumeSource{}, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, }, }) } // addVolumeMountIfNotPresent adds a volume mount to a given container but only if no volumemoun // with this name has been already added. container must not be nil -func addVolumeMountIfNotPresent(container *v1.Container, mountName string, mountPath string) { +func addVolumeMountIfNotPresent(container *corev1.Container, mountName string, mountPath string) { for _, v := range container.VolumeMounts { if v.Name == mountName { return } } - modelMount := v1.VolumeMount{ + modelMount := corev1.VolumeMount{ Name: mountName, MountPath: mountPath, ReadOnly: false, @@ -801,9 +805,9 @@ func getParentDirectory(path string) string { // Use JSON Marshal/Unmarshal to merge Container structs using strategic merge patch. // Use container name from defaultContainer spec, crdContainer takes precedence for other fields. -func mergeContainerSpecs(defaultContainer *v1.Container, crdContainer *v1.Container) (*v1.Container, error) { +func mergeContainerSpecs(defaultContainer *corev1.Container, crdContainer *corev1.Container) (*corev1.Container, error) { if defaultContainer == nil { - return nil, fmt.Errorf("defaultContainer is nil") + return nil, errors.New("defaultContainer is nil") } containerName := defaultContainer.Name @@ -818,7 +822,7 @@ func mergeContainerSpecs(defaultContainer *v1.Container, crdContainer *v1.Contai return nil, err } - mergedContainer := v1.Container{} + mergedContainer := corev1.Container{} jsonResult, err := strategicpatch.StrategicMergePatch(defaultContainerJson, overrides, mergedContainer) if err != nil { return nil, err @@ -851,7 +855,7 @@ func parsePvcURI(srcURI string) (pvcName string, pvcPath string, err error) { return pvcName, pvcPath, nil } -func needCaBundleMount(caBundleConfigMapName string, initContainer *v1.Container) bool { +func needCaBundleMount(caBundleConfigMapName string, initContainer *corev1.Container) bool { result := false if caBundleConfigMapName != "" { result = true diff --git a/pkg/webhook/admission/pod/storage_initializer_injector_test.go b/pkg/webhook/admission/pod/storage_initializer_injector_test.go index b1a9085f84c..c3135f02a31 100644 --- a/pkg/webhook/admission/pod/storage_initializer_injector_test.go +++ b/pkg/webhook/admission/pod/storage_initializer_injector_test.go @@ -25,7 +25,8 @@ import ( "github.com/onsi/gomega" "github.com/onsi/gomega/types" "github.com/stretchr/testify/assert" - v1 "k8s.io/api/core/v1" + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "knative.dev/pkg/kmp" @@ -59,42 +60,42 @@ var ( EnableDirectPvcVolumeMount: StorageInitializerDefaultEnableDirectPvcVolumeMount, } - resourceRequirement = v1.ResourceRequirements{ - Limits: map[v1.ResourceName]resource.Quantity{ - v1.ResourceCPU: resource.MustParse(StorageInitializerDefaultCPULimit), - v1.ResourceMemory: resource.MustParse(StorageInitializerDefaultMemoryLimit), + resourceRequirement = corev1.ResourceRequirements{ + Limits: map[corev1.ResourceName]resource.Quantity{ + corev1.ResourceCPU: resource.MustParse(StorageInitializerDefaultCPULimit), + corev1.ResourceMemory: resource.MustParse(StorageInitializerDefaultMemoryLimit), }, - Requests: map[v1.ResourceName]resource.Quantity{ - v1.ResourceCPU: resource.MustParse(StorageInitializerDefaultCPURequest), - v1.ResourceMemory: resource.MustParse(StorageInitializerDefaultMemoryRequest), + Requests: map[corev1.ResourceName]resource.Quantity{ + corev1.ResourceCPU: resource.MustParse(StorageInitializerDefaultCPURequest), + corev1.ResourceMemory: resource.MustParse(StorageInitializerDefaultMemoryRequest), }, } ) func TestStorageInitializerInjector(t *testing.T) { scenarios := map[string]struct { - original *v1.Pod - expected *v1.Pod + original *corev1.Pod + expected *corev1.Pod }{ "MissingAnnotations": { - original: &v1.Pod{ + original: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{}, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, }, }, }, }, - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{}, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, }, @@ -103,38 +104,38 @@ func TestStorageInitializerInjector(t *testing.T) { }, }, "AlreadyInjected": { - original: &v1.Pod{ + original: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "gs://foo", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, }, }, - InitContainers: []v1.Container{ + InitContainers: []corev1.Container{ { Name: "storage-initializer", }, }, }, }, - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "gs://foo", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, }, }, - InitContainers: []v1.Container{ + InitContainers: []corev1.Container{ { Name: "storage-initializer", }, @@ -143,31 +144,31 @@ func TestStorageInitializerInjector(t *testing.T) { }, }, "StorageInitializerInjected": { - original: &v1.Pod{ + original: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "gs://foo", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, }, }, }, }, - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "gs://foo", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-provision-location", MountPath: constants.DefaultModelLocalMountPath, @@ -176,14 +177,14 @@ func TestStorageInitializerInjector(t *testing.T) { }, }, }, - InitContainers: []v1.Container{ + InitContainers: []corev1.Container{ { Name: "storage-initializer", Image: StorageInitializerContainerImage + ":" + StorageInitializerContainerImageVersion, Args: []string{"gs://foo", constants.DefaultModelLocalMountPath}, Resources: resourceRequirement, TerminationMessagePolicy: "FallbackToLogsOnError", - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-provision-location", MountPath: constants.DefaultModelLocalMountPath, @@ -191,11 +192,11 @@ func TestStorageInitializerInjector(t *testing.T) { }, }, }, - Volumes: []v1.Volume{ + Volumes: []corev1.Volume{ { Name: "kserve-provision-location", - VolumeSource: v1.VolumeSource{ - EmptyDir: &v1.EmptyDirVolumeSource{}, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, }, }, }, @@ -204,31 +205,31 @@ func TestStorageInitializerInjector(t *testing.T) { }, "StorageInitializerInjectedReadOnlyUnset": { - original: &v1.Pod{ + original: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "gs://foo", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, }, }, }, }, - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "gs://foo", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: StorageInitializerVolumeName, MountPath: constants.DefaultModelLocalMountPath, @@ -237,14 +238,14 @@ func TestStorageInitializerInjector(t *testing.T) { }, }, }, - InitContainers: []v1.Container{ + InitContainers: []corev1.Container{ { Name: "storage-initializer", Image: StorageInitializerContainerImage + ":" + StorageInitializerContainerImageVersion, Args: []string{"gs://foo", constants.DefaultModelLocalMountPath}, Resources: resourceRequirement, TerminationMessagePolicy: "FallbackToLogsOnError", - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: StorageInitializerVolumeName, MountPath: constants.DefaultModelLocalMountPath, @@ -252,11 +253,11 @@ func TestStorageInitializerInjector(t *testing.T) { }, }, }, - Volumes: []v1.Volume{ + Volumes: []corev1.Volume{ { Name: StorageInitializerVolumeName, - VolumeSource: v1.VolumeSource{ - EmptyDir: &v1.EmptyDirVolumeSource{}, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, }, }, }, @@ -265,32 +266,32 @@ func TestStorageInitializerInjector(t *testing.T) { }, "StorageInitializerInjectedReadOnlyFalse": { - original: &v1.Pod{ + original: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "gs://foo", constants.StorageReadonlyAnnotationKey: "false", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, }, }, }, }, - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "gs://foo", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: StorageInitializerVolumeName, MountPath: constants.DefaultModelLocalMountPath, @@ -299,14 +300,14 @@ func TestStorageInitializerInjector(t *testing.T) { }, }, }, - InitContainers: []v1.Container{ + InitContainers: []corev1.Container{ { Name: "storage-initializer", Image: StorageInitializerContainerImage + ":" + StorageInitializerContainerImageVersion, Args: []string{"gs://foo", constants.DefaultModelLocalMountPath}, Resources: resourceRequirement, TerminationMessagePolicy: "FallbackToLogsOnError", - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: StorageInitializerVolumeName, MountPath: constants.DefaultModelLocalMountPath, @@ -314,11 +315,11 @@ func TestStorageInitializerInjector(t *testing.T) { }, }, }, - Volumes: []v1.Volume{ + Volumes: []corev1.Volume{ { Name: StorageInitializerVolumeName, - VolumeSource: v1.VolumeSource{ - EmptyDir: &v1.EmptyDirVolumeSource{}, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, }, }, }, @@ -327,32 +328,32 @@ func TestStorageInitializerInjector(t *testing.T) { }, "StorageInitializerInjectedReadOnlyTrue": { - original: &v1.Pod{ + original: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "gs://foo", constants.StorageReadonlyAnnotationKey: "true", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, }, }, }, }, - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "gs://foo", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: StorageInitializerVolumeName, MountPath: constants.DefaultModelLocalMountPath, @@ -361,14 +362,14 @@ func TestStorageInitializerInjector(t *testing.T) { }, }, }, - InitContainers: []v1.Container{ + InitContainers: []corev1.Container{ { Name: "storage-initializer", Image: StorageInitializerContainerImage + ":" + StorageInitializerContainerImageVersion, Args: []string{"gs://foo", constants.DefaultModelLocalMountPath}, Resources: resourceRequirement, TerminationMessagePolicy: "FallbackToLogsOnError", - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: StorageInitializerVolumeName, MountPath: constants.DefaultModelLocalMountPath, @@ -376,11 +377,11 @@ func TestStorageInitializerInjector(t *testing.T) { }, }, }, - Volumes: []v1.Volume{ + Volumes: []corev1.Volume{ { Name: StorageInitializerVolumeName, - VolumeSource: v1.VolumeSource{ - EmptyDir: &v1.EmptyDirVolumeSource{}, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, }, }, }, @@ -389,31 +390,31 @@ func TestStorageInitializerInjector(t *testing.T) { }, "StorageInitializerInjectedAndMountsPvc": { - original: &v1.Pod{ + original: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "pvc://mypvcname/some/path/on/pvc", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, }, }, }, }, - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "pvc://mypvcname/some/path/on/pvc", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-pvc-source", MountPath: "/mnt/pvc", @@ -427,14 +428,14 @@ func TestStorageInitializerInjector(t *testing.T) { }, }, }, - InitContainers: []v1.Container{ + InitContainers: []corev1.Container{ { Name: "storage-initializer", Image: StorageInitializerContainerImage + ":" + StorageInitializerContainerImageVersion, Args: []string{"/mnt/pvc/some/path/on/pvc", constants.DefaultModelLocalMountPath}, Resources: resourceRequirement, TerminationMessagePolicy: "FallbackToLogsOnError", - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-pvc-source", MountPath: "/mnt/pvc", @@ -447,11 +448,11 @@ func TestStorageInitializerInjector(t *testing.T) { }, }, }, - Volumes: []v1.Volume{ + Volumes: []corev1.Volume{ { Name: "kserve-pvc-source", - VolumeSource: v1.VolumeSource{ - PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{ + VolumeSource: corev1.VolumeSource{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ ClaimName: "mypvcname", ReadOnly: false, }, @@ -459,8 +460,8 @@ func TestStorageInitializerInjector(t *testing.T) { }, { Name: "kserve-provision-location", - VolumeSource: v1.VolumeSource{ - EmptyDir: &v1.EmptyDirVolumeSource{}, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, }, }, }, @@ -468,7 +469,7 @@ func TestStorageInitializerInjector(t *testing.T) { }, }, "StorageSpecInjected": { - original: &v1.Pod{ + original: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Namespace: "default", Annotations: map[string]string{ @@ -477,15 +478,15 @@ func TestStorageInitializerInjector(t *testing.T) { constants.StorageSpecParamAnnotationKey: `{"type": "s3", "bucket": "my-bucket"}`, }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, }, }, }, }, - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "://foo/bar", @@ -493,11 +494,11 @@ func TestStorageInitializerInjector(t *testing.T) { constants.StorageSpecParamAnnotationKey: `{"type": "s3", "bucket": "my-bucket"}`, }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-provision-location", MountPath: constants.DefaultModelLocalMountPath, @@ -506,12 +507,12 @@ func TestStorageInitializerInjector(t *testing.T) { }, }, }, - InitContainers: []v1.Container{ + InitContainers: []corev1.Container{ { Name: "storage-initializer", Image: StorageInitializerContainerImage + ":" + StorageInitializerContainerImageVersion, Args: []string{"s3://my-bucket/foo/bar", constants.DefaultModelLocalMountPath}, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: credentials.StorageOverrideConfigEnvKey, Value: `{"bucket":"my-bucket","type":"s3"}`, @@ -519,7 +520,7 @@ func TestStorageInitializerInjector(t *testing.T) { }, Resources: resourceRequirement, TerminationMessagePolicy: "FallbackToLogsOnError", - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-provision-location", MountPath: constants.DefaultModelLocalMountPath, @@ -527,11 +528,11 @@ func TestStorageInitializerInjector(t *testing.T) { }, }, }, - Volumes: []v1.Volume{ + Volumes: []corev1.Volume{ { Name: "kserve-provision-location", - VolumeSource: v1.VolumeSource{ - EmptyDir: &v1.EmptyDirVolumeSource{}, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, }, }, }, @@ -542,7 +543,7 @@ func TestStorageInitializerInjector(t *testing.T) { for name, scenario := range scenarios { injector := &StorageInitializerInjector{ - credentialBuilder: credentials.NewCredentialBuilder(c, clientset, &v1.ConfigMap{ + credentialBuilder: credentials.NewCredentialBuilder(c, clientset, &corev1.ConfigMap{ Data: map[string]string{}, }), config: storageInitializerConfig, @@ -559,18 +560,18 @@ func TestStorageInitializerInjector(t *testing.T) { func TestStorageInitializerFailureCases(t *testing.T) { scenarios := map[string]struct { - original *v1.Pod + original *corev1.Pod expectedErrorPrefix string }{ "MissingUserContainer": { - original: &v1.Pod{ + original: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "pvc://mypvcname/some/path/on/pvc", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: "random-container", }, @@ -583,7 +584,7 @@ func TestStorageInitializerFailureCases(t *testing.T) { for name, scenario := range scenarios { injector := &StorageInitializerInjector{ - credentialBuilder: credentials.NewCredentialBuilder(c, clientset, &v1.ConfigMap{ + credentialBuilder: credentials.NewCredentialBuilder(c, clientset, &corev1.ConfigMap{ Data: map[string]string{}, }), config: storageInitializerConfig, @@ -601,21 +602,21 @@ func TestStorageInitializerFailureCases(t *testing.T) { func TestCustomSpecStorageUriInjection(t *testing.T) { scenarios := map[string]struct { - original *v1.Pod - expectedStorageUriEnvVariable *v1.EnvVar + original *corev1.Pod + expectedStorageUriEnvVariable *corev1.EnvVar }{ "CustomSpecStorageUriSet": { - original: &v1.Pod{ + original: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "pvc://mypvcname/some/path/on/pvc", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: constants.CustomSpecStorageUriEnvVarKey, Value: "pvc://mypvcname/some/path/on/pvc", @@ -625,23 +626,23 @@ func TestCustomSpecStorageUriInjection(t *testing.T) { }, }, }, - expectedStorageUriEnvVariable: &v1.EnvVar{ + expectedStorageUriEnvVariable: &corev1.EnvVar{ Name: constants.CustomSpecStorageUriEnvVarKey, Value: constants.DefaultModelLocalMountPath, }, }, "CustomSpecStorageUriEmpty": { - original: &v1.Pod{ + original: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "pvc://mypvcname/some/path/on/pvc", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: constants.CustomSpecStorageUriEnvVarKey, Value: "", @@ -651,23 +652,23 @@ func TestCustomSpecStorageUriInjection(t *testing.T) { }, }, }, - expectedStorageUriEnvVariable: &v1.EnvVar{ + expectedStorageUriEnvVariable: &corev1.EnvVar{ Name: constants.CustomSpecStorageUriEnvVarKey, Value: "", }, }, "CustomSpecStorageUriNotSet": { - original: &v1.Pod{ + original: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "pvc://mypvcname/some/path/on/pvc", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "TestRandom", Value: "val", @@ -683,7 +684,7 @@ func TestCustomSpecStorageUriInjection(t *testing.T) { for name, scenario := range scenarios { injector := &StorageInitializerInjector{ - credentialBuilder: credentials.NewCredentialBuilder(c, clientset, &v1.ConfigMap{ + credentialBuilder: credentials.NewCredentialBuilder(c, clientset, &corev1.ConfigMap{ Data: map[string]string{}, }), config: storageInitializerConfig, @@ -693,7 +694,7 @@ func TestCustomSpecStorageUriInjection(t *testing.T) { t.Errorf("Test %q unexpected result: %s", name, err) } - var originalEnvVar *v1.EnvVar + var originalEnvVar *corev1.EnvVar for _, envVar := range scenario.original.Spec.Containers[0].Env { if envVar.Name == constants.CustomSpecStorageUriEnvVarKey { originalEnvVar = &envVar @@ -705,8 +706,8 @@ func TestCustomSpecStorageUriInjection(t *testing.T) { } } -func makePod() *v1.Pod { - return &v1.Pod{ +func makePod() *corev1.Pod { + return &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "default", @@ -714,8 +715,8 @@ func makePod() *v1.Pod { constants.StorageInitializerSourceUriInternalAnnotationKey: "gs://foo", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, }, @@ -727,25 +728,25 @@ func makePod() *v1.Pod { func TestCredentialInjection(t *testing.T) { g := gomega.NewGomegaWithT(t) scenarios := map[string]struct { - sa *v1.ServiceAccount - secret *v1.Secret - original *v1.Pod - expected *v1.Pod + sa *corev1.ServiceAccount + secret *corev1.Secret + original *corev1.Pod + expected *corev1.Pod }{ "Test s3 secrets injection": { - sa: &v1.ServiceAccount{ + sa: &corev1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{ Name: "default", Namespace: "default", }, - Secrets: []v1.ObjectReference{ + Secrets: []corev1.ObjectReference{ { Name: "s3-secret", Namespace: "default", }, }, }, - secret: &v1.Secret{ + secret: &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: "s3-secret", Namespace: "default", @@ -759,7 +760,7 @@ func TestCredentialInjection(t *testing.T) { }, }, original: makePod(), - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "default", @@ -767,11 +768,11 @@ func TestCredentialInjection(t *testing.T) { constants.StorageInitializerSourceUriInternalAnnotationKey: "gs://foo", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-provision-location", MountPath: constants.DefaultModelLocalMountPath, @@ -780,25 +781,25 @@ func TestCredentialInjection(t *testing.T) { }, }, }, - InitContainers: []v1.Container{ + InitContainers: []corev1.Container{ { Name: "storage-initializer", Image: StorageInitializerContainerImage + ":" + StorageInitializerContainerImageVersion, Args: []string{"gs://foo", constants.DefaultModelLocalMountPath}, Resources: resourceRequirement, TerminationMessagePolicy: "FallbackToLogsOnError", - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-provision-location", MountPath: constants.DefaultModelLocalMountPath, }, }, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: s3.AWSAccessKeyId, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "s3-secret", }, Key: "awsAccessKeyID", @@ -807,9 +808,9 @@ func TestCredentialInjection(t *testing.T) { }, { Name: s3.AWSSecretAccessKey, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "s3-secret", }, Key: "awsSecretAccessKey", @@ -827,11 +828,11 @@ func TestCredentialInjection(t *testing.T) { }, }, }, - Volumes: []v1.Volume{ + Volumes: []corev1.Volume{ { Name: "kserve-provision-location", - VolumeSource: v1.VolumeSource{ - EmptyDir: &v1.EmptyDirVolumeSource{}, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, }, }, }, @@ -839,19 +840,19 @@ func TestCredentialInjection(t *testing.T) { }, }, "Test GCS secrets injection": { - sa: &v1.ServiceAccount{ + sa: &corev1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{ Name: "default", Namespace: "default", }, - Secrets: []v1.ObjectReference{ + Secrets: []corev1.ObjectReference{ { Name: "user-gcp-sa", Namespace: "default", }, }, }, - secret: &v1.Secret{ + secret: &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: "user-gcp-sa", Namespace: "default", @@ -861,17 +862,17 @@ func TestCredentialInjection(t *testing.T) { }, }, original: makePod(), - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "gs://foo", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-provision-location", MountPath: constants.DefaultModelLocalMountPath, @@ -880,14 +881,14 @@ func TestCredentialInjection(t *testing.T) { }, }, }, - InitContainers: []v1.Container{ + InitContainers: []corev1.Container{ { Name: "storage-initializer", Image: StorageInitializerContainerImage + ":" + StorageInitializerContainerImageVersion, Args: []string{"gs://foo", constants.DefaultModelLocalMountPath}, Resources: resourceRequirement, TerminationMessagePolicy: "FallbackToLogsOnError", - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-provision-location", MountPath: constants.DefaultModelLocalMountPath, @@ -898,7 +899,7 @@ func TestCredentialInjection(t *testing.T) { MountPath: gcs.GCSCredentialVolumeMountPath, }, }, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: gcs.GCSCredentialEnvKey, Value: gcs.GCSCredentialVolumeMountPath + "gcloud-application-credentials.json", @@ -906,17 +907,17 @@ func TestCredentialInjection(t *testing.T) { }, }, }, - Volumes: []v1.Volume{ + Volumes: []corev1.Volume{ { Name: "kserve-provision-location", - VolumeSource: v1.VolumeSource{ - EmptyDir: &v1.EmptyDirVolumeSource{}, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, }, }, { Name: gcs.GCSCredentialVolumeName, - VolumeSource: v1.VolumeSource{ - Secret: &v1.SecretVolumeSource{ + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ SecretName: "user-gcp-sa", }, }, @@ -926,13 +927,13 @@ func TestCredentialInjection(t *testing.T) { }, }, "TestStorageSpecSecretInjection": { - sa: &v1.ServiceAccount{ + sa: &corev1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{ // Service account not used Name: "default", Namespace: "default", }, }, - secret: &v1.Secret{ + secret: &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: "storage-config", Namespace: "default", @@ -941,7 +942,7 @@ func TestCredentialInjection(t *testing.T) { "my-storage": `{"type": "s3", "bucket": "my-bucket", "region": "na"}`, }, }, - original: &v1.Pod{ + original: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Namespace: "default", Annotations: map[string]string{ @@ -951,15 +952,15 @@ func TestCredentialInjection(t *testing.T) { constants.StorageSpecKeyAnnotationKey: "my-storage", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, }, }, }, }, - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "://foo/bar", @@ -968,11 +969,11 @@ func TestCredentialInjection(t *testing.T) { constants.StorageSpecKeyAnnotationKey: "my-storage", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-provision-location", MountPath: constants.DefaultModelLocalMountPath, @@ -981,17 +982,17 @@ func TestCredentialInjection(t *testing.T) { }, }, }, - InitContainers: []v1.Container{ + InitContainers: []corev1.Container{ { Name: "storage-initializer", Image: StorageInitializerContainerImage + ":" + StorageInitializerContainerImageVersion, Args: []string{"s3://my-bucket/foo/bar", constants.DefaultModelLocalMountPath}, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: credentials.StorageConfigEnvKey, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{Name: "storage-config"}, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{Name: "storage-config"}, Key: "my-storage", }, }, @@ -1003,7 +1004,7 @@ func TestCredentialInjection(t *testing.T) { }, Resources: resourceRequirement, TerminationMessagePolicy: "FallbackToLogsOnError", - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-provision-location", MountPath: constants.DefaultModelLocalMountPath, @@ -1011,11 +1012,11 @@ func TestCredentialInjection(t *testing.T) { }, }, }, - Volumes: []v1.Volume{ + Volumes: []corev1.Volume{ { Name: "kserve-provision-location", - VolumeSource: v1.VolumeSource{ - EmptyDir: &v1.EmptyDirVolumeSource{}, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, }, }, }, @@ -1023,13 +1024,13 @@ func TestCredentialInjection(t *testing.T) { }, }, "TestStorageSpecDefaultSecretInjection": { - sa: &v1.ServiceAccount{ + sa: &corev1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{ // Service account not used Name: "default", Namespace: "default", }, }, - secret: &v1.Secret{ + secret: &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: "storage-config", Namespace: "default", @@ -1038,7 +1039,7 @@ func TestCredentialInjection(t *testing.T) { credentials.DefaultStorageSecretKey: `{"type": "s3", "bucket": "my-bucket", "region": "na"}`, }, }, - original: &v1.Pod{ + original: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Namespace: "default", Annotations: map[string]string{ @@ -1047,15 +1048,15 @@ func TestCredentialInjection(t *testing.T) { constants.StorageSpecParamAnnotationKey: `{"some-param": "some-val"}`, }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, }, }, }, }, - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "://foo/bar", @@ -1063,11 +1064,11 @@ func TestCredentialInjection(t *testing.T) { constants.StorageSpecParamAnnotationKey: `{"some-param":"some-val"}`, }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-provision-location", MountPath: constants.DefaultModelLocalMountPath, @@ -1076,17 +1077,17 @@ func TestCredentialInjection(t *testing.T) { }, }, }, - InitContainers: []v1.Container{ + InitContainers: []corev1.Container{ { Name: "storage-initializer", Image: StorageInitializerContainerImage + ":" + StorageInitializerContainerImageVersion, Args: []string{"s3://my-bucket/foo/bar", constants.DefaultModelLocalMountPath}, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: credentials.StorageConfigEnvKey, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{Name: "storage-config"}, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{Name: "storage-config"}, Key: credentials.DefaultStorageSecretKey, }, }, @@ -1098,7 +1099,7 @@ func TestCredentialInjection(t *testing.T) { }, Resources: resourceRequirement, TerminationMessagePolicy: "FallbackToLogsOnError", - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-provision-location", MountPath: constants.DefaultModelLocalMountPath, @@ -1106,11 +1107,11 @@ func TestCredentialInjection(t *testing.T) { }, }, }, - Volumes: []v1.Volume{ + Volumes: []corev1.Volume{ { Name: "kserve-provision-location", - VolumeSource: v1.VolumeSource{ - EmptyDir: &v1.EmptyDirVolumeSource{}, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, }, }, }, @@ -1119,7 +1120,7 @@ func TestCredentialInjection(t *testing.T) { }, } - var configMap = &v1.ConfigMap{ + configMap := &corev1.ConfigMap{ Data: map[string]string{ "credentials": `{ "gcs" : {"gcsCredentialFileName": "gcloud-application-credentials.json"}, @@ -1155,35 +1156,35 @@ func TestCredentialInjection(t *testing.T) { func TestStorageInitializerConfigmap(t *testing.T) { scenarios := map[string]struct { - original *v1.Pod - expected *v1.Pod + original *corev1.Pod + expected *corev1.Pod }{ "StorageInitializerConfig": { - original: &v1.Pod{ + original: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "gs://foo", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, }, }, }, }, - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "gs://foo", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-provision-location", MountPath: constants.DefaultModelLocalMountPath, @@ -1192,14 +1193,14 @@ func TestStorageInitializerConfigmap(t *testing.T) { }, }, }, - InitContainers: []v1.Container{ + InitContainers: []corev1.Container{ { Name: "storage-initializer", Image: "kserve/storage-initializer@sha256:xxx", Args: []string{"gs://foo", constants.DefaultModelLocalMountPath}, Resources: resourceRequirement, TerminationMessagePolicy: "FallbackToLogsOnError", - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-provision-location", MountPath: constants.DefaultModelLocalMountPath, @@ -1207,11 +1208,11 @@ func TestStorageInitializerConfigmap(t *testing.T) { }, }, }, - Volumes: []v1.Volume{ + Volumes: []corev1.Volume{ { Name: "kserve-provision-location", - VolumeSource: v1.VolumeSource{ - EmptyDir: &v1.EmptyDirVolumeSource{}, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, }, }, }, @@ -1222,7 +1223,7 @@ func TestStorageInitializerConfigmap(t *testing.T) { for name, scenario := range scenarios { injector := &StorageInitializerInjector{ - credentialBuilder: credentials.NewCredentialBuilder(c, clientset, &v1.ConfigMap{ + credentialBuilder: credentials.NewCredentialBuilder(c, clientset, &corev1.ConfigMap{ Data: map[string]string{}, }), config: &StorageInitializerConfig{ @@ -1249,12 +1250,12 @@ func TestGetStorageInitializerConfigs(t *testing.T) { g := gomega.NewGomegaWithT(t) cases := []struct { name string - configMap *v1.ConfigMap + configMap *corev1.ConfigMap matchers []types.GomegaMatcher }{ { name: "Valid Storage Initializer Config", - configMap: &v1.ConfigMap{ + configMap: &corev1.ConfigMap{ TypeMeta: metav1.TypeMeta{}, ObjectMeta: metav1.ObjectMeta{}, Data: map[string]string{ @@ -1285,7 +1286,7 @@ func TestGetStorageInitializerConfigs(t *testing.T) { }, { name: "Invalid Resource Value", - configMap: &v1.ConfigMap{ + configMap: &corev1.ConfigMap{ TypeMeta: metav1.TypeMeta{}, ObjectMeta: metav1.ObjectMeta{}, Data: map[string]string{ @@ -1357,13 +1358,12 @@ func TestParsePvcURI(t *testing.T) { g.Expect(pvcPath).Should(tc.matchers[1]) g.Expect(err).Should(tc.matchers[2]) }) - } } func TestCaBundleConfigMapVolumeMountInStorageInitializer(t *testing.T) { g := gomega.NewGomegaWithT(t) - var configMap = &v1.ConfigMap{ + configMap := &corev1.ConfigMap{ Data: map[string]string{ "credentials": `{ "gcs" : {"gcsCredentialFileName": "gcloud-application-credentials.json"}, @@ -1376,14 +1376,14 @@ func TestCaBundleConfigMapVolumeMountInStorageInitializer(t *testing.T) { } scenarios := map[string]struct { storageConfig *StorageInitializerConfig - secret *v1.Secret - sa *v1.ServiceAccount - original *v1.Pod - expected *v1.Pod + secret *corev1.Secret + sa *corev1.ServiceAccount + original *corev1.Pod + expected *corev1.Pod }{ "DoNotMountWithCaBundleConfigMapVolumeWhenCaBundleConfigMapNameNotSet": { storageConfig: storageInitializerConfig, - secret: &v1.Secret{ + secret: &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: "s3-secret", Namespace: "default", @@ -1393,12 +1393,12 @@ func TestCaBundleConfigMapVolumeMountInStorageInitializer(t *testing.T) { "awsSecretAccessKey": {}, }, }, - sa: &v1.ServiceAccount{ + sa: &corev1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{ Name: "default", Namespace: "default", }, - Secrets: []v1.ObjectReference{ + Secrets: []corev1.ObjectReference{ { Name: "s3-secret", Namespace: "default", @@ -1406,17 +1406,17 @@ func TestCaBundleConfigMapVolumeMountInStorageInitializer(t *testing.T) { }, }, original: makePod(), - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "gs://foo", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-provision-location", MountPath: constants.DefaultModelLocalMountPath, @@ -1425,17 +1425,17 @@ func TestCaBundleConfigMapVolumeMountInStorageInitializer(t *testing.T) { }, }, }, - InitContainers: []v1.Container{ + InitContainers: []corev1.Container{ { Name: "storage-initializer", Image: StorageInitializerContainerImage + ":" + StorageInitializerContainerImageVersion, Args: []string{"gs://foo", constants.DefaultModelLocalMountPath}, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: s3.AWSAccessKeyId, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "s3-secret", }, Key: "awsAccessKeyID", @@ -1444,9 +1444,9 @@ func TestCaBundleConfigMapVolumeMountInStorageInitializer(t *testing.T) { }, { Name: s3.AWSSecretAccessKey, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "s3-secret", }, Key: "awsSecretAccessKey", @@ -1456,7 +1456,7 @@ func TestCaBundleConfigMapVolumeMountInStorageInitializer(t *testing.T) { }, Resources: resourceRequirement, TerminationMessagePolicy: "FallbackToLogsOnError", - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-provision-location", MountPath: constants.DefaultModelLocalMountPath, @@ -1464,11 +1464,11 @@ func TestCaBundleConfigMapVolumeMountInStorageInitializer(t *testing.T) { }, }, }, - Volumes: []v1.Volume{ + Volumes: []corev1.Volume{ { Name: "kserve-provision-location", - VolumeSource: v1.VolumeSource{ - EmptyDir: &v1.EmptyDirVolumeSource{}, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, }, }, }, @@ -1484,7 +1484,7 @@ func TestCaBundleConfigMapVolumeMountInStorageInitializer(t *testing.T) { MemoryLimit: "1Gi", CaBundleConfigMapName: "custom-certs", // enable CA bundle config volume mount }, - secret: &v1.Secret{ + secret: &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: "s3-secret", Namespace: "default", @@ -1494,12 +1494,12 @@ func TestCaBundleConfigMapVolumeMountInStorageInitializer(t *testing.T) { "awsSecretAccessKey": {}, }, }, - sa: &v1.ServiceAccount{ + sa: &corev1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{ Name: "default", Namespace: "default", }, - Secrets: []v1.ObjectReference{ + Secrets: []corev1.ObjectReference{ { Name: "s3-secret", Namespace: "default", @@ -1507,17 +1507,17 @@ func TestCaBundleConfigMapVolumeMountInStorageInitializer(t *testing.T) { }, }, original: makePod(), - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "gs://foo", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-provision-location", MountPath: constants.DefaultModelLocalMountPath, @@ -1526,17 +1526,17 @@ func TestCaBundleConfigMapVolumeMountInStorageInitializer(t *testing.T) { }, }, }, - InitContainers: []v1.Container{ + InitContainers: []corev1.Container{ { Name: "storage-initializer", Image: StorageInitializerContainerImage + ":" + StorageInitializerContainerImageVersion, Args: []string{"gs://foo", constants.DefaultModelLocalMountPath}, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: s3.AWSAccessKeyId, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "s3-secret", }, Key: "awsAccessKeyID", @@ -1545,9 +1545,9 @@ func TestCaBundleConfigMapVolumeMountInStorageInitializer(t *testing.T) { }, { Name: s3.AWSSecretAccessKey, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "s3-secret", }, Key: "awsSecretAccessKey", @@ -1559,7 +1559,7 @@ func TestCaBundleConfigMapVolumeMountInStorageInitializer(t *testing.T) { }, Resources: resourceRequirement, TerminationMessagePolicy: "FallbackToLogsOnError", - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-provision-location", MountPath: constants.DefaultModelLocalMountPath, @@ -1572,18 +1572,18 @@ func TestCaBundleConfigMapVolumeMountInStorageInitializer(t *testing.T) { }, }, }, - Volumes: []v1.Volume{ + Volumes: []corev1.Volume{ { Name: "kserve-provision-location", - VolumeSource: v1.VolumeSource{ - EmptyDir: &v1.EmptyDirVolumeSource{}, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, }, }, { Name: CaBundleVolumeName, - VolumeSource: v1.VolumeSource{ - ConfigMap: &v1.ConfigMapVolumeSource{ - LocalObjectReference: v1.LocalObjectReference{ + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ Name: constants.DefaultGlobalCaBundleConfigMapName, }, }, @@ -1601,7 +1601,7 @@ func TestCaBundleConfigMapVolumeMountInStorageInitializer(t *testing.T) { MemoryRequest: "200Mi", MemoryLimit: "1Gi", }, - secret: &v1.Secret{ + secret: &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: "s3-secret", Namespace: "default", @@ -1614,12 +1614,12 @@ func TestCaBundleConfigMapVolumeMountInStorageInitializer(t *testing.T) { "awsSecretAccessKey": {}, }, }, - sa: &v1.ServiceAccount{ + sa: &corev1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{ Name: "default", Namespace: "default", }, - Secrets: []v1.ObjectReference{ + Secrets: []corev1.ObjectReference{ { Name: "s3-secret", Namespace: "default", @@ -1627,17 +1627,17 @@ func TestCaBundleConfigMapVolumeMountInStorageInitializer(t *testing.T) { }, }, original: makePod(), - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "gs://foo", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-provision-location", MountPath: constants.DefaultModelLocalMountPath, @@ -1646,17 +1646,17 @@ func TestCaBundleConfigMapVolumeMountInStorageInitializer(t *testing.T) { }, }, }, - InitContainers: []v1.Container{ + InitContainers: []corev1.Container{ { Name: "storage-initializer", Image: StorageInitializerContainerImage + ":" + StorageInitializerContainerImageVersion, Args: []string{"gs://foo", constants.DefaultModelLocalMountPath}, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: s3.AWSAccessKeyId, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "s3-secret", }, Key: "awsAccessKeyID", @@ -1665,9 +1665,9 @@ func TestCaBundleConfigMapVolumeMountInStorageInitializer(t *testing.T) { }, { Name: s3.AWSSecretAccessKey, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "s3-secret", }, Key: "awsSecretAccessKey", @@ -1680,7 +1680,7 @@ func TestCaBundleConfigMapVolumeMountInStorageInitializer(t *testing.T) { }, Resources: resourceRequirement, TerminationMessagePolicy: "FallbackToLogsOnError", - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-provision-location", MountPath: constants.DefaultModelLocalMountPath, @@ -1693,18 +1693,18 @@ func TestCaBundleConfigMapVolumeMountInStorageInitializer(t *testing.T) { }, }, }, - Volumes: []v1.Volume{ + Volumes: []corev1.Volume{ { Name: "kserve-provision-location", - VolumeSource: v1.VolumeSource{ - EmptyDir: &v1.EmptyDirVolumeSource{}, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, }, }, { Name: CaBundleVolumeName, - VolumeSource: v1.VolumeSource{ - ConfigMap: &v1.ConfigMapVolumeSource{ - LocalObjectReference: v1.LocalObjectReference{ + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "cabundle-annotation", }, }, @@ -1723,7 +1723,7 @@ func TestCaBundleConfigMapVolumeMountInStorageInitializer(t *testing.T) { MemoryLimit: "1Gi", CaBundleConfigMapName: "custom-certs", // enable CA bundle configmap volume mount }, - secret: &v1.Secret{ + secret: &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: "s3-secret", Namespace: "default", @@ -1736,12 +1736,12 @@ func TestCaBundleConfigMapVolumeMountInStorageInitializer(t *testing.T) { "awsSecretAccessKey": {}, }, }, - sa: &v1.ServiceAccount{ + sa: &corev1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{ Name: "default", Namespace: "default", }, - Secrets: []v1.ObjectReference{ + Secrets: []corev1.ObjectReference{ { Name: "s3-secret", Namespace: "default", @@ -1749,17 +1749,17 @@ func TestCaBundleConfigMapVolumeMountInStorageInitializer(t *testing.T) { }, }, original: makePod(), - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "gs://foo", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-provision-location", MountPath: constants.DefaultModelLocalMountPath, @@ -1768,17 +1768,17 @@ func TestCaBundleConfigMapVolumeMountInStorageInitializer(t *testing.T) { }, }, }, - InitContainers: []v1.Container{ + InitContainers: []corev1.Container{ { Name: "storage-initializer", Image: StorageInitializerContainerImage + ":" + StorageInitializerContainerImageVersion, Args: []string{"gs://foo", constants.DefaultModelLocalMountPath}, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: s3.AWSAccessKeyId, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "s3-secret", }, Key: "awsAccessKeyID", @@ -1787,9 +1787,9 @@ func TestCaBundleConfigMapVolumeMountInStorageInitializer(t *testing.T) { }, { Name: s3.AWSSecretAccessKey, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "s3-secret", }, Key: "awsSecretAccessKey", @@ -1802,7 +1802,7 @@ func TestCaBundleConfigMapVolumeMountInStorageInitializer(t *testing.T) { }, Resources: resourceRequirement, TerminationMessagePolicy: "FallbackToLogsOnError", - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-provision-location", MountPath: constants.DefaultModelLocalMountPath, @@ -1815,18 +1815,18 @@ func TestCaBundleConfigMapVolumeMountInStorageInitializer(t *testing.T) { }, }, }, - Volumes: []v1.Volume{ + Volumes: []corev1.Volume{ { Name: "kserve-provision-location", - VolumeSource: v1.VolumeSource{ - EmptyDir: &v1.EmptyDirVolumeSource{}, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, }, }, { Name: CaBundleVolumeName, - VolumeSource: v1.VolumeSource{ - ConfigMap: &v1.ConfigMapVolumeSource{ - LocalObjectReference: v1.LocalObjectReference{ + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "cabundle-annotation", }, }, @@ -1838,7 +1838,7 @@ func TestCaBundleConfigMapVolumeMountInStorageInitializer(t *testing.T) { }, "DoNotSetMountsCaBundleConfigMapVolumePathByAnnotationIfCaBundleConfigMapNameDidNotSet": { storageConfig: storageInitializerConfig, - secret: &v1.Secret{ + secret: &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: "s3-secret", Namespace: "default", @@ -1851,12 +1851,12 @@ func TestCaBundleConfigMapVolumeMountInStorageInitializer(t *testing.T) { "awsSecretAccessKey": {}, }, }, - sa: &v1.ServiceAccount{ + sa: &corev1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{ Name: "default", Namespace: "default", }, - Secrets: []v1.ObjectReference{ + Secrets: []corev1.ObjectReference{ { Name: "s3-secret", Namespace: "default", @@ -1864,17 +1864,17 @@ func TestCaBundleConfigMapVolumeMountInStorageInitializer(t *testing.T) { }, }, original: makePod(), - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "gs://foo", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-provision-location", MountPath: constants.DefaultModelLocalMountPath, @@ -1883,17 +1883,17 @@ func TestCaBundleConfigMapVolumeMountInStorageInitializer(t *testing.T) { }, }, }, - InitContainers: []v1.Container{ + InitContainers: []corev1.Container{ { Name: "storage-initializer", Image: StorageInitializerContainerImage + ":" + StorageInitializerContainerImageVersion, Args: []string{"gs://foo", constants.DefaultModelLocalMountPath}, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: s3.AWSAccessKeyId, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "s3-secret", }, Key: "awsAccessKeyID", @@ -1902,9 +1902,9 @@ func TestCaBundleConfigMapVolumeMountInStorageInitializer(t *testing.T) { }, { Name: s3.AWSSecretAccessKey, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "s3-secret", }, Key: "awsSecretAccessKey", @@ -1915,7 +1915,7 @@ func TestCaBundleConfigMapVolumeMountInStorageInitializer(t *testing.T) { }, Resources: resourceRequirement, TerminationMessagePolicy: "FallbackToLogsOnError", - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-provision-location", MountPath: constants.DefaultModelLocalMountPath, @@ -1923,11 +1923,11 @@ func TestCaBundleConfigMapVolumeMountInStorageInitializer(t *testing.T) { }, }, }, - Volumes: []v1.Volume{ + Volumes: []corev1.Volume{ { Name: "kserve-provision-location", - VolumeSource: v1.VolumeSource{ - EmptyDir: &v1.EmptyDirVolumeSource{}, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, }, }, }, @@ -1944,7 +1944,7 @@ func TestCaBundleConfigMapVolumeMountInStorageInitializer(t *testing.T) { CaBundleConfigMapName: "custom-certs", // enable CA bundle configmap volume mount CaBundleVolumeMountPath: "/path/to", // set CA bundle configmap volume mount path }, - secret: &v1.Secret{ + secret: &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: "s3-secret", Namespace: "default", @@ -1957,12 +1957,12 @@ func TestCaBundleConfigMapVolumeMountInStorageInitializer(t *testing.T) { "awsSecretAccessKey": {}, }, }, - sa: &v1.ServiceAccount{ + sa: &corev1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{ Name: "default", Namespace: "default", }, - Secrets: []v1.ObjectReference{ + Secrets: []corev1.ObjectReference{ { Name: "s3-secret", Namespace: "default", @@ -1970,17 +1970,17 @@ func TestCaBundleConfigMapVolumeMountInStorageInitializer(t *testing.T) { }, }, original: makePod(), - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "gs://foo", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-provision-location", MountPath: constants.DefaultModelLocalMountPath, @@ -1989,17 +1989,17 @@ func TestCaBundleConfigMapVolumeMountInStorageInitializer(t *testing.T) { }, }, }, - InitContainers: []v1.Container{ + InitContainers: []corev1.Container{ { Name: "storage-initializer", Image: StorageInitializerContainerImage + ":" + StorageInitializerContainerImageVersion, Args: []string{"gs://foo", constants.DefaultModelLocalMountPath}, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: s3.AWSAccessKeyId, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "s3-secret", }, Key: "awsAccessKeyID", @@ -2008,9 +2008,9 @@ func TestCaBundleConfigMapVolumeMountInStorageInitializer(t *testing.T) { }, { Name: s3.AWSSecretAccessKey, - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: "s3-secret", }, Key: "awsSecretAccessKey", @@ -2023,7 +2023,7 @@ func TestCaBundleConfigMapVolumeMountInStorageInitializer(t *testing.T) { }, Resources: resourceRequirement, TerminationMessagePolicy: "FallbackToLogsOnError", - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-provision-location", MountPath: constants.DefaultModelLocalMountPath, @@ -2036,18 +2036,18 @@ func TestCaBundleConfigMapVolumeMountInStorageInitializer(t *testing.T) { }, }, }, - Volumes: []v1.Volume{ + Volumes: []corev1.Volume{ { Name: "kserve-provision-location", - VolumeSource: v1.VolumeSource{ - EmptyDir: &v1.EmptyDirVolumeSource{}, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, }, }, { Name: CaBundleVolumeName, - VolumeSource: v1.VolumeSource{ - ConfigMap: &v1.ConfigMapVolumeSource{ - LocalObjectReference: v1.LocalObjectReference{ + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ Name: constants.DefaultGlobalCaBundleConfigMapName, }, }, @@ -2079,40 +2079,39 @@ func TestCaBundleConfigMapVolumeMountInStorageInitializer(t *testing.T) { g.Expect(c.Delete(context.TODO(), scenario.secret)).NotTo(gomega.HaveOccurred()) g.Expect(c.Delete(context.TODO(), scenario.sa)).NotTo(gomega.HaveOccurred()) } - } func TestDirectVolumeMountForPvc(t *testing.T) { scenarios := map[string]struct { - original *v1.Pod - expected *v1.Pod + original *corev1.Pod + expected *corev1.Pod }{ "StorageInitializerNotInjectedAndMountsPvcViaVolumeMount": { - original: &v1.Pod{ + original: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "pvc://mypvcname/some/path/on/pvc", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, }, }, }, }, - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "pvc://mypvcname/some/path/on/pvc", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-pvc-source", MountPath: "/mnt/models", @@ -2122,11 +2121,11 @@ func TestDirectVolumeMountForPvc(t *testing.T) { }, }, }, - Volumes: []v1.Volume{ + Volumes: []corev1.Volume{ { Name: "kserve-pvc-source", - VolumeSource: v1.VolumeSource{ - PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{ + VolumeSource: corev1.VolumeSource{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ ClaimName: "mypvcname", ReadOnly: false, }, @@ -2137,31 +2136,31 @@ func TestDirectVolumeMountForPvc(t *testing.T) { }, }, "StorageInitializerNotInjectedAndMountsPvcViaVolumeMountShortestPath": { - original: &v1.Pod{ + original: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "pvc://mypvcname", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, }, }, }, }, - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "pvc://mypvcname", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-pvc-source", MountPath: "/mnt/models", @@ -2171,11 +2170,11 @@ func TestDirectVolumeMountForPvc(t *testing.T) { }, }, }, - Volumes: []v1.Volume{ + Volumes: []corev1.Volume{ { Name: "kserve-pvc-source", - VolumeSource: v1.VolumeSource{ - PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{ + VolumeSource: corev1.VolumeSource{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ ClaimName: "mypvcname", ReadOnly: false, }, @@ -2186,32 +2185,32 @@ func TestDirectVolumeMountForPvc(t *testing.T) { }, }, "StorageInitializerNotInjectedAndMountsPvcViaVolumeMountReadOnlyFalse": { - original: &v1.Pod{ + original: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "pvc://mypvcname/some/path/on/pvc", constants.StorageReadonlyAnnotationKey: "false", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, }, }, }, }, - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "pvc://mypvcname/some/path/on/pvc", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-pvc-source", MountPath: "/mnt/models", @@ -2221,11 +2220,11 @@ func TestDirectVolumeMountForPvc(t *testing.T) { }, }, }, - Volumes: []v1.Volume{ + Volumes: []corev1.Volume{ { Name: "kserve-pvc-source", - VolumeSource: v1.VolumeSource{ - PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{ + VolumeSource: corev1.VolumeSource{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ ClaimName: "mypvcname", ReadOnly: false, }, @@ -2236,32 +2235,32 @@ func TestDirectVolumeMountForPvc(t *testing.T) { }, }, "StorageInitializerNotInjectedAndMountsPvcViaVolumeMountReadOnlyTrue": { - original: &v1.Pod{ + original: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "pvc://mypvcname/some/path/on/pvc", constants.StorageReadonlyAnnotationKey: "true", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, }, }, }, }, - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "pvc://mypvcname/some/path/on/pvc", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-pvc-source", MountPath: "/mnt/models", @@ -2271,11 +2270,11 @@ func TestDirectVolumeMountForPvc(t *testing.T) { }, }, }, - Volumes: []v1.Volume{ + Volumes: []corev1.Volume{ { Name: "kserve-pvc-source", - VolumeSource: v1.VolumeSource{ - PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{ + VolumeSource: corev1.VolumeSource{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ ClaimName: "mypvcname", ReadOnly: false, }, @@ -2289,7 +2288,7 @@ func TestDirectVolumeMountForPvc(t *testing.T) { for name, scenario := range scenarios { injector := &StorageInitializerInjector{ - credentialBuilder: credentials.NewCredentialBuilder(c, clientset, &v1.ConfigMap{ + credentialBuilder: credentials.NewCredentialBuilder(c, clientset, &corev1.ConfigMap{ Data: map[string]string{}, }), config: &StorageInitializerConfig{ @@ -2309,22 +2308,22 @@ func TestDirectVolumeMountForPvc(t *testing.T) { func TestTransformerCollocation(t *testing.T) { scenarios := map[string]struct { storageConfig *StorageInitializerConfig - original *v1.Pod - expected *v1.Pod + original *corev1.Pod + expected *corev1.Pod }{ "Transformer collocation with pvc": { storageConfig: storageInitializerConfig, - original: &v1.Pod{ + original: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "pvc://mypvcname/some/path/on/pvc", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: constants.CustomSpecStorageUriEnvVarKey, Value: "pvc://mypvcname/some/path/on/pvc", @@ -2338,21 +2337,21 @@ func TestTransformerCollocation(t *testing.T) { }, }, }, - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{}, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: constants.CustomSpecStorageUriEnvVarKey, Value: constants.DefaultModelLocalMountPath, }, }, - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-pvc-source", MountPath: "/mnt/pvc", @@ -2368,7 +2367,7 @@ func TestTransformerCollocation(t *testing.T) { { Name: constants.TransformerContainerName, Image: "test/image:latest", - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-pvc-source", MountPath: "/mnt/pvc", @@ -2382,14 +2381,14 @@ func TestTransformerCollocation(t *testing.T) { }, }, }, - InitContainers: []v1.Container{ + InitContainers: []corev1.Container{ { Name: "storage-initializer", Image: StorageInitializerContainerImage + ":" + StorageInitializerContainerImageVersion, Args: []string{"/mnt/pvc/some/path/on/pvc", constants.DefaultModelLocalMountPath}, Resources: resourceRequirement, TerminationMessagePolicy: "FallbackToLogsOnError", - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-pvc-source", MountPath: "/mnt/pvc", @@ -2402,11 +2401,11 @@ func TestTransformerCollocation(t *testing.T) { }, }, }, - Volumes: []v1.Volume{ + Volumes: []corev1.Volume{ { Name: "kserve-pvc-source", - VolumeSource: v1.VolumeSource{ - PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{ + VolumeSource: corev1.VolumeSource{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ ClaimName: "mypvcname", ReadOnly: false, }, @@ -2414,8 +2413,8 @@ func TestTransformerCollocation(t *testing.T) { }, { Name: "kserve-provision-location", - VolumeSource: v1.VolumeSource{ - EmptyDir: &v1.EmptyDirVolumeSource{}, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, }, }, }, @@ -2426,17 +2425,17 @@ func TestTransformerCollocation(t *testing.T) { storageConfig: &StorageInitializerConfig{ EnableDirectPvcVolumeMount: true, // enable direct volume mount for PVC }, - original: &v1.Pod{ + original: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "pvc://mypvcname/some/path/on/pvc", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: constants.CustomSpecStorageUriEnvVarKey, Value: "pvc://mypvcname/some/path/on/pvc", @@ -2450,21 +2449,21 @@ func TestTransformerCollocation(t *testing.T) { }, }, }, - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{}, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: constants.CustomSpecStorageUriEnvVarKey, Value: constants.DefaultModelLocalMountPath, }, }, - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-pvc-source", MountPath: "/mnt/models", @@ -2476,7 +2475,7 @@ func TestTransformerCollocation(t *testing.T) { { Name: constants.TransformerContainerName, Image: "test/image:latest", - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-pvc-source", MountPath: "/mnt/models", @@ -2486,11 +2485,11 @@ func TestTransformerCollocation(t *testing.T) { }, }, }, - Volumes: []v1.Volume{ + Volumes: []corev1.Volume{ { Name: "kserve-pvc-source", - VolumeSource: v1.VolumeSource{ - PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{ + VolumeSource: corev1.VolumeSource{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ ClaimName: "mypvcname", ReadOnly: false, }, @@ -2502,17 +2501,17 @@ func TestTransformerCollocation(t *testing.T) { }, "No collocation": { storageConfig: storageInitializerConfig, - original: &v1.Pod{ + original: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "pvc://mypvcname/some/path/on/pvc", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: constants.CustomSpecStorageUriEnvVarKey, Value: "pvc://mypvcname/some/path/on/pvc", @@ -2522,21 +2521,21 @@ func TestTransformerCollocation(t *testing.T) { }, }, }, - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{}, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ { Name: constants.CustomSpecStorageUriEnvVarKey, Value: constants.DefaultModelLocalMountPath, }, }, - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-pvc-source", MountPath: "/mnt/pvc", @@ -2550,14 +2549,14 @@ func TestTransformerCollocation(t *testing.T) { }, }, }, - InitContainers: []v1.Container{ + InitContainers: []corev1.Container{ { Name: "storage-initializer", Image: StorageInitializerContainerImage + ":" + StorageInitializerContainerImageVersion, Args: []string{"/mnt/pvc/some/path/on/pvc", constants.DefaultModelLocalMountPath}, Resources: resourceRequirement, TerminationMessagePolicy: "FallbackToLogsOnError", - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-pvc-source", MountPath: "/mnt/pvc", @@ -2570,11 +2569,11 @@ func TestTransformerCollocation(t *testing.T) { }, }, }, - Volumes: []v1.Volume{ + Volumes: []corev1.Volume{ { Name: "kserve-pvc-source", - VolumeSource: v1.VolumeSource{ - PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{ + VolumeSource: corev1.VolumeSource{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ ClaimName: "mypvcname", ReadOnly: false, }, @@ -2582,8 +2581,8 @@ func TestTransformerCollocation(t *testing.T) { }, { Name: "kserve-provision-location", - VolumeSource: v1.VolumeSource{ - EmptyDir: &v1.EmptyDirVolumeSource{}, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, }, }, }, @@ -2594,7 +2593,7 @@ func TestTransformerCollocation(t *testing.T) { for name, scenario := range scenarios { injector := &StorageInitializerInjector{ - credentialBuilder: credentials.NewCredentialBuilder(c, clientset, &v1.ConfigMap{ + credentialBuilder: credentials.NewCredentialBuilder(c, clientset, &corev1.ConfigMap{ Data: map[string]string{}, }), config: scenario.storageConfig, @@ -2616,14 +2615,14 @@ func TestGetStorageContainerSpec(t *testing.T) { Name: "custom", }, Spec: v1alpha1.StorageContainerSpec{ - Container: v1.Container{ + Container: corev1.Container{ Image: "kserve/custom:latest", - Resources: v1.ResourceRequirements{ - Limits: v1.ResourceList{ - v1.ResourceMemory: resource.MustParse("200Mi"), + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("200Mi"), }, }, - SecurityContext: &v1.SecurityContext{ + SecurityContext: &corev1.SecurityContext{ RunAsNonRoot: ptr.Bool(true), }, }, @@ -2635,11 +2634,11 @@ func TestGetStorageContainerSpec(t *testing.T) { Name: "s3-azure", }, Spec: v1alpha1.StorageContainerSpec{ - Container: v1.Container{ + Container: corev1.Container{ Image: "kserve/storage-initializer:latest", - Resources: v1.ResourceRequirements{ - Limits: v1.ResourceList{ - v1.ResourceMemory: resource.MustParse("200Mi"), + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("200Mi"), }, }, }, @@ -2663,7 +2662,7 @@ func TestGetStorageContainerSpec(t *testing.T) { }() scenarios := map[string]struct { storageUri string - expectedSpec *v1.Container + expectedSpec *corev1.Container }{ "s3": { storageUri: "s3://foo", @@ -2679,10 +2678,10 @@ func TestGetStorageContainerSpec(t *testing.T) { }, } for name, scenario := range scenarios { - var container *v1.Container + var container *corev1.Container var err error - if container, err = GetContainerSpecForStorageUri(scenario.storageUri, c); err != nil { + if container, err = GetContainerSpecForStorageUri(context.Background(), scenario.storageUri, c); err != nil { t.Errorf("Test %q unexpected result: %s", name, err) } g.Expect(container).To(gomega.Equal(scenario.expectedSpec)) @@ -2695,11 +2694,11 @@ func TestStorageContainerCRDInjection(t *testing.T) { Name: "custom", }, Spec: v1alpha1.StorageContainerSpec{ - Container: v1.Container{ + Container: corev1.Container{ Image: "kserve/storage-initializer:latest", - Resources: v1.ResourceRequirements{ - Limits: v1.ResourceList{ - v1.ResourceMemory: resource.MustParse("200Mi"), + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("200Mi"), }, }, }, @@ -2711,14 +2710,14 @@ func TestStorageContainerCRDInjection(t *testing.T) { Name: "s3-azure", }, Spec: v1alpha1.StorageContainerSpec{ - Container: v1.Container{ + Container: corev1.Container{ Image: "kserve/storage-initializer:latest", - Resources: v1.ResourceRequirements{ - Limits: v1.ResourceList{ - v1.ResourceMemory: resource.MustParse("500Mi"), + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("500Mi"), }, }, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "name", Value: "value"}, }, }, @@ -2741,35 +2740,35 @@ func TestStorageContainerCRDInjection(t *testing.T) { }() scenarios := map[string]struct { - original *v1.Pod - expected *v1.Pod + original *corev1.Pod + expected *corev1.Pod }{ "s3": { - original: &v1.Pod{ + original: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "s3://foo", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, }, }, }, }, - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "s3://foo", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-provision-location", MountPath: constants.DefaultModelLocalMountPath, @@ -2778,38 +2777,38 @@ func TestStorageContainerCRDInjection(t *testing.T) { }, }, }, - InitContainers: []v1.Container{ + InitContainers: []corev1.Container{ { Name: "storage-initializer", Image: StorageInitializerContainerImage + ":" + StorageInitializerContainerImageVersion, Args: []string{"s3://foo", constants.DefaultModelLocalMountPath}, - Resources: v1.ResourceRequirements{ - Limits: map[v1.ResourceName]resource.Quantity{ - v1.ResourceCPU: resource.MustParse(StorageInitializerDefaultCPULimit), - v1.ResourceMemory: resource.MustParse("500Mi"), // From CRD + Resources: corev1.ResourceRequirements{ + Limits: map[corev1.ResourceName]resource.Quantity{ + corev1.ResourceCPU: resource.MustParse(StorageInitializerDefaultCPULimit), + corev1.ResourceMemory: resource.MustParse("500Mi"), // From CRD }, - Requests: map[v1.ResourceName]resource.Quantity{ - v1.ResourceCPU: resource.MustParse(StorageInitializerDefaultCPURequest), - v1.ResourceMemory: resource.MustParse(StorageInitializerDefaultMemoryRequest), + Requests: map[corev1.ResourceName]resource.Quantity{ + corev1.ResourceCPU: resource.MustParse(StorageInitializerDefaultCPURequest), + corev1.ResourceMemory: resource.MustParse(StorageInitializerDefaultMemoryRequest), }, }, TerminationMessagePolicy: "FallbackToLogsOnError", - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-provision-location", MountPath: constants.DefaultModelLocalMountPath, }, }, - Env: []v1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "name", Value: "value"}, }, }, }, - Volumes: []v1.Volume{ + Volumes: []corev1.Volume{ { Name: "kserve-provision-location", - VolumeSource: v1.VolumeSource{ - EmptyDir: &v1.EmptyDirVolumeSource{}, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, }, }, }, @@ -2817,31 +2816,31 @@ func TestStorageContainerCRDInjection(t *testing.T) { }, }, "Default config if storage uri not matched in CRs": { - original: &v1.Pod{ + original: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "https://foo", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, }, }, }, }, - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "https://foo", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-provision-location", MountPath: constants.DefaultModelLocalMountPath, @@ -2850,14 +2849,14 @@ func TestStorageContainerCRDInjection(t *testing.T) { }, }, }, - InitContainers: []v1.Container{ + InitContainers: []corev1.Container{ { Name: "storage-initializer", Image: StorageInitializerContainerImage + ":" + StorageInitializerContainerImageVersion, Args: []string{"https://foo", constants.DefaultModelLocalMountPath}, Resources: resourceRequirement, // from configMap instead of the CR TerminationMessagePolicy: "FallbackToLogsOnError", - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-provision-location", MountPath: constants.DefaultModelLocalMountPath, @@ -2865,11 +2864,11 @@ func TestStorageContainerCRDInjection(t *testing.T) { }, }, }, - Volumes: []v1.Volume{ + Volumes: []corev1.Volume{ { Name: "kserve-provision-location", - VolumeSource: v1.VolumeSource{ - EmptyDir: &v1.EmptyDirVolumeSource{}, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, }, }, }, @@ -2879,7 +2878,7 @@ func TestStorageContainerCRDInjection(t *testing.T) { } for name, scenario := range scenarios { injector := &StorageInitializerInjector{ - credentialBuilder: credentials.NewCredentialBuilder(c, clientset, &v1.ConfigMap{ + credentialBuilder: credentials.NewCredentialBuilder(c, clientset, &corev1.ConfigMap{ Data: map[string]string{}, }), config: storageInitializerConfig, @@ -2898,7 +2897,7 @@ func TestStorageContainerCRDInjection(t *testing.T) { func TestAddOrReplaceEnv(t *testing.T) { tests := []struct { name string - container *v1.Container + container *corev1.Container envKey string envValue string wantEnvLen int @@ -2906,7 +2905,7 @@ func TestAddOrReplaceEnv(t *testing.T) { }{ { name: "nil env array", - container: &v1.Container{}, + container: &corev1.Container{}, envKey: "TEST_KEY", envValue: "test_value", wantEnvLen: 1, @@ -2914,8 +2913,8 @@ func TestAddOrReplaceEnv(t *testing.T) { }, { name: "env array without key", - container: &v1.Container{ - Env: []v1.EnvVar{ + container: &corev1.Container{ + Env: []corev1.EnvVar{ {Name: "EXISTING_KEY", Value: "existing_value"}, }, }, @@ -2926,8 +2925,8 @@ func TestAddOrReplaceEnv(t *testing.T) { }, { name: "env array with existing key", - container: &v1.Container{ - Env: []v1.EnvVar{ + container: &corev1.Container{ + Env: []corev1.EnvVar{ {Name: "TEST_KEY", Value: "old_value"}, }, }, @@ -2958,10 +2957,9 @@ func TestAddOrReplaceEnv(t *testing.T) { func TestInjectModelcar(t *testing.T) { // Test when annotation key is not set { - pod := &v1.Pod{} + pod := &corev1.Pod{} mi := &StorageInitializerInjector{} err := mi.InjectModelcar(pod) - if err != nil { t.Errorf("Expected nil error but got %v", err) } @@ -2972,7 +2970,7 @@ func TestInjectModelcar(t *testing.T) { // Test when srcURI does not start with OciURIPrefix { - pod := &v1.Pod{ + pod := &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "s3://bla/blub", @@ -2981,7 +2979,6 @@ func TestInjectModelcar(t *testing.T) { } mi := &StorageInitializerInjector{} err := mi.InjectModelcar(pod) - if err != nil { t.Errorf("Expected nil error but got %v", err) } @@ -2997,7 +2994,6 @@ func TestInjectModelcar(t *testing.T) { config: &StorageInitializerConfig{}, } err := mi.InjectModelcar(pod) - if err != nil { t.Errorf("Expected nil error but got %v", err) } @@ -3013,22 +3009,23 @@ func TestInjectModelcar(t *testing.T) { } // Check that an init container has been injected, and it is the model container - if len(pod.Spec.InitContainers) != 1 { + switch { + case len(pod.Spec.InitContainers) != 1: t.Errorf("Expected one init container but got %d", len(pod.Spec.InitContainers)) - } else if pod.Spec.InitContainers[0].Name != ModelcarInitContainerName { + case pod.Spec.InitContainers[0].Name != ModelcarInitContainerName: t.Errorf("Expected the init container to be the model but got %s", pod.Spec.InitContainers[0].Name) - } else { + default: // Check that resources are correctly set. - if _, ok := pod.Spec.InitContainers[0].Resources.Limits[v1.ResourceCPU]; !ok { + if _, ok := pod.Spec.InitContainers[0].Resources.Limits[corev1.ResourceCPU]; !ok { t.Error("The model container does not have CPU limit set") } - if _, ok := pod.Spec.InitContainers[0].Resources.Limits[v1.ResourceMemory]; !ok { + if _, ok := pod.Spec.InitContainers[0].Resources.Limits[corev1.ResourceMemory]; !ok { t.Error("The model container does not have Memory limit set") } - if _, ok := pod.Spec.InitContainers[0].Resources.Requests[v1.ResourceCPU]; !ok { + if _, ok := pod.Spec.InitContainers[0].Resources.Requests[corev1.ResourceCPU]; !ok { t.Error("The model container does not have CPU request set") } - if _, ok := pod.Spec.InitContainers[0].Resources.Requests[v1.ResourceMemory]; !ok { + if _, ok := pod.Spec.InitContainers[0].Resources.Requests[corev1.ResourceMemory]; !ok { t.Error("The model container does not have Memory request set") } @@ -3066,15 +3063,15 @@ func TestInjectModelcar(t *testing.T) { } } -func createTestPodForModelcar() *v1.Pod { - pod := &v1.Pod{ +func createTestPodForModelcar() *corev1.Pod { + pod := &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: OciURIPrefix + "myrepo/mymodelimage", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ {Name: constants.InferenceServiceContainerName}, }, }, @@ -3082,9 +3079,9 @@ func createTestPodForModelcar() *v1.Pod { return pod } -func createTestPodForModelcarWithTransformer() *v1.Pod { +func createTestPodForModelcarWithTransformer() *corev1.Pod { pod := createTestPodForModelcar() - pod.Spec.Containers = append(pod.Spec.Containers, v1.Container{Name: constants.TransformerContainerName}) + pod.Spec.Containers = append(pod.Spec.Containers, corev1.Container{Name: constants.TransformerContainerName}) return pod } @@ -3101,10 +3098,10 @@ func TestModelcarVolumeMounts(t *testing.T) { }) } -func checkVolumeMounts(t *testing.T, pod *v1.Pod, containerNames []string) { +func checkVolumeMounts(t *testing.T, pod *corev1.Pod, containerNames []string) { injector := &StorageInitializerInjector{config: &StorageInitializerConfig{}} err := injector.InjectModelcar(pod) - assert.Nil(t, err) + require.NoError(t, err) for _, containerName := range containerNames { container := getContainerWithName(pod, containerName) @@ -3125,13 +3122,13 @@ func TestModelcarIdempotency(t *testing.T) { // Inject modelcar twice err := injector.InjectModelcar(pod) - assert.Nil(t, err) + require.NoError(t, err) err = injector.InjectModelcar(pod) - assert.Nil(t, err) + require.NoError(t, err) // Reference modelcar err = injector.InjectModelcar(podReference) - assert.Nil(t, err) + require.NoError(t, err) // It should not make a difference if the modelcar is injected once or twice assert.True(t, reflect.DeepEqual(podReference, pod)) @@ -3145,7 +3142,7 @@ func TestStorageInitializerInjectorWithModelcarConfig(t *testing.T) { pod := createTestPodForModelcar() err := injector.InjectModelcar(pod) - assert.Nil(t, err) + require.NoError(t, err) // Assertions modelcarContainer := getContainerWithName(pod, ModelcarContainerName) @@ -3163,7 +3160,7 @@ func TestStorageInitializerInjectorWithModelcarConfig(t *testing.T) { pod := createTestPodForModelcar() err := injector.InjectModelcar(pod) - assert.Nil(t, err) + require.NoError(t, err) // Assertions modelcarContainer := getContainerWithName(pod, ModelcarContainerName) @@ -3180,7 +3177,7 @@ func TestStorageInitializerInjectorWithModelcarConfig(t *testing.T) { pod := createTestPodForModelcar() err := injector.InjectModelcar(pod) - assert.Nil(t, err) + require.NoError(t, err) // Assertions modelcarContainer := getContainerWithName(pod, ModelcarContainerName) @@ -3195,9 +3192,9 @@ func TestStorageInitializerInjectorWithModelcarConfig(t *testing.T) { func TestGetContainerWithName(t *testing.T) { // Test case: Container exists { - pod := &v1.Pod{ - Spec: v1.PodSpec{ - Containers: []v1.Container{ + pod := &corev1.Pod{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ {Name: "container-1"}, {Name: "container-2"}, }, @@ -3216,9 +3213,9 @@ func TestGetContainerWithName(t *testing.T) { // Test case: Container does not exist { - pod := &v1.Pod{ - Spec: v1.PodSpec{ - Containers: []v1.Container{ + pod := &corev1.Pod{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ {Name: "container-1"}, {Name: "container-2"}, }, @@ -3236,11 +3233,11 @@ func TestGetContainerWithName(t *testing.T) { func TestStorageInitializerUIDForIstioCNI(t *testing.T) { scenarios := map[string]struct { - original *v1.Pod - expected *v1.Pod + original *corev1.Pod + expected *corev1.Pod }{ "StorageInitializerCniUidSet": { - original: &v1.Pod{ + original: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "gs://foo", @@ -3248,31 +3245,31 @@ func TestStorageInitializerUIDForIstioCNI(t *testing.T) { constants.IstioInterceptionModeAnnotation: constants.IstioInterceptModeRedirect, }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, }, { Name: "istio-sidecar", - SecurityContext: &v1.SecurityContext{ + SecurityContext: &corev1.SecurityContext{ RunAsUser: ptr.Int64(501), }, }, }, }, }, - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "gs://foo", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-provision-location", MountPath: constants.DefaultModelLocalMountPath, @@ -3282,34 +3279,34 @@ func TestStorageInitializerUIDForIstioCNI(t *testing.T) { }, { Name: "istio-sidecar", - SecurityContext: &v1.SecurityContext{ + SecurityContext: &corev1.SecurityContext{ RunAsUser: ptr.Int64(501), }, }, }, - InitContainers: []v1.Container{ + InitContainers: []corev1.Container{ { Name: "storage-initializer", Image: StorageInitializerContainerImage + ":" + StorageInitializerContainerImageVersion, Args: []string{"gs://foo", constants.DefaultModelLocalMountPath}, Resources: resourceRequirement, TerminationMessagePolicy: "FallbackToLogsOnError", - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-provision-location", MountPath: constants.DefaultModelLocalMountPath, }, }, - SecurityContext: &v1.SecurityContext{ + SecurityContext: &corev1.SecurityContext{ RunAsUser: ptr.Int64(501), }, }, }, - Volumes: []v1.Volume{ + Volumes: []corev1.Volume{ { Name: "kserve-provision-location", - VolumeSource: v1.VolumeSource{ - EmptyDir: &v1.EmptyDirVolumeSource{}, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, }, }, }, @@ -3317,7 +3314,7 @@ func TestStorageInitializerUIDForIstioCNI(t *testing.T) { }, }, "StorageInitializerCniUidDefault": { - original: &v1.Pod{ + original: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "gs://foo", @@ -3325,8 +3322,8 @@ func TestStorageInitializerUIDForIstioCNI(t *testing.T) { constants.IstioInterceptionModeAnnotation: constants.IstioInterceptModeRedirect, }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, }, @@ -3336,17 +3333,17 @@ func TestStorageInitializerUIDForIstioCNI(t *testing.T) { }, }, }, - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "gs://foo", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-provision-location", MountPath: constants.DefaultModelLocalMountPath, @@ -3358,29 +3355,29 @@ func TestStorageInitializerUIDForIstioCNI(t *testing.T) { Name: "istio-sidecar", }, }, - InitContainers: []v1.Container{ + InitContainers: []corev1.Container{ { Name: "storage-initializer", Image: StorageInitializerContainerImage + ":" + StorageInitializerContainerImageVersion, Args: []string{"gs://foo", constants.DefaultModelLocalMountPath}, Resources: resourceRequirement, TerminationMessagePolicy: "FallbackToLogsOnError", - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-provision-location", MountPath: constants.DefaultModelLocalMountPath, }, }, - SecurityContext: &v1.SecurityContext{ + SecurityContext: &corev1.SecurityContext{ RunAsUser: ptr.Int64(1337), }, }, }, - Volumes: []v1.Volume{ + Volumes: []corev1.Volume{ { Name: "kserve-provision-location", - VolumeSource: v1.VolumeSource{ - EmptyDir: &v1.EmptyDirVolumeSource{}, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, }, }, }, @@ -3388,7 +3385,7 @@ func TestStorageInitializerUIDForIstioCNI(t *testing.T) { }, }, "StorageInitializerUidNotSetIfIstioInitPresent": { - original: &v1.Pod{ + original: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "gs://foo", @@ -3396,36 +3393,36 @@ func TestStorageInitializerUIDForIstioCNI(t *testing.T) { constants.IstioInterceptionModeAnnotation: constants.IstioInterceptModeRedirect, }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, }, { Name: "istio-sidecar", - SecurityContext: &v1.SecurityContext{ + SecurityContext: &corev1.SecurityContext{ RunAsUser: ptr.Int64(501), }, }, }, - InitContainers: []v1.Container{ + InitContainers: []corev1.Container{ { Name: constants.IstioInitContainerName, }, }, }, }, - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "gs://foo", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-provision-location", MountPath: constants.DefaultModelLocalMountPath, @@ -3435,12 +3432,12 @@ func TestStorageInitializerUIDForIstioCNI(t *testing.T) { }, { Name: "istio-sidecar", - SecurityContext: &v1.SecurityContext{ + SecurityContext: &corev1.SecurityContext{ RunAsUser: ptr.Int64(501), }, }, }, - InitContainers: []v1.Container{ + InitContainers: []corev1.Container{ { Name: constants.IstioInitContainerName, }, @@ -3450,7 +3447,7 @@ func TestStorageInitializerUIDForIstioCNI(t *testing.T) { Args: []string{"gs://foo", constants.DefaultModelLocalMountPath}, Resources: resourceRequirement, TerminationMessagePolicy: "FallbackToLogsOnError", - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-provision-location", MountPath: constants.DefaultModelLocalMountPath, @@ -3458,11 +3455,11 @@ func TestStorageInitializerUIDForIstioCNI(t *testing.T) { }, }, }, - Volumes: []v1.Volume{ + Volumes: []corev1.Volume{ { Name: "kserve-provision-location", - VolumeSource: v1.VolumeSource{ - EmptyDir: &v1.EmptyDirVolumeSource{}, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, }, }, }, @@ -3470,7 +3467,7 @@ func TestStorageInitializerUIDForIstioCNI(t *testing.T) { }, }, "StorageInitializerUidNotSetIfProxyMissing": { - original: &v1.Pod{ + original: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "gs://foo", @@ -3478,25 +3475,25 @@ func TestStorageInitializerUIDForIstioCNI(t *testing.T) { constants.IstioInterceptionModeAnnotation: constants.IstioInterceptModeRedirect, }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, }, }, }, }, - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "gs://foo", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-provision-location", MountPath: constants.DefaultModelLocalMountPath, @@ -3505,14 +3502,14 @@ func TestStorageInitializerUIDForIstioCNI(t *testing.T) { }, }, }, - InitContainers: []v1.Container{ + InitContainers: []corev1.Container{ { Name: "storage-initializer", Image: StorageInitializerContainerImage + ":" + StorageInitializerContainerImageVersion, Args: []string{"gs://foo", constants.DefaultModelLocalMountPath}, Resources: resourceRequirement, TerminationMessagePolicy: "FallbackToLogsOnError", - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-provision-location", MountPath: constants.DefaultModelLocalMountPath, @@ -3520,11 +3517,11 @@ func TestStorageInitializerUIDForIstioCNI(t *testing.T) { }, }, }, - Volumes: []v1.Volume{ + Volumes: []corev1.Volume{ { Name: "kserve-provision-location", - VolumeSource: v1.VolumeSource{ - EmptyDir: &v1.EmptyDirVolumeSource{}, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, }, }, }, @@ -3532,7 +3529,7 @@ func TestStorageInitializerUIDForIstioCNI(t *testing.T) { }, }, "StorageInitializerUidNotSetIfProxyNameMissing": { - original: &v1.Pod{ + original: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "gs://foo", @@ -3540,31 +3537,31 @@ func TestStorageInitializerUIDForIstioCNI(t *testing.T) { constants.IstioInterceptionModeAnnotation: constants.IstioInterceptModeRedirect, }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, }, { Name: "istio-sidecar", - SecurityContext: &v1.SecurityContext{ + SecurityContext: &corev1.SecurityContext{ RunAsUser: ptr.Int64(501), }, }, }, }, }, - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "gs://foo", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-provision-location", MountPath: constants.DefaultModelLocalMountPath, @@ -3574,19 +3571,19 @@ func TestStorageInitializerUIDForIstioCNI(t *testing.T) { }, { Name: "istio-sidecar", - SecurityContext: &v1.SecurityContext{ + SecurityContext: &corev1.SecurityContext{ RunAsUser: ptr.Int64(501), }, }, }, - InitContainers: []v1.Container{ + InitContainers: []corev1.Container{ { Name: "storage-initializer", Image: StorageInitializerContainerImage + ":" + StorageInitializerContainerImageVersion, Args: []string{"gs://foo", constants.DefaultModelLocalMountPath}, Resources: resourceRequirement, TerminationMessagePolicy: "FallbackToLogsOnError", - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-provision-location", MountPath: constants.DefaultModelLocalMountPath, @@ -3594,11 +3591,11 @@ func TestStorageInitializerUIDForIstioCNI(t *testing.T) { }, }, }, - Volumes: []v1.Volume{ + Volumes: []corev1.Volume{ { Name: "kserve-provision-location", - VolumeSource: v1.VolumeSource{ - EmptyDir: &v1.EmptyDirVolumeSource{}, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, }, }, }, @@ -3606,7 +3603,7 @@ func TestStorageInitializerUIDForIstioCNI(t *testing.T) { }, }, "StorageInitializerUidNotSetIfIstioStatusBlank": { - original: &v1.Pod{ + original: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "gs://foo", @@ -3614,31 +3611,31 @@ func TestStorageInitializerUIDForIstioCNI(t *testing.T) { constants.IstioInterceptionModeAnnotation: constants.IstioInterceptModeRedirect, }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, }, { Name: "istio-sidecar", - SecurityContext: &v1.SecurityContext{ + SecurityContext: &corev1.SecurityContext{ RunAsUser: ptr.Int64(501), }, }, }, }, }, - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "gs://foo", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-provision-location", MountPath: constants.DefaultModelLocalMountPath, @@ -3648,19 +3645,19 @@ func TestStorageInitializerUIDForIstioCNI(t *testing.T) { }, { Name: "istio-sidecar", - SecurityContext: &v1.SecurityContext{ + SecurityContext: &corev1.SecurityContext{ RunAsUser: ptr.Int64(501), }, }, }, - InitContainers: []v1.Container{ + InitContainers: []corev1.Container{ { Name: "storage-initializer", Image: StorageInitializerContainerImage + ":" + StorageInitializerContainerImageVersion, Args: []string{"gs://foo", constants.DefaultModelLocalMountPath}, Resources: resourceRequirement, TerminationMessagePolicy: "FallbackToLogsOnError", - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-provision-location", MountPath: constants.DefaultModelLocalMountPath, @@ -3668,11 +3665,11 @@ func TestStorageInitializerUIDForIstioCNI(t *testing.T) { }, }, }, - Volumes: []v1.Volume{ + Volumes: []corev1.Volume{ { Name: "kserve-provision-location", - VolumeSource: v1.VolumeSource{ - EmptyDir: &v1.EmptyDirVolumeSource{}, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, }, }, }, @@ -3680,7 +3677,7 @@ func TestStorageInitializerUIDForIstioCNI(t *testing.T) { }, }, "StorageInitializerUidNotSetIfInterceptModeNotRedirect": { - original: &v1.Pod{ + original: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "gs://foo", @@ -3688,31 +3685,31 @@ func TestStorageInitializerUIDForIstioCNI(t *testing.T) { constants.IstioInterceptionModeAnnotation: "OTHER_REDIRECT", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, }, { Name: "istio-sidecar", - SecurityContext: &v1.SecurityContext{ + SecurityContext: &corev1.SecurityContext{ RunAsUser: ptr.Int64(501), }, }, }, }, }, - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "gs://foo", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-provision-location", MountPath: constants.DefaultModelLocalMountPath, @@ -3722,19 +3719,19 @@ func TestStorageInitializerUIDForIstioCNI(t *testing.T) { }, { Name: "istio-sidecar", - SecurityContext: &v1.SecurityContext{ + SecurityContext: &corev1.SecurityContext{ RunAsUser: ptr.Int64(501), }, }, }, - InitContainers: []v1.Container{ + InitContainers: []corev1.Container{ { Name: "storage-initializer", Image: StorageInitializerContainerImage + ":" + StorageInitializerContainerImageVersion, Args: []string{"gs://foo", constants.DefaultModelLocalMountPath}, Resources: resourceRequirement, TerminationMessagePolicy: "FallbackToLogsOnError", - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-provision-location", MountPath: constants.DefaultModelLocalMountPath, @@ -3742,11 +3739,11 @@ func TestStorageInitializerUIDForIstioCNI(t *testing.T) { }, }, }, - Volumes: []v1.Volume{ + Volumes: []corev1.Volume{ { Name: "kserve-provision-location", - VolumeSource: v1.VolumeSource{ - EmptyDir: &v1.EmptyDirVolumeSource{}, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, }, }, }, @@ -3754,38 +3751,38 @@ func TestStorageInitializerUIDForIstioCNI(t *testing.T) { }, }, "StorageInitializerUidNotSetIfInterceptModeMissing": { - original: &v1.Pod{ + original: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "gs://foo", constants.IstioSidecarStatusAnnotation: "{\"containers\": [\"istio-sidecar\"]}", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, }, { Name: "istio-sidecar", - SecurityContext: &v1.SecurityContext{ + SecurityContext: &corev1.SecurityContext{ RunAsUser: ptr.Int64(501), }, }, }, }, }, - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "gs://foo", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-provision-location", MountPath: constants.DefaultModelLocalMountPath, @@ -3795,19 +3792,19 @@ func TestStorageInitializerUIDForIstioCNI(t *testing.T) { }, { Name: "istio-sidecar", - SecurityContext: &v1.SecurityContext{ + SecurityContext: &corev1.SecurityContext{ RunAsUser: ptr.Int64(501), }, }, }, - InitContainers: []v1.Container{ + InitContainers: []corev1.Container{ { Name: "storage-initializer", Image: StorageInitializerContainerImage + ":" + StorageInitializerContainerImageVersion, Args: []string{"gs://foo", constants.DefaultModelLocalMountPath}, Resources: resourceRequirement, TerminationMessagePolicy: "FallbackToLogsOnError", - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-provision-location", MountPath: constants.DefaultModelLocalMountPath, @@ -3815,11 +3812,11 @@ func TestStorageInitializerUIDForIstioCNI(t *testing.T) { }, }, }, - Volumes: []v1.Volume{ + Volumes: []corev1.Volume{ { Name: "kserve-provision-location", - VolumeSource: v1.VolumeSource{ - EmptyDir: &v1.EmptyDirVolumeSource{}, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, }, }, }, @@ -3827,38 +3824,38 @@ func TestStorageInitializerUIDForIstioCNI(t *testing.T) { }, }, "StorageInitializerUidNotSetIfIstioStatusMissing": { - original: &v1.Pod{ + original: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "gs://foo", constants.IstioInterceptionModeAnnotation: constants.IstioInterceptModeRedirect, }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, }, { Name: "istio-sidecar", - SecurityContext: &v1.SecurityContext{ + SecurityContext: &corev1.SecurityContext{ RunAsUser: ptr.Int64(501), }, }, }, }, }, - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "gs://foo", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-provision-location", MountPath: constants.DefaultModelLocalMountPath, @@ -3868,19 +3865,19 @@ func TestStorageInitializerUIDForIstioCNI(t *testing.T) { }, { Name: "istio-sidecar", - SecurityContext: &v1.SecurityContext{ + SecurityContext: &corev1.SecurityContext{ RunAsUser: ptr.Int64(501), }, }, }, - InitContainers: []v1.Container{ + InitContainers: []corev1.Container{ { Name: "storage-initializer", Image: StorageInitializerContainerImage + ":" + StorageInitializerContainerImageVersion, Args: []string{"gs://foo", constants.DefaultModelLocalMountPath}, Resources: resourceRequirement, TerminationMessagePolicy: "FallbackToLogsOnError", - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-provision-location", MountPath: constants.DefaultModelLocalMountPath, @@ -3888,11 +3885,11 @@ func TestStorageInitializerUIDForIstioCNI(t *testing.T) { }, }, }, - Volumes: []v1.Volume{ + Volumes: []corev1.Volume{ { Name: "kserve-provision-location", - VolumeSource: v1.VolumeSource{ - EmptyDir: &v1.EmptyDirVolumeSource{}, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, }, }, }, @@ -3900,7 +3897,7 @@ func TestStorageInitializerUIDForIstioCNI(t *testing.T) { }, }, "StorageInitializerUidNotSetIfIstioStatusEmpty": { - original: &v1.Pod{ + original: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "gs://foo", @@ -3908,31 +3905,31 @@ func TestStorageInitializerUIDForIstioCNI(t *testing.T) { constants.IstioInterceptionModeAnnotation: constants.IstioInterceptModeRedirect, }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, }, { Name: "istio-sidecar", - SecurityContext: &v1.SecurityContext{ + SecurityContext: &corev1.SecurityContext{ RunAsUser: ptr.Int64(501), }, }, }, }, }, - expected: &v1.Pod{ + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: "gs://foo", }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-provision-location", MountPath: constants.DefaultModelLocalMountPath, @@ -3942,19 +3939,19 @@ func TestStorageInitializerUIDForIstioCNI(t *testing.T) { }, { Name: "istio-sidecar", - SecurityContext: &v1.SecurityContext{ + SecurityContext: &corev1.SecurityContext{ RunAsUser: ptr.Int64(501), }, }, }, - InitContainers: []v1.Container{ + InitContainers: []corev1.Container{ { Name: "storage-initializer", Image: StorageInitializerContainerImage + ":" + StorageInitializerContainerImageVersion, Args: []string{"gs://foo", constants.DefaultModelLocalMountPath}, Resources: resourceRequirement, TerminationMessagePolicy: "FallbackToLogsOnError", - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-provision-location", MountPath: constants.DefaultModelLocalMountPath, @@ -3962,11 +3959,11 @@ func TestStorageInitializerUIDForIstioCNI(t *testing.T) { }, }, }, - Volumes: []v1.Volume{ + Volumes: []corev1.Volume{ { Name: "kserve-provision-location", - VolumeSource: v1.VolumeSource{ - EmptyDir: &v1.EmptyDirVolumeSource{}, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, }, }, }, @@ -3977,7 +3974,7 @@ func TestStorageInitializerUIDForIstioCNI(t *testing.T) { for name, scenario := range scenarios { injector := &StorageInitializerInjector{ - credentialBuilder: credentials.NewCredentialBuilder(c, clientset, &v1.ConfigMap{ + credentialBuilder: credentials.NewCredentialBuilder(c, clientset, &corev1.ConfigMap{ Data: map[string]string{}, }), config: storageInitializerConfig, @@ -4037,12 +4034,12 @@ func TestLocalModelPVC(t *testing.T) { } podScenarios := make(map[string]struct { - original *v1.Pod - expected *v1.Pod + original *corev1.Pod + expected *corev1.Pod }) for name, scenario := range scenarios { - original := &v1.Pod{ + original := &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: scenario.storageUri, @@ -4053,25 +4050,25 @@ func TestLocalModelPVC(t *testing.T) { constants.LocalModelLabel: scenario.localModelLabel, }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, }, }, }, } - expected := &v1.Pod{ + expected := &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ constants.StorageInitializerSourceUriInternalAnnotationKey: scenario.storageUri, }, }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ { Name: constants.InferenceServiceContainerName, - VolumeMounts: []v1.VolumeMount{ + VolumeMounts: []corev1.VolumeMount{ { Name: "kserve-pvc-source", MountPath: constants.DefaultModelLocalMountPath, @@ -4081,11 +4078,11 @@ func TestLocalModelPVC(t *testing.T) { }, }, }, - Volumes: []v1.Volume{ + Volumes: []corev1.Volume{ { Name: "kserve-pvc-source", - VolumeSource: v1.VolumeSource{ - PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{ClaimName: scenario.pvcName, ReadOnly: false}, + VolumeSource: corev1.VolumeSource{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ClaimName: scenario.pvcName, ReadOnly: false}, }, }, }, @@ -4093,13 +4090,13 @@ func TestLocalModelPVC(t *testing.T) { } podScenarios[name] = struct { - original *v1.Pod - expected *v1.Pod + original *corev1.Pod + expected *corev1.Pod }{original: original, expected: expected} } for name, scenario := range podScenarios { injector := &StorageInitializerInjector{ - credentialBuilder: credentials.NewCredentialBuilder(c, clientset, &v1.ConfigMap{ + credentialBuilder: credentials.NewCredentialBuilder(c, clientset, &corev1.ConfigMap{ Data: map[string]string{}, }), config: storageConfig, diff --git a/pkg/webhook/admission/pod/suite_test.go b/pkg/webhook/admission/pod/suite_test.go index aa7fff6f561..6ac01a1bff4 100644 --- a/pkg/webhook/admission/pod/suite_test.go +++ b/pkg/webhook/admission/pod/suite_test.go @@ -31,9 +31,11 @@ import ( pkgtest "github.com/kserve/kserve/pkg/testing" ) -var cfg *rest.Config -var c client.Client -var clientset kubernetes.Interface +var ( + cfg *rest.Config + c client.Client + clientset kubernetes.Interface +) func TestMain(m *testing.M) { crdDirectoryPaths := []string{ diff --git a/pkg/webhook/admission/servingruntime/servingruntime_webhook.go b/pkg/webhook/admission/servingruntime/servingruntime_webhook.go index 58046e26320..f3659f7c39c 100644 --- a/pkg/webhook/admission/servingruntime/servingruntime_webhook.go +++ b/pkg/webhook/admission/servingruntime/servingruntime_webhook.go @@ -25,12 +25,13 @@ import ( "strconv" "strings" - "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" - "github.com/kserve/kserve/pkg/constants" - "github.com/kserve/kserve/pkg/utils" "sigs.k8s.io/controller-runtime/pkg/client" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/webhook/admission" + + "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" + "github.com/kserve/kserve/pkg/constants" + "github.com/kserve/kserve/pkg/utils" ) var log = logf.Log.WithName(constants.ServingRuntimeValidatorWebhookName) @@ -77,7 +78,7 @@ func (sr *ServingRuntimeValidator) Handle(ctx context.Context, req admission.Req } ExistingRuntimes := &v1alpha1.ServingRuntimeList{} - if err := sr.Client.List(context.TODO(), ExistingRuntimes, client.InNamespace(servingRuntime.Namespace)); err != nil { + if err := sr.Client.List(ctx, ExistingRuntimes, client.InNamespace(servingRuntime.Namespace)); err != nil { log.Error(err, "Failed to get serving runtime list", "namespace", servingRuntime.Namespace) return admission.Errored(http.StatusInternalServerError, err) } @@ -116,7 +117,7 @@ func (csr *ClusterServingRuntimeValidator) Handle(ctx context.Context, req admis } ExistingRuntimes := &v1alpha1.ClusterServingRuntimeList{} - if err := csr.Client.List(context.TODO(), ExistingRuntimes); err != nil { + if err := csr.Client.List(ctx, ExistingRuntimes); err != nil { log.Error(err, "Failed to get cluster serving runtime list") return admission.Errored(http.StatusInternalServerError, err) } diff --git a/pkg/webhook/admission/servingruntime/servingruntime_webhook_test.go b/pkg/webhook/admission/servingruntime/servingruntime_webhook_test.go index 71610556616..301c5661abc 100644 --- a/pkg/webhook/admission/servingruntime/servingruntime_webhook_test.go +++ b/pkg/webhook/admission/servingruntime/servingruntime_webhook_test.go @@ -19,17 +19,15 @@ package servingruntime import ( "errors" "fmt" - - "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" - "github.com/kserve/kserve/pkg/constants" - "github.com/onsi/gomega" - "testing" + "github.com/onsi/gomega" "google.golang.org/protobuf/proto" - corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" + "github.com/kserve/kserve/pkg/constants" ) func TestValidateServingRuntimePriority(t *testing.T) { @@ -1810,6 +1808,7 @@ func TestValidateMultiNodeVariables(t *testing.T) { }) } } + func intPtr(i int) *int { return &i } diff --git a/python/VERSION b/python/VERSION index a803cc227fe..8185f23c2c0 100644 --- a/python/VERSION +++ b/python/VERSION @@ -1 +1 @@ -0.14.0 +0.15.0rc0 diff --git a/python/aiffairness/poetry.lock b/python/aiffairness/poetry.lock index 13097276512..f7070dd71b0 100644 --- a/python/aiffairness/poetry.lock +++ b/python/aiffairness/poetry.lock @@ -950,7 +950,7 @@ files = [ [[package]] name = "kserve" -version = "0.14.0" +version = "0.15.0rc0" description = "KServe Python SDK" optional = false python-versions = ">=3.9,<3.13" diff --git a/python/aiffairness/pyproject.toml b/python/aiffairness/pyproject.toml index 31d38a155d5..069525e78fc 100644 --- a/python/aiffairness/pyproject.toml +++ b/python/aiffairness/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "aifserver" -version = "0.14.0" +version = "0.15.0rc0" description = "'Model Server implementation for AI fairness. Not intended for use outside KServe Frameworks Images." authors = ["Andrew Butler "] license = "https://github.com/kserve/kserve/blob/master/LICENSE" diff --git a/python/artexplainer/poetry.lock b/python/artexplainer/poetry.lock index ca77dcea662..8d71c115744 100644 --- a/python/artexplainer/poetry.lock +++ b/python/artexplainer/poetry.lock @@ -724,7 +724,7 @@ rich = "*" [[package]] name = "kserve" -version = "0.14.0" +version = "0.15.0rc0" description = "KServe Python SDK" optional = false python-versions = ">=3.9,<3.13" diff --git a/python/artexplainer/pyproject.toml b/python/artexplainer/pyproject.toml index f75663e19b0..b225f762186 100644 --- a/python/artexplainer/pyproject.toml +++ b/python/artexplainer/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "artserver" -version = "0.14.0" +version = "0.15.0rc0" description = "Model Server implementation for AI Robustness Toolbox. Not intended for use outside KServe Frameworks Images." authors = ["Andrew Butler "] license = "https://github.com/kserve/kserve/blob/master/LICENSE" diff --git a/python/custom_model/poetry.lock b/python/custom_model/poetry.lock index 543a83a68bb..8aa069a8d7b 100644 --- a/python/custom_model/poetry.lock +++ b/python/custom_model/poetry.lock @@ -13,112 +13,112 @@ files = [ [[package]] name = "aiohttp" -version = "3.10.5" +version = "3.10.11" description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.8" files = [ - {file = "aiohttp-3.10.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:18a01eba2574fb9edd5f6e5fb25f66e6ce061da5dab5db75e13fe1558142e0a3"}, - {file = "aiohttp-3.10.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:94fac7c6e77ccb1ca91e9eb4cb0ac0270b9fb9b289738654120ba8cebb1189c6"}, - {file = "aiohttp-3.10.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2f1f1c75c395991ce9c94d3e4aa96e5c59c8356a15b1c9231e783865e2772699"}, - {file = "aiohttp-3.10.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f7acae3cf1a2a2361ec4c8e787eaaa86a94171d2417aae53c0cca6ca3118ff6"}, - {file = "aiohttp-3.10.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:94c4381ffba9cc508b37d2e536b418d5ea9cfdc2848b9a7fea6aebad4ec6aac1"}, - {file = "aiohttp-3.10.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c31ad0c0c507894e3eaa843415841995bf8de4d6b2d24c6e33099f4bc9fc0d4f"}, - {file = "aiohttp-3.10.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0912b8a8fadeb32ff67a3ed44249448c20148397c1ed905d5dac185b4ca547bb"}, - {file = "aiohttp-3.10.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d93400c18596b7dc4794d48a63fb361b01a0d8eb39f28800dc900c8fbdaca91"}, - {file = "aiohttp-3.10.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d00f3c5e0d764a5c9aa5a62d99728c56d455310bcc288a79cab10157b3af426f"}, - {file = "aiohttp-3.10.5-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:d742c36ed44f2798c8d3f4bc511f479b9ceef2b93f348671184139e7d708042c"}, - {file = "aiohttp-3.10.5-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:814375093edae5f1cb31e3407997cf3eacefb9010f96df10d64829362ae2df69"}, - {file = "aiohttp-3.10.5-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8224f98be68a84b19f48e0bdc14224b5a71339aff3a27df69989fa47d01296f3"}, - {file = "aiohttp-3.10.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d9a487ef090aea982d748b1b0d74fe7c3950b109df967630a20584f9a99c0683"}, - {file = "aiohttp-3.10.5-cp310-cp310-win32.whl", hash = "sha256:d9ef084e3dc690ad50137cc05831c52b6ca428096e6deb3c43e95827f531d5ef"}, - {file = "aiohttp-3.10.5-cp310-cp310-win_amd64.whl", hash = "sha256:66bf9234e08fe561dccd62083bf67400bdbf1c67ba9efdc3dac03650e97c6088"}, - {file = "aiohttp-3.10.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8c6a4e5e40156d72a40241a25cc226051c0a8d816610097a8e8f517aeacd59a2"}, - {file = "aiohttp-3.10.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2c634a3207a5445be65536d38c13791904fda0748b9eabf908d3fe86a52941cf"}, - {file = "aiohttp-3.10.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4aff049b5e629ef9b3e9e617fa6e2dfeda1bf87e01bcfecaf3949af9e210105e"}, - {file = "aiohttp-3.10.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1942244f00baaacaa8155eca94dbd9e8cc7017deb69b75ef67c78e89fdad3c77"}, - {file = "aiohttp-3.10.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e04a1f2a65ad2f93aa20f9ff9f1b672bf912413e5547f60749fa2ef8a644e061"}, - {file = "aiohttp-3.10.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7f2bfc0032a00405d4af2ba27f3c429e851d04fad1e5ceee4080a1c570476697"}, - {file = "aiohttp-3.10.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:424ae21498790e12eb759040bbb504e5e280cab64693d14775c54269fd1d2bb7"}, - {file = "aiohttp-3.10.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:975218eee0e6d24eb336d0328c768ebc5d617609affaca5dbbd6dd1984f16ed0"}, - {file = "aiohttp-3.10.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4120d7fefa1e2d8fb6f650b11489710091788de554e2b6f8347c7a20ceb003f5"}, - {file = "aiohttp-3.10.5-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:b90078989ef3fc45cf9221d3859acd1108af7560c52397ff4ace8ad7052a132e"}, - {file = "aiohttp-3.10.5-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ba5a8b74c2a8af7d862399cdedce1533642fa727def0b8c3e3e02fcb52dca1b1"}, - {file = "aiohttp-3.10.5-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:02594361128f780eecc2a29939d9dfc870e17b45178a867bf61a11b2a4367277"}, - {file = "aiohttp-3.10.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8fb4fc029e135859f533025bc82047334e24b0d489e75513144f25408ecaf058"}, - {file = "aiohttp-3.10.5-cp311-cp311-win32.whl", hash = "sha256:e1ca1ef5ba129718a8fc827b0867f6aa4e893c56eb00003b7367f8a733a9b072"}, - {file = "aiohttp-3.10.5-cp311-cp311-win_amd64.whl", hash = "sha256:349ef8a73a7c5665cca65c88ab24abe75447e28aa3bc4c93ea5093474dfdf0ff"}, - {file = "aiohttp-3.10.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:305be5ff2081fa1d283a76113b8df7a14c10d75602a38d9f012935df20731487"}, - {file = "aiohttp-3.10.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3a1c32a19ee6bbde02f1cb189e13a71b321256cc1d431196a9f824050b160d5a"}, - {file = "aiohttp-3.10.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:61645818edd40cc6f455b851277a21bf420ce347baa0b86eaa41d51ef58ba23d"}, - {file = "aiohttp-3.10.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c225286f2b13bab5987425558baa5cbdb2bc925b2998038fa028245ef421e75"}, - {file = "aiohttp-3.10.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ba01ebc6175e1e6b7275c907a3a36be48a2d487549b656aa90c8a910d9f3178"}, - {file = "aiohttp-3.10.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8eaf44ccbc4e35762683078b72bf293f476561d8b68ec8a64f98cf32811c323e"}, - {file = "aiohttp-3.10.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1c43eb1ab7cbf411b8e387dc169acb31f0ca0d8c09ba63f9eac67829585b44f"}, - {file = "aiohttp-3.10.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:de7a5299827253023c55ea549444e058c0eb496931fa05d693b95140a947cb73"}, - {file = "aiohttp-3.10.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4790f0e15f00058f7599dab2b206d3049d7ac464dc2e5eae0e93fa18aee9e7bf"}, - {file = "aiohttp-3.10.5-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:44b324a6b8376a23e6ba25d368726ee3bc281e6ab306db80b5819999c737d820"}, - {file = "aiohttp-3.10.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:0d277cfb304118079e7044aad0b76685d30ecb86f83a0711fc5fb257ffe832ca"}, - {file = "aiohttp-3.10.5-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:54d9ddea424cd19d3ff6128601a4a4d23d54a421f9b4c0fff740505813739a91"}, - {file = "aiohttp-3.10.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4f1c9866ccf48a6df2b06823e6ae80573529f2af3a0992ec4fe75b1a510df8a6"}, - {file = "aiohttp-3.10.5-cp312-cp312-win32.whl", hash = "sha256:dc4826823121783dccc0871e3f405417ac116055bf184ac04c36f98b75aacd12"}, - {file = "aiohttp-3.10.5-cp312-cp312-win_amd64.whl", hash = "sha256:22c0a23a3b3138a6bf76fc553789cb1a703836da86b0f306b6f0dc1617398abc"}, - {file = "aiohttp-3.10.5-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7f6b639c36734eaa80a6c152a238242bedcee9b953f23bb887e9102976343092"}, - {file = "aiohttp-3.10.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f29930bc2921cef955ba39a3ff87d2c4398a0394ae217f41cb02d5c26c8b1b77"}, - {file = "aiohttp-3.10.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f489a2c9e6455d87eabf907ac0b7d230a9786be43fbe884ad184ddf9e9c1e385"}, - {file = "aiohttp-3.10.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:123dd5b16b75b2962d0fff566effb7a065e33cd4538c1692fb31c3bda2bfb972"}, - {file = "aiohttp-3.10.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b98e698dc34966e5976e10bbca6d26d6724e6bdea853c7c10162a3235aba6e16"}, - {file = "aiohttp-3.10.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3b9162bab7e42f21243effc822652dc5bb5e8ff42a4eb62fe7782bcbcdfacf6"}, - {file = "aiohttp-3.10.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1923a5c44061bffd5eebeef58cecf68096e35003907d8201a4d0d6f6e387ccaa"}, - {file = "aiohttp-3.10.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d55f011da0a843c3d3df2c2cf4e537b8070a419f891c930245f05d329c4b0689"}, - {file = "aiohttp-3.10.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:afe16a84498441d05e9189a15900640a2d2b5e76cf4efe8cbb088ab4f112ee57"}, - {file = "aiohttp-3.10.5-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f8112fb501b1e0567a1251a2fd0747baae60a4ab325a871e975b7bb67e59221f"}, - {file = "aiohttp-3.10.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:1e72589da4c90337837fdfe2026ae1952c0f4a6e793adbbfbdd40efed7c63599"}, - {file = "aiohttp-3.10.5-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:4d46c7b4173415d8e583045fbc4daa48b40e31b19ce595b8d92cf639396c15d5"}, - {file = "aiohttp-3.10.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:33e6bc4bab477c772a541f76cd91e11ccb6d2efa2b8d7d7883591dfb523e5987"}, - {file = "aiohttp-3.10.5-cp313-cp313-win32.whl", hash = "sha256:c58c6837a2c2a7cf3133983e64173aec11f9c2cd8e87ec2fdc16ce727bcf1a04"}, - {file = "aiohttp-3.10.5-cp313-cp313-win_amd64.whl", hash = "sha256:38172a70005252b6893088c0f5e8a47d173df7cc2b2bd88650957eb84fcf5022"}, - {file = "aiohttp-3.10.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:f6f18898ace4bcd2d41a122916475344a87f1dfdec626ecde9ee802a711bc569"}, - {file = "aiohttp-3.10.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5ede29d91a40ba22ac1b922ef510aab871652f6c88ef60b9dcdf773c6d32ad7a"}, - {file = "aiohttp-3.10.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:673f988370f5954df96cc31fd99c7312a3af0a97f09e407399f61583f30da9bc"}, - {file = "aiohttp-3.10.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58718e181c56a3c02d25b09d4115eb02aafe1a732ce5714ab70326d9776457c3"}, - {file = "aiohttp-3.10.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4b38b1570242fbab8d86a84128fb5b5234a2f70c2e32f3070143a6d94bc854cf"}, - {file = "aiohttp-3.10.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:074d1bff0163e107e97bd48cad9f928fa5a3eb4b9d33366137ffce08a63e37fe"}, - {file = "aiohttp-3.10.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd31f176429cecbc1ba499d4aba31aaccfea488f418d60376b911269d3b883c5"}, - {file = "aiohttp-3.10.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7384d0b87d4635ec38db9263e6a3f1eb609e2e06087f0aa7f63b76833737b471"}, - {file = "aiohttp-3.10.5-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:8989f46f3d7ef79585e98fa991e6ded55d2f48ae56d2c9fa5e491a6e4effb589"}, - {file = "aiohttp-3.10.5-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:c83f7a107abb89a227d6c454c613e7606c12a42b9a4ca9c5d7dad25d47c776ae"}, - {file = "aiohttp-3.10.5-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:cde98f323d6bf161041e7627a5fd763f9fd829bcfcd089804a5fdce7bb6e1b7d"}, - {file = "aiohttp-3.10.5-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:676f94c5480d8eefd97c0c7e3953315e4d8c2b71f3b49539beb2aa676c58272f"}, - {file = "aiohttp-3.10.5-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:2d21ac12dc943c68135ff858c3a989f2194a709e6e10b4c8977d7fcd67dfd511"}, - {file = "aiohttp-3.10.5-cp38-cp38-win32.whl", hash = "sha256:17e997105bd1a260850272bfb50e2a328e029c941c2708170d9d978d5a30ad9a"}, - {file = "aiohttp-3.10.5-cp38-cp38-win_amd64.whl", hash = "sha256:1c19de68896747a2aa6257ae4cf6ef59d73917a36a35ee9d0a6f48cff0f94db8"}, - {file = "aiohttp-3.10.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7e2fe37ac654032db1f3499fe56e77190282534810e2a8e833141a021faaab0e"}, - {file = "aiohttp-3.10.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f5bf3ead3cb66ab990ee2561373b009db5bc0e857549b6c9ba84b20bc462e172"}, - {file = "aiohttp-3.10.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1b2c16a919d936ca87a3c5f0e43af12a89a3ce7ccbce59a2d6784caba945b68b"}, - {file = "aiohttp-3.10.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad146dae5977c4dd435eb31373b3fe9b0b1bf26858c6fc452bf6af394067e10b"}, - {file = "aiohttp-3.10.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8c5c6fa16412b35999320f5c9690c0f554392dc222c04e559217e0f9ae244b92"}, - {file = "aiohttp-3.10.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:95c4dc6f61d610bc0ee1edc6f29d993f10febfe5b76bb470b486d90bbece6b22"}, - {file = "aiohttp-3.10.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da452c2c322e9ce0cfef392e469a26d63d42860f829026a63374fde6b5c5876f"}, - {file = "aiohttp-3.10.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:898715cf566ec2869d5cb4d5fb4be408964704c46c96b4be267442d265390f32"}, - {file = "aiohttp-3.10.5-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:391cc3a9c1527e424c6865e087897e766a917f15dddb360174a70467572ac6ce"}, - {file = "aiohttp-3.10.5-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:380f926b51b92d02a34119d072f178d80bbda334d1a7e10fa22d467a66e494db"}, - {file = "aiohttp-3.10.5-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ce91db90dbf37bb6fa0997f26574107e1b9d5ff939315247b7e615baa8ec313b"}, - {file = "aiohttp-3.10.5-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9093a81e18c45227eebe4c16124ebf3e0d893830c6aca7cc310bfca8fe59d857"}, - {file = "aiohttp-3.10.5-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ee40b40aa753d844162dcc80d0fe256b87cba48ca0054f64e68000453caead11"}, - {file = "aiohttp-3.10.5-cp39-cp39-win32.whl", hash = "sha256:03f2645adbe17f274444953bdea69f8327e9d278d961d85657cb0d06864814c1"}, - {file = "aiohttp-3.10.5-cp39-cp39-win_amd64.whl", hash = "sha256:d17920f18e6ee090bdd3d0bfffd769d9f2cb4c8ffde3eb203777a3895c128862"}, - {file = "aiohttp-3.10.5.tar.gz", hash = "sha256:f071854b47d39591ce9a17981c46790acb30518e2f83dfca8db2dfa091178691"}, + {file = "aiohttp-3.10.11-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5077b1a5f40ffa3ba1f40d537d3bec4383988ee51fbba6b74aa8fb1bc466599e"}, + {file = "aiohttp-3.10.11-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8d6a14a4d93b5b3c2891fca94fa9d41b2322a68194422bef0dd5ec1e57d7d298"}, + {file = "aiohttp-3.10.11-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ffbfde2443696345e23a3c597049b1dd43049bb65337837574205e7368472177"}, + {file = "aiohttp-3.10.11-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20b3d9e416774d41813bc02fdc0663379c01817b0874b932b81c7f777f67b217"}, + {file = "aiohttp-3.10.11-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2b943011b45ee6bf74b22245c6faab736363678e910504dd7531a58c76c9015a"}, + {file = "aiohttp-3.10.11-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48bc1d924490f0d0b3658fe5c4b081a4d56ebb58af80a6729d4bd13ea569797a"}, + {file = "aiohttp-3.10.11-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e12eb3f4b1f72aaaf6acd27d045753b18101524f72ae071ae1c91c1cd44ef115"}, + {file = "aiohttp-3.10.11-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f14ebc419a568c2eff3c1ed35f634435c24ead2fe19c07426af41e7adb68713a"}, + {file = "aiohttp-3.10.11-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:72b191cdf35a518bfc7ca87d770d30941decc5aaf897ec8b484eb5cc8c7706f3"}, + {file = "aiohttp-3.10.11-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5ab2328a61fdc86424ee540d0aeb8b73bbcad7351fb7cf7a6546fc0bcffa0038"}, + {file = "aiohttp-3.10.11-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:aa93063d4af05c49276cf14e419550a3f45258b6b9d1f16403e777f1addf4519"}, + {file = "aiohttp-3.10.11-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:30283f9d0ce420363c24c5c2421e71a738a2155f10adbb1a11a4d4d6d2715cfc"}, + {file = "aiohttp-3.10.11-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e5358addc8044ee49143c546d2182c15b4ac3a60be01c3209374ace05af5733d"}, + {file = "aiohttp-3.10.11-cp310-cp310-win32.whl", hash = "sha256:e1ffa713d3ea7cdcd4aea9cddccab41edf6882fa9552940344c44e59652e1120"}, + {file = "aiohttp-3.10.11-cp310-cp310-win_amd64.whl", hash = "sha256:778cbd01f18ff78b5dd23c77eb82987ee4ba23408cbed233009fd570dda7e674"}, + {file = "aiohttp-3.10.11-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:80ff08556c7f59a7972b1e8919f62e9c069c33566a6d28586771711e0eea4f07"}, + {file = "aiohttp-3.10.11-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2c8f96e9ee19f04c4914e4e7a42a60861066d3e1abf05c726f38d9d0a466e695"}, + {file = "aiohttp-3.10.11-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fb8601394d537da9221947b5d6e62b064c9a43e88a1ecd7414d21a1a6fba9c24"}, + {file = "aiohttp-3.10.11-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ea224cf7bc2d8856d6971cea73b1d50c9c51d36971faf1abc169a0d5f85a382"}, + {file = "aiohttp-3.10.11-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:db9503f79e12d5d80b3efd4d01312853565c05367493379df76d2674af881caa"}, + {file = "aiohttp-3.10.11-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0f449a50cc33f0384f633894d8d3cd020e3ccef81879c6e6245c3c375c448625"}, + {file = "aiohttp-3.10.11-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82052be3e6d9e0c123499127782a01a2b224b8af8c62ab46b3f6197035ad94e9"}, + {file = "aiohttp-3.10.11-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:20063c7acf1eec550c8eb098deb5ed9e1bb0521613b03bb93644b810986027ac"}, + {file = "aiohttp-3.10.11-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:489cced07a4c11488f47aab1f00d0c572506883f877af100a38f1fedaa884c3a"}, + {file = "aiohttp-3.10.11-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ea9b3bab329aeaa603ed3bf605f1e2a6f36496ad7e0e1aa42025f368ee2dc07b"}, + {file = "aiohttp-3.10.11-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ca117819d8ad113413016cb29774b3f6d99ad23c220069789fc050267b786c16"}, + {file = "aiohttp-3.10.11-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:2dfb612dcbe70fb7cdcf3499e8d483079b89749c857a8f6e80263b021745c730"}, + {file = "aiohttp-3.10.11-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9b615d3da0d60e7d53c62e22b4fd1c70f4ae5993a44687b011ea3a2e49051b8"}, + {file = "aiohttp-3.10.11-cp311-cp311-win32.whl", hash = "sha256:29103f9099b6068bbdf44d6a3d090e0a0b2be6d3c9f16a070dd9d0d910ec08f9"}, + {file = "aiohttp-3.10.11-cp311-cp311-win_amd64.whl", hash = "sha256:236b28ceb79532da85d59aa9b9bf873b364e27a0acb2ceaba475dc61cffb6f3f"}, + {file = "aiohttp-3.10.11-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:7480519f70e32bfb101d71fb9a1f330fbd291655a4c1c922232a48c458c52710"}, + {file = "aiohttp-3.10.11-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f65267266c9aeb2287a6622ee2bb39490292552f9fbf851baabc04c9f84e048d"}, + {file = "aiohttp-3.10.11-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7400a93d629a0608dc1d6c55f1e3d6e07f7375745aaa8bd7f085571e4d1cee97"}, + {file = "aiohttp-3.10.11-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f34b97e4b11b8d4eb2c3a4f975be626cc8af99ff479da7de49ac2c6d02d35725"}, + {file = "aiohttp-3.10.11-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1e7b825da878464a252ccff2958838f9caa82f32a8dbc334eb9b34a026e2c636"}, + {file = "aiohttp-3.10.11-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f9f92a344c50b9667827da308473005f34767b6a2a60d9acff56ae94f895f385"}, + {file = "aiohttp-3.10.11-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc6f1ab987a27b83c5268a17218463c2ec08dbb754195113867a27b166cd6087"}, + {file = "aiohttp-3.10.11-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1dc0f4ca54842173d03322793ebcf2c8cc2d34ae91cc762478e295d8e361e03f"}, + {file = "aiohttp-3.10.11-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7ce6a51469bfaacff146e59e7fb61c9c23006495d11cc24c514a455032bcfa03"}, + {file = "aiohttp-3.10.11-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:aad3cd91d484d065ede16f3cf15408254e2469e3f613b241a1db552c5eb7ab7d"}, + {file = "aiohttp-3.10.11-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f4df4b8ca97f658c880fb4b90b1d1ec528315d4030af1ec763247ebfd33d8b9a"}, + {file = "aiohttp-3.10.11-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:2e4e18a0a2d03531edbc06c366954e40a3f8d2a88d2b936bbe78a0c75a3aab3e"}, + {file = "aiohttp-3.10.11-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6ce66780fa1a20e45bc753cda2a149daa6dbf1561fc1289fa0c308391c7bc0a4"}, + {file = "aiohttp-3.10.11-cp312-cp312-win32.whl", hash = "sha256:a919c8957695ea4c0e7a3e8d16494e3477b86f33067478f43106921c2fef15bb"}, + {file = "aiohttp-3.10.11-cp312-cp312-win_amd64.whl", hash = "sha256:b5e29706e6389a2283a91611c91bf24f218962717c8f3b4e528ef529d112ee27"}, + {file = "aiohttp-3.10.11-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:703938e22434d7d14ec22f9f310559331f455018389222eed132808cd8f44127"}, + {file = "aiohttp-3.10.11-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9bc50b63648840854e00084c2b43035a62e033cb9b06d8c22b409d56eb098413"}, + {file = "aiohttp-3.10.11-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5f0463bf8b0754bc744e1feb61590706823795041e63edf30118a6f0bf577461"}, + {file = "aiohttp-3.10.11-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f6c6dec398ac5a87cb3a407b068e1106b20ef001c344e34154616183fe684288"}, + {file = "aiohttp-3.10.11-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bcaf2d79104d53d4dcf934f7ce76d3d155302d07dae24dff6c9fffd217568067"}, + {file = "aiohttp-3.10.11-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:25fd5470922091b5a9aeeb7e75be609e16b4fba81cdeaf12981393fb240dd10e"}, + {file = "aiohttp-3.10.11-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbde2ca67230923a42161b1f408c3992ae6e0be782dca0c44cb3206bf330dee1"}, + {file = "aiohttp-3.10.11-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:249c8ff8d26a8b41a0f12f9df804e7c685ca35a207e2410adbd3e924217b9006"}, + {file = "aiohttp-3.10.11-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:878ca6a931ee8c486a8f7b432b65431d095c522cbeb34892bee5be97b3481d0f"}, + {file = "aiohttp-3.10.11-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:8663f7777ce775f0413324be0d96d9730959b2ca73d9b7e2c2c90539139cbdd6"}, + {file = "aiohttp-3.10.11-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:6cd3f10b01f0c31481fba8d302b61603a2acb37b9d30e1d14e0f5a58b7b18a31"}, + {file = "aiohttp-3.10.11-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:4e8d8aad9402d3aa02fdc5ca2fe68bcb9fdfe1f77b40b10410a94c7f408b664d"}, + {file = "aiohttp-3.10.11-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:38e3c4f80196b4f6c3a85d134a534a56f52da9cb8d8e7af1b79a32eefee73a00"}, + {file = "aiohttp-3.10.11-cp313-cp313-win32.whl", hash = "sha256:fc31820cfc3b2863c6e95e14fcf815dc7afe52480b4dc03393c4873bb5599f71"}, + {file = "aiohttp-3.10.11-cp313-cp313-win_amd64.whl", hash = "sha256:4996ff1345704ffdd6d75fb06ed175938c133425af616142e7187f28dc75f14e"}, + {file = "aiohttp-3.10.11-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:74baf1a7d948b3d640badeac333af581a367ab916b37e44cf90a0334157cdfd2"}, + {file = "aiohttp-3.10.11-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:473aebc3b871646e1940c05268d451f2543a1d209f47035b594b9d4e91ce8339"}, + {file = "aiohttp-3.10.11-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c2f746a6968c54ab2186574e15c3f14f3e7f67aef12b761e043b33b89c5b5f95"}, + {file = "aiohttp-3.10.11-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d110cabad8360ffa0dec8f6ec60e43286e9d251e77db4763a87dcfe55b4adb92"}, + {file = "aiohttp-3.10.11-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e0099c7d5d7afff4202a0c670e5b723f7718810000b4abcbc96b064129e64bc7"}, + {file = "aiohttp-3.10.11-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0316e624b754dbbf8c872b62fe6dcb395ef20c70e59890dfa0de9eafccd2849d"}, + {file = "aiohttp-3.10.11-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a5f7ab8baf13314e6b2485965cbacb94afff1e93466ac4d06a47a81c50f9cca"}, + {file = "aiohttp-3.10.11-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c891011e76041e6508cbfc469dd1a8ea09bc24e87e4c204e05f150c4c455a5fa"}, + {file = "aiohttp-3.10.11-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:9208299251370ee815473270c52cd3f7069ee9ed348d941d574d1457d2c73e8b"}, + {file = "aiohttp-3.10.11-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:459f0f32c8356e8125f45eeff0ecf2b1cb6db1551304972702f34cd9e6c44658"}, + {file = "aiohttp-3.10.11-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:14cdc8c1810bbd4b4b9f142eeee23cda528ae4e57ea0923551a9af4820980e39"}, + {file = "aiohttp-3.10.11-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:971aa438a29701d4b34e4943e91b5e984c3ae6ccbf80dd9efaffb01bd0b243a9"}, + {file = "aiohttp-3.10.11-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:9a309c5de392dfe0f32ee57fa43ed8fc6ddf9985425e84bd51ed66bb16bce3a7"}, + {file = "aiohttp-3.10.11-cp38-cp38-win32.whl", hash = "sha256:9ec1628180241d906a0840b38f162a3215114b14541f1a8711c368a8739a9be4"}, + {file = "aiohttp-3.10.11-cp38-cp38-win_amd64.whl", hash = "sha256:9c6e0ffd52c929f985c7258f83185d17c76d4275ad22e90aa29f38e211aacbec"}, + {file = "aiohttp-3.10.11-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:cdc493a2e5d8dc79b2df5bec9558425bcd39aff59fc949810cbd0832e294b106"}, + {file = "aiohttp-3.10.11-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b3e70f24e7d0405be2348da9d5a7836936bf3a9b4fd210f8c37e8d48bc32eca6"}, + {file = "aiohttp-3.10.11-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:968b8fb2a5eee2770eda9c7b5581587ef9b96fbdf8dcabc6b446d35ccc69df01"}, + {file = "aiohttp-3.10.11-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:deef4362af9493d1382ef86732ee2e4cbc0d7c005947bd54ad1a9a16dd59298e"}, + {file = "aiohttp-3.10.11-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:686b03196976e327412a1b094f4120778c7c4b9cff9bce8d2fdfeca386b89829"}, + {file = "aiohttp-3.10.11-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3bf6d027d9d1d34e1c2e1645f18a6498c98d634f8e373395221121f1c258ace8"}, + {file = "aiohttp-3.10.11-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:099fd126bf960f96d34a760e747a629c27fb3634da5d05c7ef4d35ef4ea519fc"}, + {file = "aiohttp-3.10.11-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c73c4d3dae0b4644bc21e3de546530531d6cdc88659cdeb6579cd627d3c206aa"}, + {file = "aiohttp-3.10.11-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0c5580f3c51eea91559db3facd45d72e7ec970b04528b4709b1f9c2555bd6d0b"}, + {file = "aiohttp-3.10.11-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:fdf6429f0caabfd8a30c4e2eaecb547b3c340e4730ebfe25139779b9815ba138"}, + {file = "aiohttp-3.10.11-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:d97187de3c276263db3564bb9d9fad9e15b51ea10a371ffa5947a5ba93ad6777"}, + {file = "aiohttp-3.10.11-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:0acafb350cfb2eba70eb5d271f55e08bd4502ec35e964e18ad3e7d34d71f7261"}, + {file = "aiohttp-3.10.11-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c13ed0c779911c7998a58e7848954bd4d63df3e3575f591e321b19a2aec8df9f"}, + {file = "aiohttp-3.10.11-cp39-cp39-win32.whl", hash = "sha256:22b7c540c55909140f63ab4f54ec2c20d2635c0289cdd8006da46f3327f971b9"}, + {file = "aiohttp-3.10.11-cp39-cp39-win_amd64.whl", hash = "sha256:7b26b1551e481012575dab8e3727b16fe7dd27eb2711d2e63ced7368756268fb"}, + {file = "aiohttp-3.10.11.tar.gz", hash = "sha256:9dc2b8f3dcab2e39e0fa309c8da50c3b55e6f34ab25f1a71d3288f24924d33a7"}, ] [package.dependencies] aiohappyeyeballs = ">=2.3.0" aiosignal = ">=1.1.2" -async-timeout = {version = ">=4.0,<5.0", markers = "python_version < \"3.11\""} +async-timeout = {version = ">=4.0,<6.0", markers = "python_version < \"3.11\""} attrs = ">=17.3.0" frozenlist = ">=1.1.1" multidict = ">=4.5,<7.0" -yarl = ">=1.0,<2.0" +yarl = ">=1.12.0,<2.0" [package.extras] speedups = ["Brotli", "aiodns (>=3.2.0)", "brotlicffi"] @@ -960,7 +960,7 @@ referencing = ">=0.31.0" [[package]] name = "kserve" -version = "0.14.0" +version = "0.15.0rc0" description = "KServe Python SDK" optional = false python-versions = ">=3.9,<3.13" @@ -1834,6 +1834,97 @@ files = [ [package.extras] twisted = ["twisted"] +[[package]] +name = "propcache" +version = "0.2.1" +description = "Accelerated property cache" +optional = false +python-versions = ">=3.9" +files = [ + {file = "propcache-0.2.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6b3f39a85d671436ee3d12c017f8fdea38509e4f25b28eb25877293c98c243f6"}, + {file = "propcache-0.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d51fbe4285d5db5d92a929e3e21536ea3dd43732c5b177c7ef03f918dff9f2"}, + {file = "propcache-0.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6445804cf4ec763dc70de65a3b0d9954e868609e83850a47ca4f0cb64bd79fea"}, + {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9479aa06a793c5aeba49ce5c5692ffb51fcd9a7016e017d555d5e2b0045d212"}, + {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9631c5e8b5b3a0fda99cb0d29c18133bca1e18aea9effe55adb3da1adef80d3"}, + {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3156628250f46a0895f1f36e1d4fbe062a1af8718ec3ebeb746f1d23f0c5dc4d"}, + {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b6fb63ae352e13748289f04f37868099e69dba4c2b3e271c46061e82c745634"}, + {file = "propcache-0.2.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:887d9b0a65404929641a9fabb6452b07fe4572b269d901d622d8a34a4e9043b2"}, + {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a96dc1fa45bd8c407a0af03b2d5218392729e1822b0c32e62c5bf7eeb5fb3958"}, + {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:a7e65eb5c003a303b94aa2c3852ef130230ec79e349632d030e9571b87c4698c"}, + {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:999779addc413181912e984b942fbcc951be1f5b3663cd80b2687758f434c583"}, + {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:19a0f89a7bb9d8048d9c4370c9c543c396e894c76be5525f5e1ad287f1750ddf"}, + {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:1ac2f5fe02fa75f56e1ad473f1175e11f475606ec9bd0be2e78e4734ad575034"}, + {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:574faa3b79e8ebac7cb1d7930f51184ba1ccf69adfdec53a12f319a06030a68b"}, + {file = "propcache-0.2.1-cp310-cp310-win32.whl", hash = "sha256:03ff9d3f665769b2a85e6157ac8b439644f2d7fd17615a82fa55739bc97863f4"}, + {file = "propcache-0.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:2d3af2e79991102678f53e0dbf4c35de99b6b8b58f29a27ca0325816364caaba"}, + {file = "propcache-0.2.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1ffc3cca89bb438fb9c95c13fc874012f7b9466b89328c3c8b1aa93cdcfadd16"}, + {file = "propcache-0.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f174bbd484294ed9fdf09437f889f95807e5f229d5d93588d34e92106fbf6717"}, + {file = "propcache-0.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:70693319e0b8fd35dd863e3e29513875eb15c51945bf32519ef52927ca883bc3"}, + {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b480c6a4e1138e1aa137c0079b9b6305ec6dcc1098a8ca5196283e8a49df95a9"}, + {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d27b84d5880f6d8aa9ae3edb253c59d9f6642ffbb2c889b78b60361eed449787"}, + {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:857112b22acd417c40fa4595db2fe28ab900c8c5fe4670c7989b1c0230955465"}, + {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf6c4150f8c0e32d241436526f3c3f9cbd34429492abddbada2ffcff506c51af"}, + {file = "propcache-0.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66d4cfda1d8ed687daa4bc0274fcfd5267873db9a5bc0418c2da19273040eeb7"}, + {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c2f992c07c0fca81655066705beae35fc95a2fa7366467366db627d9f2ee097f"}, + {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:4a571d97dbe66ef38e472703067021b1467025ec85707d57e78711c085984e54"}, + {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:bb6178c241278d5fe853b3de743087be7f5f4c6f7d6d22a3b524d323eecec505"}, + {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ad1af54a62ffe39cf34db1aa6ed1a1873bd548f6401db39d8e7cd060b9211f82"}, + {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e7048abd75fe40712005bcfc06bb44b9dfcd8e101dda2ecf2f5aa46115ad07ca"}, + {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:160291c60081f23ee43d44b08a7e5fb76681221a8e10b3139618c5a9a291b84e"}, + {file = "propcache-0.2.1-cp311-cp311-win32.whl", hash = "sha256:819ce3b883b7576ca28da3861c7e1a88afd08cc8c96908e08a3f4dd64a228034"}, + {file = "propcache-0.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:edc9fc7051e3350643ad929df55c451899bb9ae6d24998a949d2e4c87fb596d3"}, + {file = "propcache-0.2.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:081a430aa8d5e8876c6909b67bd2d937bfd531b0382d3fdedb82612c618bc41a"}, + {file = "propcache-0.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d2ccec9ac47cf4e04897619c0e0c1a48c54a71bdf045117d3a26f80d38ab1fb0"}, + {file = "propcache-0.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:14d86fe14b7e04fa306e0c43cdbeebe6b2c2156a0c9ce56b815faacc193e320d"}, + {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:049324ee97bb67285b49632132db351b41e77833678432be52bdd0289c0e05e4"}, + {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1cd9a1d071158de1cc1c71a26014dcdfa7dd3d5f4f88c298c7f90ad6f27bb46d"}, + {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98110aa363f1bb4c073e8dcfaefd3a5cea0f0834c2aab23dda657e4dab2f53b5"}, + {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:647894f5ae99c4cf6bb82a1bb3a796f6e06af3caa3d32e26d2350d0e3e3faf24"}, + {file = "propcache-0.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfd3223c15bebe26518d58ccf9a39b93948d3dcb3e57a20480dfdd315356baff"}, + {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d71264a80f3fcf512eb4f18f59423fe82d6e346ee97b90625f283df56aee103f"}, + {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:e73091191e4280403bde6c9a52a6999d69cdfde498f1fdf629105247599b57ec"}, + {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3935bfa5fede35fb202c4b569bb9c042f337ca4ff7bd540a0aa5e37131659348"}, + {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f508b0491767bb1f2b87fdfacaba5f7eddc2f867740ec69ece6d1946d29029a6"}, + {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:1672137af7c46662a1c2be1e8dc78cb6d224319aaa40271c9257d886be4363a6"}, + {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b74c261802d3d2b85c9df2dfb2fa81b6f90deeef63c2db9f0e029a3cac50b518"}, + {file = "propcache-0.2.1-cp312-cp312-win32.whl", hash = "sha256:d09c333d36c1409d56a9d29b3a1b800a42c76a57a5a8907eacdbce3f18768246"}, + {file = "propcache-0.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:c214999039d4f2a5b2073ac506bba279945233da8c786e490d411dfc30f855c1"}, + {file = "propcache-0.2.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aca405706e0b0a44cc6bfd41fbe89919a6a56999157f6de7e182a990c36e37bc"}, + {file = "propcache-0.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:12d1083f001ace206fe34b6bdc2cb94be66d57a850866f0b908972f90996b3e9"}, + {file = "propcache-0.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d93f3307ad32a27bda2e88ec81134b823c240aa3abb55821a8da553eed8d9439"}, + {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba278acf14471d36316159c94a802933d10b6a1e117b8554fe0d0d9b75c9d536"}, + {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4e6281aedfca15301c41f74d7005e6e3f4ca143584ba696ac69df4f02f40d629"}, + {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5b750a8e5a1262434fb1517ddf64b5de58327f1adc3524a5e44c2ca43305eb0b"}, + {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf72af5e0fb40e9babf594308911436c8efde3cb5e75b6f206c34ad18be5c052"}, + {file = "propcache-0.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b2d0a12018b04f4cb820781ec0dffb5f7c7c1d2a5cd22bff7fb055a2cb19ebce"}, + {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e800776a79a5aabdb17dcc2346a7d66d0777e942e4cd251defeb084762ecd17d"}, + {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:4160d9283bd382fa6c0c2b5e017acc95bc183570cd70968b9202ad6d8fc48dce"}, + {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:30b43e74f1359353341a7adb783c8f1b1c676367b011709f466f42fda2045e95"}, + {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:58791550b27d5488b1bb52bc96328456095d96206a250d28d874fafe11b3dfaf"}, + {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:0f022d381747f0dfe27e99d928e31bc51a18b65bb9e481ae0af1380a6725dd1f"}, + {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:297878dc9d0a334358f9b608b56d02e72899f3b8499fc6044133f0d319e2ec30"}, + {file = "propcache-0.2.1-cp313-cp313-win32.whl", hash = "sha256:ddfab44e4489bd79bda09d84c430677fc7f0a4939a73d2bba3073036f487a0a6"}, + {file = "propcache-0.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:556fc6c10989f19a179e4321e5d678db8eb2924131e64652a51fe83e4c3db0e1"}, + {file = "propcache-0.2.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:6a9a8c34fb7bb609419a211e59da8887eeca40d300b5ea8e56af98f6fbbb1541"}, + {file = "propcache-0.2.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ae1aa1cd222c6d205853b3013c69cd04515f9d6ab6de4b0603e2e1c33221303e"}, + {file = "propcache-0.2.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:accb6150ce61c9c4b7738d45550806aa2b71c7668c6942f17b0ac182b6142fd4"}, + {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5eee736daafa7af6d0a2dc15cc75e05c64f37fc37bafef2e00d77c14171c2097"}, + {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7a31fc1e1bd362874863fdeed71aed92d348f5336fd84f2197ba40c59f061bd"}, + {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba4cfa1052819d16699e1d55d18c92b6e094d4517c41dd231a8b9f87b6fa681"}, + {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f089118d584e859c62b3da0892b88a83d611c2033ac410e929cb6754eec0ed16"}, + {file = "propcache-0.2.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:781e65134efaf88feb447e8c97a51772aa75e48b794352f94cb7ea717dedda0d"}, + {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:31f5af773530fd3c658b32b6bdc2d0838543de70eb9a2156c03e410f7b0d3aae"}, + {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:a7a078f5d37bee6690959c813977da5291b24286e7b962e62a94cec31aa5188b"}, + {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:cea7daf9fc7ae6687cf1e2c049752f19f146fdc37c2cc376e7d0032cf4f25347"}, + {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:8b3489ff1ed1e8315674d0775dc7d2195fb13ca17b3808721b54dbe9fd020faf"}, + {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9403db39be1393618dd80c746cb22ccda168efce239c73af13c3763ef56ffc04"}, + {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5d97151bc92d2b2578ff7ce779cdb9174337390a535953cbb9452fb65164c587"}, + {file = "propcache-0.2.1-cp39-cp39-win32.whl", hash = "sha256:9caac6b54914bdf41bcc91e7eb9147d331d29235a7c967c150ef5df6464fd1bb"}, + {file = "propcache-0.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:92fc4500fcb33899b05ba73276dfb684a20d31caa567b7cb5252d48f896a91b1"}, + {file = "propcache-0.2.1-py3-none-any.whl", hash = "sha256:52277518d6aae65536e9cea52d4e7fd2f7a66f4aa2d30ed3f2fcea620ace3c54"}, + {file = "propcache-0.2.1.tar.gz", hash = "sha256:3f77ce728b19cb537714499928fe800c3dda29e8d9428778fc7c186da4c09a64"}, +] + [[package]] name = "proto-plus" version = "1.24.0" @@ -3101,106 +3192,99 @@ files = [ [[package]] name = "yarl" -version = "1.9.4" +version = "1.18.3" description = "Yet another URL library" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ - {file = "yarl-1.9.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a8c1df72eb746f4136fe9a2e72b0c9dc1da1cbd23b5372f94b5820ff8ae30e0e"}, - {file = "yarl-1.9.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a3a6ed1d525bfb91b3fc9b690c5a21bb52de28c018530ad85093cc488bee2dd2"}, - {file = "yarl-1.9.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c38c9ddb6103ceae4e4498f9c08fac9b590c5c71b0370f98714768e22ac6fa66"}, - {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d9e09c9d74f4566e905a0b8fa668c58109f7624db96a2171f21747abc7524234"}, - {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8477c1ee4bd47c57d49621a062121c3023609f7a13b8a46953eb6c9716ca392"}, - {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5ff2c858f5f6a42c2a8e751100f237c5e869cbde669a724f2062d4c4ef93551"}, - {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:357495293086c5b6d34ca9616a43d329317feab7917518bc97a08f9e55648455"}, - {file = "yarl-1.9.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54525ae423d7b7a8ee81ba189f131054defdb122cde31ff17477951464c1691c"}, - {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:801e9264d19643548651b9db361ce3287176671fb0117f96b5ac0ee1c3530d53"}, - {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e516dc8baf7b380e6c1c26792610230f37147bb754d6426462ab115a02944385"}, - {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:7d5aaac37d19b2904bb9dfe12cdb08c8443e7ba7d2852894ad448d4b8f442863"}, - {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:54beabb809ffcacbd9d28ac57b0db46e42a6e341a030293fb3185c409e626b8b"}, - {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bac8d525a8dbc2a1507ec731d2867025d11ceadcb4dd421423a5d42c56818541"}, - {file = "yarl-1.9.4-cp310-cp310-win32.whl", hash = "sha256:7855426dfbddac81896b6e533ebefc0af2f132d4a47340cee6d22cac7190022d"}, - {file = "yarl-1.9.4-cp310-cp310-win_amd64.whl", hash = "sha256:848cd2a1df56ddbffeb375535fb62c9d1645dde33ca4d51341378b3f5954429b"}, - {file = "yarl-1.9.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:35a2b9396879ce32754bd457d31a51ff0a9d426fd9e0e3c33394bf4b9036b099"}, - {file = "yarl-1.9.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c7d56b293cc071e82532f70adcbd8b61909eec973ae9d2d1f9b233f3d943f2c"}, - {file = "yarl-1.9.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d8a1c6c0be645c745a081c192e747c5de06e944a0d21245f4cf7c05e457c36e0"}, - {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b3c1ffe10069f655ea2d731808e76e0f452fc6c749bea04781daf18e6039525"}, - {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:549d19c84c55d11687ddbd47eeb348a89df9cb30e1993f1b128f4685cd0ebbf8"}, - {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7409f968456111140c1c95301cadf071bd30a81cbd7ab829169fb9e3d72eae9"}, - {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e23a6d84d9d1738dbc6e38167776107e63307dfc8ad108e580548d1f2c587f42"}, - {file = "yarl-1.9.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d8b889777de69897406c9fb0b76cdf2fd0f31267861ae7501d93003d55f54fbe"}, - {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:03caa9507d3d3c83bca08650678e25364e1843b484f19986a527630ca376ecce"}, - {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4e9035df8d0880b2f1c7f5031f33f69e071dfe72ee9310cfc76f7b605958ceb9"}, - {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:c0ec0ed476f77db9fb29bca17f0a8fcc7bc97ad4c6c1d8959c507decb22e8572"}, - {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:ee04010f26d5102399bd17f8df8bc38dc7ccd7701dc77f4a68c5b8d733406958"}, - {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:49a180c2e0743d5d6e0b4d1a9e5f633c62eca3f8a86ba5dd3c471060e352ca98"}, - {file = "yarl-1.9.4-cp311-cp311-win32.whl", hash = "sha256:81eb57278deb6098a5b62e88ad8281b2ba09f2f1147c4767522353eaa6260b31"}, - {file = "yarl-1.9.4-cp311-cp311-win_amd64.whl", hash = "sha256:d1d2532b340b692880261c15aee4dc94dd22ca5d61b9db9a8a361953d36410b1"}, - {file = "yarl-1.9.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0d2454f0aef65ea81037759be5ca9947539667eecebca092733b2eb43c965a81"}, - {file = "yarl-1.9.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:44d8ffbb9c06e5a7f529f38f53eda23e50d1ed33c6c869e01481d3fafa6b8142"}, - {file = "yarl-1.9.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:aaaea1e536f98754a6e5c56091baa1b6ce2f2700cc4a00b0d49eca8dea471074"}, - {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3777ce5536d17989c91696db1d459574e9a9bd37660ea7ee4d3344579bb6f129"}, - {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fc5fc1eeb029757349ad26bbc5880557389a03fa6ada41703db5e068881e5f2"}, - {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea65804b5dc88dacd4a40279af0cdadcfe74b3e5b4c897aa0d81cf86927fee78"}, - {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa102d6d280a5455ad6a0f9e6d769989638718e938a6a0a2ff3f4a7ff8c62cc4"}, - {file = "yarl-1.9.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09efe4615ada057ba2d30df871d2f668af661e971dfeedf0c159927d48bbeff0"}, - {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:008d3e808d03ef28542372d01057fd09168419cdc8f848efe2804f894ae03e51"}, - {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:6f5cb257bc2ec58f437da2b37a8cd48f666db96d47b8a3115c29f316313654ff"}, - {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:992f18e0ea248ee03b5a6e8b3b4738850ae7dbb172cc41c966462801cbf62cf7"}, - {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0e9d124c191d5b881060a9e5060627694c3bdd1fe24c5eecc8d5d7d0eb6faabc"}, - {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3986b6f41ad22988e53d5778f91855dc0399b043fc8946d4f2e68af22ee9ff10"}, - {file = "yarl-1.9.4-cp312-cp312-win32.whl", hash = "sha256:4b21516d181cd77ebd06ce160ef8cc2a5e9ad35fb1c5930882baff5ac865eee7"}, - {file = "yarl-1.9.4-cp312-cp312-win_amd64.whl", hash = "sha256:a9bd00dc3bc395a662900f33f74feb3e757429e545d831eef5bb280252631984"}, - {file = "yarl-1.9.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:63b20738b5aac74e239622d2fe30df4fca4942a86e31bf47a81a0e94c14df94f"}, - {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7d7f7de27b8944f1fee2c26a88b4dabc2409d2fea7a9ed3df79b67277644e17"}, - {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c74018551e31269d56fab81a728f683667e7c28c04e807ba08f8c9e3bba32f14"}, - {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ca06675212f94e7a610e85ca36948bb8fc023e458dd6c63ef71abfd482481aa5"}, - {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5aef935237d60a51a62b86249839b51345f47564208c6ee615ed2a40878dccdd"}, - {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b134fd795e2322b7684155b7855cc99409d10b2e408056db2b93b51a52accc7"}, - {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d25039a474c4c72a5ad4b52495056f843a7ff07b632c1b92ea9043a3d9950f6e"}, - {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f7d6b36dd2e029b6bcb8a13cf19664c7b8e19ab3a58e0fefbb5b8461447ed5ec"}, - {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:957b4774373cf6f709359e5c8c4a0af9f6d7875db657adb0feaf8d6cb3c3964c"}, - {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:d7eeb6d22331e2fd42fce928a81c697c9ee2d51400bd1a28803965883e13cead"}, - {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:6a962e04b8f91f8c4e5917e518d17958e3bdee71fd1d8b88cdce74dd0ebbf434"}, - {file = "yarl-1.9.4-cp37-cp37m-win32.whl", hash = "sha256:f3bc6af6e2b8f92eced34ef6a96ffb248e863af20ef4fde9448cc8c9b858b749"}, - {file = "yarl-1.9.4-cp37-cp37m-win_amd64.whl", hash = "sha256:ad4d7a90a92e528aadf4965d685c17dacff3df282db1121136c382dc0b6014d2"}, - {file = "yarl-1.9.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ec61d826d80fc293ed46c9dd26995921e3a82146feacd952ef0757236fc137be"}, - {file = "yarl-1.9.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8be9e837ea9113676e5754b43b940b50cce76d9ed7d2461df1af39a8ee674d9f"}, - {file = "yarl-1.9.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:bef596fdaa8f26e3d66af846bbe77057237cb6e8efff8cd7cc8dff9a62278bbf"}, - {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d47552b6e52c3319fede1b60b3de120fe83bde9b7bddad11a69fb0af7db32f1"}, - {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84fc30f71689d7fc9168b92788abc977dc8cefa806909565fc2951d02f6b7d57"}, - {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4aa9741085f635934f3a2583e16fcf62ba835719a8b2b28fb2917bb0537c1dfa"}, - {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:206a55215e6d05dbc6c98ce598a59e6fbd0c493e2de4ea6cc2f4934d5a18d130"}, - {file = "yarl-1.9.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07574b007ee20e5c375a8fe4a0789fad26db905f9813be0f9fef5a68080de559"}, - {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5a2e2433eb9344a163aced6a5f6c9222c0786e5a9e9cac2c89f0b28433f56e23"}, - {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6ad6d10ed9b67a382b45f29ea028f92d25bc0bc1daf6c5b801b90b5aa70fb9ec"}, - {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:6fe79f998a4052d79e1c30eeb7d6c1c1056ad33300f682465e1b4e9b5a188b78"}, - {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a825ec844298c791fd28ed14ed1bffc56a98d15b8c58a20e0e08c1f5f2bea1be"}, - {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8619d6915b3b0b34420cf9b2bb6d81ef59d984cb0fde7544e9ece32b4b3043c3"}, - {file = "yarl-1.9.4-cp38-cp38-win32.whl", hash = "sha256:686a0c2f85f83463272ddffd4deb5e591c98aac1897d65e92319f729c320eece"}, - {file = "yarl-1.9.4-cp38-cp38-win_amd64.whl", hash = "sha256:a00862fb23195b6b8322f7d781b0dc1d82cb3bcac346d1e38689370cc1cc398b"}, - {file = "yarl-1.9.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:604f31d97fa493083ea21bd9b92c419012531c4e17ea6da0f65cacdcf5d0bd27"}, - {file = "yarl-1.9.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8a854227cf581330ffa2c4824d96e52ee621dd571078a252c25e3a3b3d94a1b1"}, - {file = "yarl-1.9.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ba6f52cbc7809cd8d74604cce9c14868306ae4aa0282016b641c661f981a6e91"}, - {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6327976c7c2f4ee6816eff196e25385ccc02cb81427952414a64811037bbc8b"}, - {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8397a3817d7dcdd14bb266283cd1d6fc7264a48c186b986f32e86d86d35fbac5"}, - {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e0381b4ce23ff92f8170080c97678040fc5b08da85e9e292292aba67fdac6c34"}, - {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23d32a2594cb5d565d358a92e151315d1b2268bc10f4610d098f96b147370136"}, - {file = "yarl-1.9.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ddb2a5c08a4eaaba605340fdee8fc08e406c56617566d9643ad8bf6852778fc7"}, - {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:26a1dc6285e03f3cc9e839a2da83bcbf31dcb0d004c72d0730e755b33466c30e"}, - {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:18580f672e44ce1238b82f7fb87d727c4a131f3a9d33a5e0e82b793362bf18b4"}, - {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:29e0f83f37610f173eb7e7b5562dd71467993495e568e708d99e9d1944f561ec"}, - {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:1f23e4fe1e8794f74b6027d7cf19dc25f8b63af1483d91d595d4a07eca1fb26c"}, - {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:db8e58b9d79200c76956cefd14d5c90af54416ff5353c5bfd7cbe58818e26ef0"}, - {file = "yarl-1.9.4-cp39-cp39-win32.whl", hash = "sha256:c7224cab95645c7ab53791022ae77a4509472613e839dab722a72abe5a684575"}, - {file = "yarl-1.9.4-cp39-cp39-win_amd64.whl", hash = "sha256:824d6c50492add5da9374875ce72db7a0733b29c2394890aef23d533106e2b15"}, - {file = "yarl-1.9.4-py3-none-any.whl", hash = "sha256:928cecb0ef9d5a7946eb6ff58417ad2fe9375762382f1bf5c55e61645f2c43ad"}, - {file = "yarl-1.9.4.tar.gz", hash = "sha256:566db86717cf8080b99b58b083b773a908ae40f06681e87e589a976faf8246bf"}, + {file = "yarl-1.18.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7df647e8edd71f000a5208fe6ff8c382a1de8edfbccdbbfe649d263de07d8c34"}, + {file = "yarl-1.18.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c69697d3adff5aa4f874b19c0e4ed65180ceed6318ec856ebc423aa5850d84f7"}, + {file = "yarl-1.18.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:602d98f2c2d929f8e697ed274fbadc09902c4025c5a9963bf4e9edfc3ab6f7ed"}, + {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c654d5207c78e0bd6d749f6dae1dcbbfde3403ad3a4b11f3c5544d9906969dde"}, + {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5094d9206c64181d0f6e76ebd8fb2f8fe274950a63890ee9e0ebfd58bf9d787b"}, + {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35098b24e0327fc4ebdc8ffe336cee0a87a700c24ffed13161af80124b7dc8e5"}, + {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3236da9272872443f81fedc389bace88408f64f89f75d1bdb2256069a8730ccc"}, + {file = "yarl-1.18.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2c08cc9b16f4f4bc522771d96734c7901e7ebef70c6c5c35dd0f10845270bcd"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:80316a8bd5109320d38eef8833ccf5f89608c9107d02d2a7f985f98ed6876990"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:c1e1cc06da1491e6734f0ea1e6294ce00792193c463350626571c287c9a704db"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fea09ca13323376a2fdfb353a5fa2e59f90cd18d7ca4eaa1fd31f0a8b4f91e62"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e3b9fd71836999aad54084906f8663dffcd2a7fb5cdafd6c37713b2e72be1760"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:757e81cae69244257d125ff31663249b3013b5dc0a8520d73694aed497fb195b"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b1771de9944d875f1b98a745bc547e684b863abf8f8287da8466cf470ef52690"}, + {file = "yarl-1.18.3-cp310-cp310-win32.whl", hash = "sha256:8874027a53e3aea659a6d62751800cf6e63314c160fd607489ba5c2edd753cf6"}, + {file = "yarl-1.18.3-cp310-cp310-win_amd64.whl", hash = "sha256:93b2e109287f93db79210f86deb6b9bbb81ac32fc97236b16f7433db7fc437d8"}, + {file = "yarl-1.18.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8503ad47387b8ebd39cbbbdf0bf113e17330ffd339ba1144074da24c545f0069"}, + {file = "yarl-1.18.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:02ddb6756f8f4517a2d5e99d8b2f272488e18dd0bfbc802f31c16c6c20f22193"}, + {file = "yarl-1.18.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:67a283dd2882ac98cc6318384f565bffc751ab564605959df4752d42483ad889"}, + {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d980e0325b6eddc81331d3f4551e2a333999fb176fd153e075c6d1c2530aa8a8"}, + {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b643562c12680b01e17239be267bc306bbc6aac1f34f6444d1bded0c5ce438ca"}, + {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c017a3b6df3a1bd45b9fa49a0f54005e53fbcad16633870104b66fa1a30a29d8"}, + {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75674776d96d7b851b6498f17824ba17849d790a44d282929c42dbb77d4f17ae"}, + {file = "yarl-1.18.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ccaa3a4b521b780a7e771cc336a2dba389a0861592bbce09a476190bb0c8b4b3"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2d06d3005e668744e11ed80812e61efd77d70bb7f03e33c1598c301eea20efbb"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:9d41beda9dc97ca9ab0b9888cb71f7539124bc05df02c0cff6e5acc5a19dcc6e"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ba23302c0c61a9999784e73809427c9dbedd79f66a13d84ad1b1943802eaaf59"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:6748dbf9bfa5ba1afcc7556b71cda0d7ce5f24768043a02a58846e4a443d808d"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0b0cad37311123211dc91eadcb322ef4d4a66008d3e1bdc404808992260e1a0e"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0fb2171a4486bb075316ee754c6d8382ea6eb8b399d4ec62fde2b591f879778a"}, + {file = "yarl-1.18.3-cp311-cp311-win32.whl", hash = "sha256:61b1a825a13bef4a5f10b1885245377d3cd0bf87cba068e1d9a88c2ae36880e1"}, + {file = "yarl-1.18.3-cp311-cp311-win_amd64.whl", hash = "sha256:b9d60031cf568c627d028239693fd718025719c02c9f55df0a53e587aab951b5"}, + {file = "yarl-1.18.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1dd4bdd05407ced96fed3d7f25dbbf88d2ffb045a0db60dbc247f5b3c5c25d50"}, + {file = "yarl-1.18.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7c33dd1931a95e5d9a772d0ac5e44cac8957eaf58e3c8da8c1414de7dd27c576"}, + {file = "yarl-1.18.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:25b411eddcfd56a2f0cd6a384e9f4f7aa3efee14b188de13048c25b5e91f1640"}, + {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:436c4fc0a4d66b2badc6c5fc5ef4e47bb10e4fd9bf0c79524ac719a01f3607c2"}, + {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e35ef8683211db69ffe129a25d5634319a677570ab6b2eba4afa860f54eeaf75"}, + {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:84b2deecba4a3f1a398df819151eb72d29bfeb3b69abb145a00ddc8d30094512"}, + {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00e5a1fea0fd4f5bfa7440a47eff01d9822a65b4488f7cff83155a0f31a2ecba"}, + {file = "yarl-1.18.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d0e883008013c0e4aef84dcfe2a0b172c4d23c2669412cf5b3371003941f72bb"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5a3f356548e34a70b0172d8890006c37be92995f62d95a07b4a42e90fba54272"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ccd17349166b1bee6e529b4add61727d3f55edb7babbe4069b5764c9587a8cc6"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b958ddd075ddba5b09bb0be8a6d9906d2ce933aee81100db289badbeb966f54e"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c7d79f7d9aabd6011004e33b22bc13056a3e3fb54794d138af57f5ee9d9032cb"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:4891ed92157e5430874dad17b15eb1fda57627710756c27422200c52d8a4e393"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ce1af883b94304f493698b00d0f006d56aea98aeb49d75ec7d98cd4a777e9285"}, + {file = "yarl-1.18.3-cp312-cp312-win32.whl", hash = "sha256:f91c4803173928a25e1a55b943c81f55b8872f0018be83e3ad4938adffb77dd2"}, + {file = "yarl-1.18.3-cp312-cp312-win_amd64.whl", hash = "sha256:7e2ee16578af3b52ac2f334c3b1f92262f47e02cc6193c598502bd46f5cd1477"}, + {file = "yarl-1.18.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:90adb47ad432332d4f0bc28f83a5963f426ce9a1a8809f5e584e704b82685dcb"}, + {file = "yarl-1.18.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:913829534200eb0f789d45349e55203a091f45c37a2674678744ae52fae23efa"}, + {file = "yarl-1.18.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ef9f7768395923c3039055c14334ba4d926f3baf7b776c923c93d80195624782"}, + {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88a19f62ff30117e706ebc9090b8ecc79aeb77d0b1f5ec10d2d27a12bc9f66d0"}, + {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e17c9361d46a4d5addf777c6dd5eab0715a7684c2f11b88c67ac37edfba6c482"}, + {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a74a13a4c857a84a845505fd2d68e54826a2cd01935a96efb1e9d86c728e186"}, + {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41f7ce59d6ee7741af71d82020346af364949314ed3d87553763a2df1829cc58"}, + {file = "yarl-1.18.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f52a265001d830bc425f82ca9eabda94a64a4d753b07d623a9f2863fde532b53"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:82123d0c954dc58db301f5021a01854a85bf1f3bb7d12ae0c01afc414a882ca2"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:2ec9bbba33b2d00999af4631a3397d1fd78290c48e2a3e52d8dd72db3a067ac8"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:fbd6748e8ab9b41171bb95c6142faf068f5ef1511935a0aa07025438dd9a9bc1"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:877d209b6aebeb5b16c42cbb377f5f94d9e556626b1bfff66d7b0d115be88d0a"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b464c4ab4bfcb41e3bfd3f1c26600d038376c2de3297760dfe064d2cb7ea8e10"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8d39d351e7faf01483cc7ff7c0213c412e38e5a340238826be7e0e4da450fdc8"}, + {file = "yarl-1.18.3-cp313-cp313-win32.whl", hash = "sha256:61ee62ead9b68b9123ec24bc866cbef297dd266175d53296e2db5e7f797f902d"}, + {file = "yarl-1.18.3-cp313-cp313-win_amd64.whl", hash = "sha256:578e281c393af575879990861823ef19d66e2b1d0098414855dd367e234f5b3c"}, + {file = "yarl-1.18.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:61e5e68cb65ac8f547f6b5ef933f510134a6bf31bb178be428994b0cb46c2a04"}, + {file = "yarl-1.18.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fe57328fbc1bfd0bd0514470ac692630f3901c0ee39052ae47acd1d90a436719"}, + {file = "yarl-1.18.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a440a2a624683108a1b454705ecd7afc1c3438a08e890a1513d468671d90a04e"}, + {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09c7907c8548bcd6ab860e5f513e727c53b4a714f459b084f6580b49fa1b9cee"}, + {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b4f6450109834af88cb4cc5ecddfc5380ebb9c228695afc11915a0bf82116789"}, + {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9ca04806f3be0ac6d558fffc2fdf8fcef767e0489d2684a21912cc4ed0cd1b8"}, + {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77a6e85b90a7641d2e07184df5557132a337f136250caafc9ccaa4a2a998ca2c"}, + {file = "yarl-1.18.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6333c5a377c8e2f5fae35e7b8f145c617b02c939d04110c76f29ee3676b5f9a5"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0b3c92fa08759dbf12b3a59579a4096ba9af8dd344d9a813fc7f5070d86bbab1"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:4ac515b860c36becb81bb84b667466885096b5fc85596948548b667da3bf9f24"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:045b8482ce9483ada4f3f23b3774f4e1bf4f23a2d5c912ed5170f68efb053318"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:a4bb030cf46a434ec0225bddbebd4b89e6471814ca851abb8696170adb163985"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:54d6921f07555713b9300bee9c50fb46e57e2e639027089b1d795ecd9f7fa910"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1d407181cfa6e70077df3377938c08012d18893f9f20e92f7d2f314a437c30b1"}, + {file = "yarl-1.18.3-cp39-cp39-win32.whl", hash = "sha256:ac36703a585e0929b032fbaab0707b75dc12703766d0b53486eabd5139ebadd5"}, + {file = "yarl-1.18.3-cp39-cp39-win_amd64.whl", hash = "sha256:ba87babd629f8af77f557b61e49e7c7cac36f22f871156b91e10a6e9d4f829e9"}, + {file = "yarl-1.18.3-py3-none-any.whl", hash = "sha256:b57f4f58099328dfb26c6a771d09fb20dbbae81d20cfb66141251ea063bd101b"}, + {file = "yarl-1.18.3.tar.gz", hash = "sha256:ac1801c45cbf77b6c99242eeff4fffb5e4e73a800b5c4ad4fc0be5def634d2e1"}, ] [package.dependencies] idna = ">=2.0" multidict = ">=4.0" +propcache = ">=0.2.0" [metadata] lock-version = "2.0" diff --git a/python/custom_model/pyproject.toml b/python/custom_model/pyproject.toml index 35e90691887..9520efc375a 100644 --- a/python/custom_model/pyproject.toml +++ b/python/custom_model/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "custom-model" -version = "0.14.0" +version = "0.15.0rc0" description = "Custom model implementation. Not intended for use outside KServe Frameworks Images." authors = ["The KServe Authors"] license = "https://github.com/kserve/kserve/blob/master/LICENSE" diff --git a/python/custom_tokenizer/poetry.lock b/python/custom_tokenizer/poetry.lock index 5a47db957e2..fe294a987b1 100644 --- a/python/custom_tokenizer/poetry.lock +++ b/python/custom_tokenizer/poetry.lock @@ -518,7 +518,7 @@ files = [ [[package]] name = "kserve" -version = "0.14.0" +version = "0.15.0rc0" description = "KServe Python SDK" optional = false python-versions = ">=3.9,<3.13" diff --git a/python/custom_tokenizer/pyproject.toml b/python/custom_tokenizer/pyproject.toml index b3be1ae1d77..0bb079965cd 100644 --- a/python/custom_tokenizer/pyproject.toml +++ b/python/custom_tokenizer/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "custom_tokenizer" -version = "0.14.0" +version = "0.15.0rc0" description = "Custom Tokenizer Examples. Not intended for use outside KServe Frameworks Images." authors = ["Dan Sun "] license = "https://github.com/kserve/kserve/blob/master/LICENSE" diff --git a/python/custom_transformer/poetry.lock b/python/custom_transformer/poetry.lock index 27a550778ad..9b2a6f95368 100644 --- a/python/custom_transformer/poetry.lock +++ b/python/custom_transformer/poetry.lock @@ -604,7 +604,7 @@ i18n = ["Babel (>=2.7)"] [[package]] name = "kserve" -version = "0.14.0" +version = "0.15.0rc0" description = "KServe Python SDK" optional = false python-versions = ">=3.9,<3.13" diff --git a/python/custom_transformer/pyproject.toml b/python/custom_transformer/pyproject.toml index da602893d36..fbfd38dac9c 100644 --- a/python/custom_transformer/pyproject.toml +++ b/python/custom_transformer/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "custom_transformer" -version = "0.14.0" +version = "0.15.0rc0" description = "Custom Transformer Examples. Not intended for use outside KServe Frameworks Images." authors = ["Dan Sun "] license = "https://github.com/kserve/kserve/blob/master/LICENSE" diff --git a/python/huggingface_server.Dockerfile b/python/huggingface_server.Dockerfile index 898dbd573f8..3a1d42b7332 100644 --- a/python/huggingface_server.Dockerfile +++ b/python/huggingface_server.Dockerfile @@ -9,7 +9,7 @@ ARG POETRY_HOME=/opt/poetry ARG POETRY_VERSION=1.8.3 # Install vllm -ARG VLLM_VERSION=0.6.3.post1 +ARG VLLM_VERSION=0.7.2 RUN apt-get update && apt-get upgrade -y && apt-get install gcc python3.10-venv python3-dev -y && apt-get clean && \ rm -rf /var/lib/apt/lists/* @@ -32,7 +32,7 @@ RUN cd huggingfaceserver && poetry install --no-root --no-interaction --no-cache COPY huggingfaceserver huggingfaceserver RUN cd huggingfaceserver && poetry install --no-interaction --no-cache -RUN pip3 install vllm==${VLLM_VERSION} +RUN pip install --upgrade pip && pip install vllm==${VLLM_VERSION} FROM nvidia/cuda:12.4.1-runtime-ubuntu22.04 AS prod diff --git a/python/huggingface_server_cpu_openvino.Dockerfile b/python/huggingface_server_cpu_openvino.Dockerfile index 4230ad9e4aa..0882fe10a7e 100644 --- a/python/huggingface_server_cpu_openvino.Dockerfile +++ b/python/huggingface_server_cpu_openvino.Dockerfile @@ -9,7 +9,7 @@ ARG POETRY_HOME=/opt/poetry ARG POETRY_VERSION=1.8.3 # Install vllm -ARG VLLM_VERSION=v0.6.3.post1 +ARG VLLM_VERSION=v0.7.2 RUN apt-get update -y && apt-get install -y \ gcc python3.10-venv python3-dev python3-pip \ @@ -43,7 +43,6 @@ RUN cd huggingfaceserver && poetry install --no-interaction --no-cache WORKDIR /vllm RUN git clone --branch $VLLM_VERSION --depth 1 https://github.com/vllm-project/vllm.git . && \ pip install --upgrade pip && \ - sed -i 's/@main//' ./requirements-openvino.txt && \ pip install -r requirements-build.txt --extra-index-url https://download.pytorch.org/whl/cpu && \ PIP_EXTRA_INDEX_URL="https://download.pytorch.org/whl/cpu" VLLM_TARGET_DEVICE="openvino" python -m pip install -v . diff --git a/python/huggingfaceserver/health_check.py b/python/huggingfaceserver/health_check.py index 9c95c6c2e4e..27448dbad23 100644 --- a/python/huggingfaceserver/health_check.py +++ b/python/huggingfaceserver/health_check.py @@ -13,41 +13,88 @@ # limitations under the License. import argparse +import sys +import time +import os + import ray import requests -import sys from kserve.logging import logger -def initialize_ray_cluster(): - if not ray.is_initialized(): # Check if Ray is already initialized - ray.init(address="auto") - return "Ray initialized" - else: - return "Ray already initialized" +def initialize_ray_cluster(ray_address="auto"): + try: + if ray.is_initialized(): # Check if Ray is already initialized + return + else: + ray.init(address=ray_address) + return + except Exception as e: + logger.error(f"Failed to initialize Ray: {e}") + sys.exit(1) + + +def show_ray_cluster_status(ray_address="auto"): + initialize_ray_cluster(ray_address) + try: + resources = ray.cluster_resources() + logger.info("Cluster resources:", resources) + except Exception as e: + logger.error(f"Error getting Ray nodes status: {e}") -def verify_status(result): - if result == "Healthy": +def verify_status(result, probe_type): + if result in ("Healthy", True): + logger.info(f"{probe_type} Probe: Healthy") sys.exit(0) else: + logger.error(f"{probe_type} Probe: Unhealthy") sys.exit(1) # Function for startup check using Ray API -def check_startup(): - try: - initialize_ray_cluster() - logger.info("Ray is accessible") +def check_registered_node_and_runtime_health( + pipeline_parallel_size, health_check_url, ray_address="auto" +): + initialize_ray_cluster(ray_address) + # Check if the registered nodes count matches PIPELINE_PARALLEL_SIZE + check_registered_nodes_status = check_registered_nodes( + pipeline_parallel_size, ray_address + ) + + # Check if server health return 200 + check_runtime_health_status = check_runtime_health(health_check_url) + logger.debug( + f"check_registered_nodes_status: {check_registered_nodes_status},check_runtime_health_status: {check_runtime_health_status}" + ) + + if ( + check_registered_nodes_status == "Healthy" + and check_runtime_health_status == "Healthy" + ): return "Healthy" - except Exception as e: - logger.error(f"Ray is NOT accessible: {e}") + else: + return "Unhealthy" + + +def check_registered_node_and_runtime_models( + pipeline_parallel_size, runtime_url, ray_address, isvc_name +): + result1 = check_registered_nodes(pipeline_parallel_size, ray_address) + result2 = check_runtime_models(runtime_url, isvc_name) + logger.debug(f"check_registered_nodes: {result1}, check_runtime_models: {result2}") + # Check both results + if (result1 == "Healthy" or result1 is True) and ( + result2 == "Healthy" or result2 is True + ): + return "Healthy" + else: return "Unhealthy" -def check_gpu_usage(probe_type): +def check_gpu_usage(ray_address="auto"): try: - initialize_ray_cluster() + initialize_ray_cluster(ray_address) nodes = ray.nodes() total_gpus = 0 used_gpus = 0 @@ -58,130 +105,200 @@ def check_gpu_usage(probe_type): # Determine health status based on GPU usage if total_gpus == 0 or total_gpus != used_gpus: logger.error( - f"{probe_type}: Unhealthy - Used: {used_gpus}, Total: {total_gpus}" + f"GPU Usage: Unhealthy - Used: {used_gpus}, Total: {total_gpus}" ) return "Unhealthy" else: - logger.info( - f"{probe_type}: Healthy - Used: {used_gpus}, Total: {total_gpus}" - ) + logger.info(f"GPU Usage: Healthy - Used: {used_gpus}, Total: {total_gpus}") return "Healthy" except Exception as e: - logger.error(f"{probe_type}: Error - Failed to get GPU status: {str(e)}") + logger.error(f"GPU Usage: Error - Failed to get GPU status: {str(e)}") return "Unhealthy" -def check_registered_nodes(pipeline_parallel_size): +def check_registered_nodes( + pipeline_parallel_size, ray_address="auto", retries=0, interval=2 +): try: - initialize_ray_cluster() - # Get list of alive nodes - nodes = ray.nodes() - registered_node_count = len([node for node in nodes if node["Alive"]]) + pipeline_parallel_size = int(pipeline_parallel_size) # Ensure it's an integer + except ValueError: + logger.error(f"Invalid pipeline_parallel_size: {pipeline_parallel_size}") + return "Unhealthy" - # Check if the registered nodes count matches PIPELINE_PARALLEL_SIZE - if registered_node_count != int(pipeline_parallel_size): - logger.error( - f"Unhealthy - Registered nodes count ({registered_node_count}) does not match PIPELINE_PARALLEL_SIZE ({pipeline_parallel_size})." + for attempt in range(1, retries + 2): + try: + initialize_ray_cluster(ray_address) + # Get list of alive nodes + nodes = ray.nodes() + registered_node_count = len([node for node in nodes if node["Alive"]]) + logger.debug( + f"registered_node_count: {registered_node_count}, pipeline_parallel_size: {pipeline_parallel_size}" ) - return "Unhealthy" - else: - logger.info( - f"Healthy - Registered nodes count ({registered_node_count}) match PIPELINE_PARALLEL_SIZE ({pipeline_parallel_size})." - ) - return "Healthy" - except Exception as e: - logger.error(f"Error checking registered nodes: {str(e)}") - return "Unhealthy" + # Check if the registered nodes count matches PIPELINE_PARALLEL_SIZE + if not registered_node_count >= pipeline_parallel_size: + logger.error( + f"Waiting - Registered nodes count ({registered_node_count}) does not match PIPELINE_PARALLEL_SIZE ({pipeline_parallel_size})." + ) + else: + logger.info( + f"Success - Registered nodes count ({registered_node_count}) matches PIPELINE_PARALLEL_SIZE ({pipeline_parallel_size})." + ) + return "Healthy" + except Exception as e: + logger.error(f"Error checking registered nodes: {str(e)}") -def check_runtime_health(health_check_url): - # Check if Huggingface server health - try: - response = requests.get(health_check_url, timeout=5) - if response.status_code != 200: - logger.error(f"Hugging Face server({health_check_url}) is not reachable.") - return "Unhealthy" - else: - logger.info(f"Hugging Face server({health_check_url}) is reachable.") - return "Healthy" - except requests.RequestException: - logger.error(f"Hugging Face server({health_check_url}) is not reachable.") - return "Unhealthy" + if attempt < retries: + time.sleep(interval) + logger.error( + "Max retries reached. Node count did not match the expected pipeline parallel size." + ) + return "Unhealthy" -def check_readiness(pipeline_parallel_size, health_check_url): - # Check if the registered nodes count matches PIPELINE_PARALLEL_SIZE - check_registered_nodes_status = check_registered_nodes(pipeline_parallel_size) +def check_runtime_health(health_check_url, retries=1, interval=1): + # Check if runtime server health + for attempt in range(1, retries + 2): + try: + response = requests.get(health_check_url, timeout=5) + if response.status_code != 200: + logger.error(f"Server({health_check_url}) did not return 200 code.") + else: + logger.info(f"Server({health_check_url}) is reachable.") + return "Healthy" + except requests.RequestException: + logger.error(f"Server({health_check_url}) is not reachable.") - # Check GPU usage - check_gpu_usage_status = check_gpu_usage("Readiness Probe") + if attempt < retries: + time.sleep(interval) - # Check if Huggingface server health - check_runtime_health_status = check_runtime_health(health_check_url) + return "Unhealthy" - if ( - check_registered_nodes_status == "Healthy" - and check_gpu_usage_status == "Healthy" - and check_runtime_health_status == "Healthy" - ): - logger.info("Readiness Probe: Healthy") - return "Healthy" - else: - logger.error("Readiness Probe: Unhealthy") - return "Unhealthy" + +def check_runtime_models(health_check_url, isvc_name, retries=1, interval=1): + # Check if runtime server health + for attempt in range(1, retries + 2): + try: + response = requests.get(health_check_url, timeout=5) + if isvc_name in response.text: + logger.info(f"Model({isvc_name}) is Ready to serve") + return True + else: + logger.error(f"Model({isvc_name}) is Not ready to serve") + except requests.RequestException: + logger.error(f"Server({health_check_url}) is not reachable.") + + if attempt < retries: + time.sleep(interval) + + return False # Main logic to handle CLI commands using argparse def main(): + # Get default values from environment variables if available + default_ray_address = os.getenv("RAY_ADDRESS", "auto") + default_isvc_name = os.getenv("ISVC_NAME", "") + default_pipeline_parallel_size = int( + os.getenv("PIPELINE_PARALLEL_SIZE", 2) + ) # Default to 2 if not set + # Create the top-level parser - parser = argparse.ArgumentParser(description="Perform multinode health checks.") + parser = argparse.ArgumentParser(description="Perform multinode operations") + parser.add_argument( + "--ray_address", default=default_ray_address, help="Ray head address" + ) + parser.add_argument( + "--isvc_name", default=default_isvc_name, help="InferenceService name" + ) - # Define subcommands (readiness, startup, gpu_usage, registered_nodes) + # Define subcommands (readiness,,liveness, startup, gpu_usage, registered_nodes) subparsers = parser.add_subparsers(dest="command", help="Sub-command to run") - # Readiness subcommand - readiness_parser = subparsers.add_parser( - "readiness", help="Perform readiness check" + # Check runtime health subcommand + runtime_health_parser = subparsers.add_parser( + "runtime_health", help="Check runtime health" ) - readiness_parser.add_argument( - "pipeline_parallel_size", type=int, help="Pipeline parallel size" + runtime_health_parser.add_argument("--health_check_url", help="Health check URL") + runtime_health_parser.add_argument("--probe_name", help="Probe name") + + # Check if registered node is the same as pipelineParalleSize + reigstered_node_parser = subparsers.add_parser( + "registered_nodes", + help="Check if registered nodes are the same as pipeline parallel size", ) - readiness_parser.add_argument("health_check_url", help="Health check URL") + reigstered_node_parser.add_argument( + "--pipeline_parallel_size", + type=int, + default=default_pipeline_parallel_size, + help="Pipeline parallel size", + ) + reigstered_node_parser.add_argument( + "--retries", type=int, default=0, help="Pipeline parallel size" + ) + reigstered_node_parser.add_argument("--probe_name", help="Probe name") - # Liveness subcommand - subparsers.add_parser("liveness", help="Perform liveness check") - # Startup subcommand - subparsers.add_parser("startup", help="Perform startup check") - # GPU Usage subcommand - subparsers.add_parser("gpu_usage", help="Check GPU usage") + # Check if registered node is the same as pipelineParalleSize/ runtime health subcommand + registered_node_and_runtime_health_parser = subparsers.add_parser( + "registered_node_and_runtime_health", + help="Check node counts and runtime health", + ) + registered_node_and_runtime_health_parser.add_argument( + "--pipeline_parallel_size", + type=int, + default=default_pipeline_parallel_size, + help="Pipeline parallel size", + ) + registered_node_and_runtime_health_parser.add_argument( + "--health_check_url", help="Health check URL" + ) + registered_node_and_runtime_health_parser.add_argument( + "--probe_name", help="Probe name" + ) - # Registered Nodes subcommand - registered_nodes_parser = subparsers.add_parser( - "registered_nodes", help="Check registered nodes" + # Check if registered node is the same as pipelineParalleSize/ model loaded on runtime subcommand + registered_node_and_runtime_models_parser = subparsers.add_parser( + "registered_node_and_runtime_models", + help="Check node counts and loaded model on runtime", + ) + registered_node_and_runtime_models_parser.add_argument( + "--pipeline_parallel_size", + type=int, + default=default_pipeline_parallel_size, + help="Pipeline parallel size", + ) + registered_node_and_runtime_models_parser.add_argument( + "--runtime_url", help="Health check URL" ) - registered_nodes_parser.add_argument( - "pipeline_parallel_size", type=int, help="Pipeline parallel size" + registered_node_and_runtime_models_parser.add_argument( + "--probe_name", help="Probe name" ) # Parse the arguments args = parser.parse_args() # Route to appropriate function based on command using if-elif-else - if args.command == "readiness": - result = check_readiness(args.pipeline_parallel_size, args.health_check_url) - verify_status(result) - elif args.command == "startup": - result = check_startup() - verify_status(result) - elif args.command == "liveness": - result = check_gpu_usage("Liveness Probe") - verify_status(result) - elif args.command == "gpu_usage": - result = check_gpu_usage("GPU Usage") - verify_status(result) + if args.command == "runtime_health": + result = check_runtime_health(args.health_check_url) + verify_status(result, args.probe_name) + elif args.command == "registered_node_and_runtime_health": + result = check_registered_node_and_runtime_health( + args.pipeline_parallel_size, args.health_check_url, args.ray_address + ) + verify_status(result, args.probe_name) + elif args.command == "registered_node_and_runtime_models": + result = check_registered_node_and_runtime_models( + args.pipeline_parallel_size, + args.runtime_url, + args.ray_address, + args.isvc_name, + ) + verify_status(result, args.probe_name) elif args.command == "registered_nodes": - result = check_registered_nodes(args.pipeline_parallel_size) - verify_status(result) + result = check_registered_nodes( + args.pipeline_parallel_size, args.ray_address, args.retries + ) + verify_status(result, args.probe_name) else: parser.print_help() diff --git a/python/huggingfaceserver/huggingfaceserver/__main__.py b/python/huggingfaceserver/huggingfaceserver/__main__.py index 1e334187286..864832192c1 100644 --- a/python/huggingfaceserver/huggingfaceserver/__main__.py +++ b/python/huggingfaceserver/huggingfaceserver/__main__.py @@ -14,7 +14,7 @@ import argparse from pathlib import Path -from typing import cast +from typing import cast, Union import torch import kserve @@ -50,6 +50,26 @@ def list_of_strings(arg): return arg.split(",") +def get_model_id_or_path(args: argparse.Namespace) -> Union[str, Path]: + # If --model_id is specified then pass model_id to HF API, otherwise load the model from /mnt/models + if args.model_id: + return cast(str, args.model_id) + return Path(Storage.download(args.model_dir)) + + +def is_vllm_backend_enabled( + args: argparse.Namespace, model_id_or_path: Union[str, Path] +) -> bool: + return ( + (args.backend == Backend.vllm or args.backend == Backend.auto) + and vllm_available() + and infer_vllm_supported_from_model_architecture( + model_id_or_path, + trust_remote_code=args.trust_remote_code, + ) + ) + + try: from vllm.utils import FlexibleArgumentParser @@ -110,7 +130,6 @@ def list_of_strings(arg): default=None, help="the tensor input names passed to the model", ) -parser.add_argument("--task", required=False, help="The ML task name") available_backends = ", ".join(f"'{b.name}'" for b in Backend) parser.add_argument( "--backend", @@ -138,7 +157,6 @@ def list_of_strings(arg): "ID numbers being printed in log." "\n\nDefault: Unlimited", ) -parser = maybe_add_vllm_cli_parser(parser) default_dtype = "float16" if torch.cuda.is_available() else "float32" if not vllm_available(): @@ -152,6 +170,17 @@ def list_of_strings(arg): f"Defaults to float16 for GPU and float32 for CPU systems", ) +# The initial_args are required to determine whether the vLLM backend is enabled. +initial_args, _ = parser.parse_known_args() +model_id_or_path = get_model_id_or_path(initial_args) +if is_vllm_backend_enabled(initial_args, model_id_or_path): + # If vLLM backend is enabled, add the vLLM specific CLI arguments to the parser + parser = maybe_add_vllm_cli_parser(parser) +else: + # If vLLM backend is not enabled, add the task argument for Huggingface backend + parser.add_argument( + "--task", required=False, help="The ML task name for huggingface backend" + ) args, _ = parser.parse_known_args() @@ -165,11 +194,7 @@ def list_of_strings(arg): def load_model(): engine_args = None - # If --model_id is specified then pass model_id to HF API, otherwise load the model from /mnt/models - if args.model_id: - model_id_or_path = cast(str, args.model_id) - else: - model_id_or_path = Path(Storage.download(args.model_dir)) + model_id_or_path = get_model_id_or_path(args) if args.disable_log_requests: request_logger = None @@ -182,14 +207,7 @@ def load_model(): if args.backend == Backend.vllm and not vllm_available(): raise RuntimeError("Backend is set to 'vllm' but vLLM is not available") - if ( - (args.backend == Backend.vllm or args.backend == Backend.auto) - and vllm_available() - and infer_vllm_supported_from_model_architecture( - model_id_or_path, - trust_remote_code=args.trust_remote_code, - ) - ): + if is_vllm_backend_enabled(args, model_id_or_path): from .vllm.vllm_model import VLLMModel args.model = args.model_id or args.model_dir diff --git a/python/huggingfaceserver/huggingfaceserver/encoder_model.py b/python/huggingfaceserver/huggingfaceserver/encoder_model.py index 9c57f087167..3fe6368486a 100644 --- a/python/huggingfaceserver/huggingfaceserver/encoder_model.py +++ b/python/huggingfaceserver/huggingfaceserver/encoder_model.py @@ -11,10 +11,13 @@ # 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. - +import base64 import pathlib -from typing import Any, Dict, Optional, Union +import struct +from http import HTTPStatus +from typing import Any, Dict, Optional, Union, List +import pydantic import torch import torch.nn.functional as F from accelerate import init_empty_weights @@ -23,6 +26,13 @@ from kserve.logging import logger from kserve.model import PredictorConfig from kserve.protocol.infer_type import InferInput, InferRequest, InferResponse +from kserve.protocol.rest.openai import ( + EmbeddingRequest, + OpenAIEmbeddingModel, +) +from kserve.protocol.rest.openai.errors import OpenAIError, create_error_response +from kserve.protocol.rest.openai.types import Embedding, EmbeddingObject +from kserve.protocol.rest.openai.types.openapi import Usage from kserve.utils.utils import ( from_np_dtype, get_predict_input, @@ -49,7 +59,9 @@ from .utils import _get_and_verify_max_len, _mean_pooling -class HuggingfaceEncoderModel(Model): # pylint:disable=c-extension-no-member +class HuggingfaceEncoderModel( + Model, OpenAIEmbeddingModel +): # pylint:disable=c-extension-no-member task: MLTask model_config: PretrainedConfig model_id_or_path: Union[pathlib.Path, str] @@ -340,3 +352,68 @@ def _log_request(self, request_id: str, prompt: list[str]) -> None: request_id, prompt=prompt, ) + + async def create_embedding(self, request: EmbeddingRequest) -> Embedding: + params = request.params + + try: + pydantic.TypeAdapter( + Union[str, List[str], List[int], List[List[int]]] + ).validate_python(params.input) + except pydantic.ValidationError as e: + raise OpenAIError( + response=create_error_response( + "'$.input' is invalid. Please check the API reference: https://platform.openai.com/docs/api-reference.", + status_code=HTTPStatus.BAD_REQUEST, + err_type="invalid_request_error", + ) + ) from e + + # The OpenAI documentation allows the input of token lists instead of strings. As the tokenization is specific + # to the model, it is most likely different from the ones used by OpenAI (e.g., tiktoken). Libraries like + # LangChain attempt to determine the proper tokenization based on the model name and will fall back to the + # default "cl100k_base" tokenization, which will certainly not match the deployed model. Instead of silently + # accepting the mismatch, we rather raise an exception. + try: + pydantic.TypeAdapter(Union[str, List[str]]).validate_python(params.input) + except pydantic.ValidationError as e: + raise OpenAIError( + response=create_error_response( + "'input' as token lists is not supported", + status_code=HTTPStatus.NOT_IMPLEMENTED, + err_type="invalid_request_error", + ) + ) from e + + # Call the inference to determine the embedding values + context = {} + instances = params.input if isinstance(params.input, list) else [params.input] + inference_out, _ = await self({"instances": instances}, context) + embedding_out = inference_out["predictions"] + + # Calculate the input token count. Attention mask is "1" for each input token. + num_input_tokens = int(context["attention_mask"].sum()) + + # Optionally encode result to base64 + if params.encoding_format == "base64": + for i, o in enumerate(embedding_out): + embedding_bytes = [struct.pack("=4.0,<5.0", markers = "python_version < \"3.11\""} +async-timeout = {version = ">=4.0,<6.0", markers = "python_version < \"3.11\""} attrs = ">=17.3.0" frozenlist = ">=1.1.1" multidict = ">=4.5,<7.0" -yarl = ">=1.12.0,<2.0" +propcache = ">=0.2.0" +yarl = ">=1.17.0,<2.0" [package.extras] speedups = ["Brotli", "aiodns (>=3.2.0)", "brotlicffi"] +[[package]] +name = "aiohttp-cors" +version = "0.7.0" +description = "CORS support for aiohttp" +optional = true +python-versions = "*" +files = [ + {file = "aiohttp-cors-0.7.0.tar.gz", hash = "sha256:4d39c6d7100fd9764ed1caf8cebf0eb01bf5e3f24e2e073fda6234bc48b19f5d"}, + {file = "aiohttp_cors-0.7.0-py3-none-any.whl", hash = "sha256:0451ba59fdf6909d0e2cd21e4c0a43752bc0703d33fc78ae94d9d9321710193e"}, +] + +[package.dependencies] +aiohttp = ">=1.1" + [[package]] name = "aiosignal" -version = "1.3.1" +version = "1.3.2" description = "aiosignal: a list of registered asynchronous callbacks" optional = true -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ - {file = "aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17"}, - {file = "aiosignal-1.3.1.tar.gz", hash = "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc"}, + {file = "aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5"}, + {file = "aiosignal-1.3.2.tar.gz", hash = "sha256:a8c255c66fafb1e499c9351d0bf32ff2d8a0321595ebac3b93713656d2436f54"}, ] [package.dependencies] frozenlist = ">=1.1.0" +[[package]] +name = "airportsdata" +version = "20241001" +description = "Extensive database of location and timezone data for nearly every airport and landing strip in the world." +optional = true +python-versions = ">=3.9" +files = [ + {file = "airportsdata-20241001-py3-none-any.whl", hash = "sha256:67d71cf2c5378cc17ff66b62b1e11aa2444043949c894543ac8fd8dafce192fd"}, + {file = "airportsdata-20241001.tar.gz", hash = "sha256:fa0bd143b4f4be3557cb892fa0612ef210fd91a92bd720b4d8221de576a4fa00"}, +] + [[package]] name = "annotated-types" version = "0.7.0" @@ -181,65 +192,76 @@ files = [ [[package]] name = "anyio" -version = "4.6.2.post1" +version = "4.8.0" description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = false python-versions = ">=3.9" files = [ - {file = "anyio-4.6.2.post1-py3-none-any.whl", hash = "sha256:6d170c36fba3bdd840c73d3868c1e777e33676a69c3a72cf0a0d5d6d8009b61d"}, - {file = "anyio-4.6.2.post1.tar.gz", hash = "sha256:4c8bc31ccdb51c7f7bd251f51c609e038d63e34219b44aa86e47576389880b4c"}, + {file = "anyio-4.8.0-py3-none-any.whl", hash = "sha256:b5011f270ab5eb0abf13385f851315585cc37ef330dd88e27ec3d34d651fd47a"}, + {file = "anyio-4.8.0.tar.gz", hash = "sha256:1d9fe889df5212298c0c0723fa20479d1b94883a2df44bd3897aa91083316f7a"}, ] [package.dependencies] exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""} idna = ">=2.8" sniffio = ">=1.1" -typing-extensions = {version = ">=4.1", markers = "python_version < \"3.11\""} +typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""} [package.extras] -doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] -test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21.0b1)"] +doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx_rtd_theme"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21)"] trio = ["trio (>=0.26.1)"] +[[package]] +name = "astor" +version = "0.8.1" +description = "Read/rewrite/write Python ASTs" +optional = true +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" +files = [ + {file = "astor-0.8.1-py2.py3-none-any.whl", hash = "sha256:070a54e890cefb5b3739d19f30f5a5ec840ffc9c50ffa7d23cc9fc1a38ebbfc5"}, + {file = "astor-0.8.1.tar.gz", hash = "sha256:6a6effda93f4e1ce9f618779b2dd1d9d84f1e32812c23a29b3fff6fd7f63fa5e"}, +] + [[package]] name = "async-timeout" -version = "4.0.3" +version = "5.0.1" description = "Timeout context manager for asyncio programs" optional = true -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"}, - {file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"}, + {file = "async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c"}, + {file = "async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3"}, ] [[package]] name = "attrs" -version = "24.2.0" +version = "24.3.0" description = "Classes Without Boilerplate" optional = true -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2"}, - {file = "attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346"}, + {file = "attrs-24.3.0-py3-none-any.whl", hash = "sha256:ac96cd038792094f438ad1f6ff80837353805ac950cd2aa0e0625ef19850c308"}, + {file = "attrs-24.3.0.tar.gz", hash = "sha256:8f5c07333d543103541ba7be0e2ce16eeee8130cb0b3f9238ab904ce1e85baff"}, ] [package.extras] benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"] tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] [[package]] name = "azure-core" -version = "1.31.0" +version = "1.32.0" description = "Microsoft Azure Core Library for Python" optional = false python-versions = ">=3.8" files = [ - {file = "azure_core-1.31.0-py3-none-any.whl", hash = "sha256:22954de3777e0250029360ef31d80448ef1be13b80a459bff80ba7073379e2cd"}, - {file = "azure_core-1.31.0.tar.gz", hash = "sha256:656a0dd61e1869b1506b7c6a3b31d62f15984b1a573d6326f6aa2f3e4123284b"}, + {file = "azure_core-1.32.0-py3-none-any.whl", hash = "sha256:eac191a0efb23bfa83fddf321b27b122b4ec847befa3091fa736a5c32c50d7b4"}, + {file = "azure_core-1.32.0.tar.gz", hash = "sha256:22b3c35d6b2dae14990f6c1be2912bf23ffe50b220e708a28ab1bb92b1c730e5"}, ] [package.dependencies] @@ -270,13 +292,13 @@ typing-extensions = ">=4.0.0" [[package]] name = "azure-storage-blob" -version = "12.23.1" +version = "12.24.0" description = "Microsoft Azure Blob Storage Client Library for Python" optional = false python-versions = ">=3.8" files = [ - {file = "azure_storage_blob-12.23.1-py3-none-any.whl", hash = "sha256:1c2238aa841d1545f42714a5017c010366137a44a0605da2d45f770174bfc6b4"}, - {file = "azure_storage_blob-12.23.1.tar.gz", hash = "sha256:a587e54d4e39d2a27bd75109db164ffa2058fe194061e5446c5a89bca918272f"}, + {file = "azure_storage_blob-12.24.0-py3-none-any.whl", hash = "sha256:4f0bb4592ea79a2d986063696514c781c9e62be240f09f6397986e01755bc071"}, + {file = "azure_storage_blob-12.24.0.tar.gz", hash = "sha256:eaaaa1507c8c363d6e1d1342bd549938fdf1adec9b1ada8658c8f5bf3aea844e"}, ] [package.dependencies] @@ -290,13 +312,13 @@ aio = ["azure-core[aio] (>=1.30.0)"] [[package]] name = "azure-storage-file-share" -version = "12.19.0" +version = "12.20.0" description = "Microsoft Azure Azure File Share Storage Client Library for Python" optional = false python-versions = ">=3.8" files = [ - {file = "azure_storage_file_share-12.19.0-py3-none-any.whl", hash = "sha256:eac6cf1a454aba58af4e6ba450b36d16aa1d0c49679fb64ea8756bb896698c5b"}, - {file = "azure_storage_file_share-12.19.0.tar.gz", hash = "sha256:ea7a4174dc6c52f50ac8c30f228159fcc3675d1f8ba771b8d0efcbc310740278"}, + {file = "azure_storage_file_share-12.20.0-py3-none-any.whl", hash = "sha256:fd5c4f09d7784d68b8ed3de473b7525904f1c4b115f9cd200c838b0ee720cb5f"}, + {file = "azure_storage_file_share-12.20.0.tar.gz", hash = "sha256:f120fc67bae0a84c1b54d06faa70df351be14d1395b9a085350e833f7d347a65"}, ] [package.dependencies] @@ -355,19 +377,113 @@ d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] uvloop = ["uvloop (>=0.15.2)"] +[[package]] +name = "blake3" +version = "1.0.1" +description = "Python bindings for the Rust blake3 crate" +optional = true +python-versions = "*" +files = [ + {file = "blake3-1.0.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:4b24dc5bfaaeda911e5b7dbc99f47c9c5fc84d8841656bcb043ce5c402db3088"}, + {file = "blake3-1.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:41018d98e062931a19dc3bfa77a0f34d5dfa756d5859f4cfe678ccbe24815b36"}, + {file = "blake3-1.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6727d1ea501b6ef9f159a17039df449997ce95ee79c1a2be7898b54fd09659db"}, + {file = "blake3-1.0.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b95a859c249b32191732c7fb55d006497c0d7bcb6211ce8e187b4e7cc64d7f1e"}, + {file = "blake3-1.0.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a3e99ffccb733468cd6a68da3b148a010721eeb6517d2892c2fad01ac088d8c4"}, + {file = "blake3-1.0.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:49033a5184cfe34fbed453f85de03564dfd6ae3271e2e33de56a11d2b453c163"}, + {file = "blake3-1.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:251bf7693b434d067632d0dce41466774531f369d8aadc83c23d702bcf70fb29"}, + {file = "blake3-1.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8abd05d45b68bedb7341fdaaaf564c105b868cb4b89d7b29e74feb388430169e"}, + {file = "blake3-1.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:f7d61b5d4d7adacf9d6d22422d8bc0cee23264f8f886b39d634a522ed82c71a5"}, + {file = "blake3-1.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:dcc46d88e820ab8ac200cb0547e287458c845fd412369fd8dc89a6ed466019d6"}, + {file = "blake3-1.0.1-cp310-cp310-win32.whl", hash = "sha256:bf3ae73c25e7dbaae099ce8511b525e50d981d437dd50f7a46a98a5ae417302a"}, + {file = "blake3-1.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:1f887aca4798ae7f1899fa3ef8c399ba889c33aa9e7ebf067e7f1ade09144481"}, + {file = "blake3-1.0.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:5f9ec05d9bd156e759fe9989c0d6e75b36410850a99296aa03eef7cd1198ca5e"}, + {file = "blake3-1.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:46515c0b1dba02d48184417b8bae611d5c12b114f9a8b103746fb8d07b981042"}, + {file = "blake3-1.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cfbe2d39ef8a88a4e1937773d38f8a9b05f6328716e760342b3af0a7f3467869"}, + {file = "blake3-1.0.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:855d9dc151017b8295fe10628b6bd8c1d10fc96f5c561fb4281faf70492a2104"}, + {file = "blake3-1.0.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96fc1cbebc964678674c2bd27650e417a37a242ba73337ae2ef7f323e8b41b21"}, + {file = "blake3-1.0.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5a27cb7af5e337d08a5d1c3c440384911b65812509b30408c6212df9c13e3bb4"}, + {file = "blake3-1.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9eee484df045753178eab4ede2c7570306ffdbf874cc5740ad08e549bd73714"}, + {file = "blake3-1.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ffda996220524e1e21d1d3a13a3638d49d67c1178d3313b03ecc03c49f8432f"}, + {file = "blake3-1.0.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f492c253bd6f8f0ffed0d185bbbfc4ccfcaff35e6846665e40154ac949a079bb"}, + {file = "blake3-1.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d38b6b9ef126e25ffecb7a64307df718f6831630c95d54301932660bb39af35a"}, + {file = "blake3-1.0.1-cp311-cp311-win32.whl", hash = "sha256:a9702eed8480c30f9da660ac7ecfc43f8ab2e9f0e1f3a4097ac4045628208696"}, + {file = "blake3-1.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:c1adb415561df49ffc19ebd4ab36025b3c263f4813ccdbddc467e9a4ac50d307"}, + {file = "blake3-1.0.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:0b8781fd128e8a6448beb1ecc5863617f167af3f911a8013afc0f54319a5542b"}, + {file = "blake3-1.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5970ec6191e4b20ff5f4e3dce9fd368599677ade0765f1b6d5fc5df859ec18e3"}, + {file = "blake3-1.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9405186a6fd2d6c9d87bcf6ebc30836f56e3a37e32697e0457d7f7c6b954e166"}, + {file = "blake3-1.0.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:55917351d9b2f404ec1216be8394ec85db030294a8b0953c45c21c7b69ff10d8"}, + {file = "blake3-1.0.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6d174c9b21be3463a8aa9cc05d796cd70da7ec03c1e01d2aaa860e03e0eb3156"}, + {file = "blake3-1.0.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e8cae27f0df12b0d4387af16a9cbe1bb7d95c66d21c863706af94b05cefd0360"}, + {file = "blake3-1.0.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ef8ec9fa6d4d3446746621245090b4328dfff18f3a93c89a5ccf82a99b50accb"}, + {file = "blake3-1.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99a258e5fbaf85e96bf40a7463df5acef626d69a4e79c599366cde33ea56ea6e"}, + {file = "blake3-1.0.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b63a8e8579ba3a5513937a114bcc2acc07a746a6b903a46070bc5d3f7d32667d"}, + {file = "blake3-1.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a49580f0ee717b55843f15c9a1ffc5f946a9cb3d15bdda4024361fdfab6ef1e6"}, + {file = "blake3-1.0.1-cp312-cp312-win32.whl", hash = "sha256:ac6f55570db7da41b29de7af120a9edfa27c2e4724e7e4ebb0aa4b41414c9e41"}, + {file = "blake3-1.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:ddcc95cd5a4db73bd97a4d21eacfc793c057baae7d5b962f182467f861d2b4a6"}, + {file = "blake3-1.0.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:8c800eff0f5dbb13062cbe0970e3b922de473f10bd9729e166d58618b79082ad"}, + {file = "blake3-1.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:026b0e9c643fdc9f2fab8435b582fe59b705acdf2560197dd68c75f24c5db3a5"}, + {file = "blake3-1.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d871674e43fe9cfb2a0e17267d988572f681b471c5bc7f262e35d6a56517380c"}, + {file = "blake3-1.0.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:598905ac64221ea8823466e5fced2923eea8d6f508d61b177d3d3b00a65fdbed"}, + {file = "blake3-1.0.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:756b4dcf4be33f1a8a518bc09b939eb7052ff00c2c118a66300ac7ae49898361"}, + {file = "blake3-1.0.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:58e3736578577da9f0297482387f7babf5d08ab68d935d71345ad3740a219c11"}, + {file = "blake3-1.0.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d0a20efd5efd46074a7c073768e958c451985cba1c296eb79f6dd19c9d7d2173"}, + {file = "blake3-1.0.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:795bca3208fa11d2eeb26c2cb38e9a94f06cbec9f97fe6752b82b12645e5ff22"}, + {file = "blake3-1.0.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:46486763ffe98f0296055f5e6a413bbd5822fd298c461127b28f5b87bb3cdfa5"}, + {file = "blake3-1.0.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:06ef09f7a9e3b2971e123a0bb408edaaf4c33ae5917f0c11fe05520b5d85f847"}, + {file = "blake3-1.0.1-cp313-cp313-win32.whl", hash = "sha256:14e29d2a0025eb8597550187dc9a4f2e86a92b9bfc2ac8f102f7e62bf55002ce"}, + {file = "blake3-1.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:e57af037cc5ef7fbd20fd0fa9b59b2975899d8150fe4f81035c0efb06d4cc7cf"}, + {file = "blake3-1.0.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:a13bb0ec647f280491d6ad9b105e5f529166d600a3f71f09412171870844792d"}, + {file = "blake3-1.0.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:6c981943bdc8bf5eff1833fbaeafb4360b889af31deccf8f52813608d98fff78"}, + {file = "blake3-1.0.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e7656948aba53b0e68380dc3cca6b2f93965973469ef99077f5855a0e62b9dc"}, + {file = "blake3-1.0.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:795fe3c98ab81691c560faeac1181066931d7c14dbb8bf37bfbf4b46b9eb4e16"}, + {file = "blake3-1.0.1-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62f04db159714e651c7e75e457b502bb121b343b756dd251608c859963a03a22"}, + {file = "blake3-1.0.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:74789ce1273c9d80929d33474dbcc8d2f06dd2a43e98cf47b8282cdfb2567367"}, + {file = "blake3-1.0.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:352086155099ea7175b71e8d3d7589f1087470542ca1c763ef325775de5533ea"}, + {file = "blake3-1.0.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3bf4e616b5ade1557978d91897294f436d952b91ed5ef2cb9cd5765a03d3d42"}, + {file = "blake3-1.0.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:a911412d45c48364917875aad0fbaa9fcfa4b3b4fc9be8a21ccdcc83f0b15d93"}, + {file = "blake3-1.0.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:9cb284412420f51f28378c65d5d218e8fe135e40940c185fd9dd3e5257f673d3"}, + {file = "blake3-1.0.1-cp313-cp313t-win32.whl", hash = "sha256:9b41e301a3bb962fa7d16b9b81b2f11f52af7dadd310aad9fb9e9317daaaf79a"}, + {file = "blake3-1.0.1-cp313-cp313t-win_amd64.whl", hash = "sha256:beb755df125c978d60bfbc8b35fbbf242aeb163ea7195f70541318b1f739f4fa"}, + {file = "blake3-1.0.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:7f395e608d1daf872b1a65e76b7e0a6fb825ddc1b25a954d777997f8bedaf285"}, + {file = "blake3-1.0.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3e717aceb2018f824a00fd0e1390698412ed34a51ecc234d99ef56111f4c1b3f"}, + {file = "blake3-1.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feb1186b5e5c4ae86b1f84e450dbe6a8d0eacad8019d17067da3c84885c6388e"}, + {file = "blake3-1.0.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7287dd5b2218cd3417a4c6922b52540f8f80d6685cab15ef910b22ae11c84ce6"}, + {file = "blake3-1.0.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:95c43b3886264cb982d8bad7a4b19b902fdb64d614073448638932abe1f16e41"}, + {file = "blake3-1.0.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2037e5f8b319003b61dfc28b2d6f59b6ad757448dd986e422642e60f5dedb970"}, + {file = "blake3-1.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3a663565cfd2abf4307e67e69a1b0f17e6b118471eac325365425aee1074e8f3"}, + {file = "blake3-1.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56f73ea343a782fbc195424c6df265e38a183051ee72dd032560206b92a09b22"}, + {file = "blake3-1.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a6550ff4a3c4c6e85ec9bbe2082bd55cd47cc26e191419a0f8bc15dccc15b5f0"}, + {file = "blake3-1.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:87af8feb07126e3868d98a32da6a8639744aecb4e0dd8819a039aaa8687f078e"}, + {file = "blake3-1.0.1-cp38-cp38-win32.whl", hash = "sha256:77c68aaae459c67bb271271e114e08c5a240af509ee5925b30be3bc1ccd24ec9"}, + {file = "blake3-1.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:9a4f747db6d28f56939e1793fba3af3ddfe17d13819ddca60b758357a295ac84"}, + {file = "blake3-1.0.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:e8a470f6f6111adbb89e054ba8f8d250369738ea07c0f4d7fca13c20c56e429d"}, + {file = "blake3-1.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6bf55e69e8bb4d26c72aff0da7e1703f48d6a3090eb1df4d646f77f419db2c3b"}, + {file = "blake3-1.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b0d8f7eff3dd243f9c7e8c3351aa7cfc0934c7ccf404c5d868bc3778b4982071"}, + {file = "blake3-1.0.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:088ee326dc1a650f9a128f3876ce2507467263fe6dcbbd21a67ce119a85ba93b"}, + {file = "blake3-1.0.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5a6b8f8b2fd288b1bf99aa80a1c9690b527bca853f560cf91d853447aec37cb4"}, + {file = "blake3-1.0.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de0e747e9c9dcd6242bf771a6dc54fcd294c8c613eed0fb2bd3aa5cbfa57db11"}, + {file = "blake3-1.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b2bdaca3949d6b40a9dd1e910a6a70d1f178ddc09b85fb8b82e08a1948479f8"}, + {file = "blake3-1.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ae2282712c50d496037b0a694d5e1e31a7124d918a49c5feedef93146838ab0"}, + {file = "blake3-1.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d524f2e94b3251e262c4ace3c6ef0fd7637e3e9b54dd933f4d49aa38cf5f2c03"}, + {file = "blake3-1.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a625c9ecb89148c0c073ff0b3be14d17c5122879873783f8e85e8b1f18cc730a"}, + {file = "blake3-1.0.1-cp39-cp39-win32.whl", hash = "sha256:35214a680c990edc110de5093ede02a55e4379e8b1882b01e69e5b2a45f9091e"}, + {file = "blake3-1.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:d1817014c7c7ab64b5bfa8db2e0a8b9cd44e43e50ea86d50af568525c5607be9"}, + {file = "blake3-1.0.1.tar.gz", hash = "sha256:a76c54e91ca3a28be760575726f8dc01572734023fe88bf8dfa76913fa48a943"}, +] + [[package]] name = "boto3" -version = "1.35.49" +version = "1.35.95" description = "The AWS SDK for Python" optional = false python-versions = ">=3.8" files = [ - {file = "boto3-1.35.49-py3-none-any.whl", hash = "sha256:b660c649a27a6b47a34f6f858f5bd7c3b0a798a16dec8dda7cbebeee80fd1f60"}, - {file = "boto3-1.35.49.tar.gz", hash = "sha256:ddecb27f5699ca9f97711c52b6c0652c2e63bf6c2bfbc13b819b4f523b4d30ff"}, + {file = "boto3-1.35.95-py3-none-any.whl", hash = "sha256:c81223488607457dacb7829ee0c99803c664593b34a2b0f86c71d421e7c3469a"}, + {file = "boto3-1.35.95.tar.gz", hash = "sha256:d5671226819f6a78e31b1f37bd60f194afb8203254a543d06bdfb76de7d79236"}, ] [package.dependencies] -botocore = ">=1.35.49,<1.36.0" +botocore = ">=1.35.95,<1.36.0" jmespath = ">=0.7.1,<2.0.0" s3transfer = ">=0.10.0,<0.11.0" @@ -376,13 +492,13 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] [[package]] name = "botocore" -version = "1.35.49" +version = "1.35.95" description = "Low-level, data-driven core of boto 3." optional = false python-versions = ">=3.8" files = [ - {file = "botocore-1.35.49-py3-none-any.whl", hash = "sha256:aed4d3643afd702920792b68fbe712a8c3847993820d1048cd238a6469354da1"}, - {file = "botocore-1.35.49.tar.gz", hash = "sha256:07d0c1325fdbfa49a4a054413dbdeab0a6030449b2aa66099241af2dac48afd8"}, + {file = "botocore-1.35.95-py3-none-any.whl", hash = "sha256:a672406f748ad6a5b2fb7ea0d8394539eb4fda5332fc5373467d232c4bb27b12"}, + {file = "botocore-1.35.95.tar.gz", hash = "sha256:b03d2d7cc58a16aa96a7e8f21941b766e98abc6ea74f61a63de7dc26c03641d3"}, ] [package.dependencies] @@ -409,13 +525,13 @@ files = [ [[package]] name = "certifi" -version = "2024.8.30" +version = "2024.12.14" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"}, - {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, + {file = "certifi-2024.12.14-py3-none-any.whl", hash = "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56"}, + {file = "certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db"}, ] [[package]] @@ -499,127 +615,114 @@ pycparser = "*" [[package]] name = "charset-normalizer" -version = "3.4.0" +version = "3.4.1" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false -python-versions = ">=3.7.0" +python-versions = ">=3.7" files = [ - {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4f9fc98dad6c2eaa32fc3af1417d95b5e3d08aff968df0cd320066def971f9a6"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0de7b687289d3c1b3e8660d0741874abe7888100efe14bd0f9fd7141bcbda92b"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5ed2e36c3e9b4f21dd9422f6893dec0abf2cca553af509b10cd630f878d3eb99"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d3ff7fc90b98c637bda91c89d51264a3dcf210cade3a2c6f838c7268d7a4ca"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1110e22af8ca26b90bd6364fe4c763329b0ebf1ee213ba32b68c73de5752323d"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:86f4e8cca779080f66ff4f191a685ced73d2f72d50216f7112185dc02b90b9b7"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f683ddc7eedd742e2889d2bfb96d69573fde1d92fcb811979cdb7165bb9c7d3"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27623ba66c183eca01bf9ff833875b459cad267aeeb044477fedac35e19ba907"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f606a1881d2663630ea5b8ce2efe2111740df4b687bd78b34a8131baa007f79b"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0b309d1747110feb25d7ed6b01afdec269c647d382c857ef4663bbe6ad95a912"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:136815f06a3ae311fae551c3df1f998a1ebd01ddd424aa5603a4336997629e95"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:14215b71a762336254351b00ec720a8e85cada43b987da5a042e4ce3e82bd68e"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:79983512b108e4a164b9c8d34de3992f76d48cadc9554c9e60b43f308988aabe"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-win32.whl", hash = "sha256:c94057af19bc953643a33581844649a7fdab902624d2eb739738a30e2b3e60fc"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:55f56e2ebd4e3bc50442fbc0888c9d8c94e4e06a933804e2af3e89e2f9c1c749"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0d99dd8ff461990f12d6e42c7347fd9ab2532fb70e9621ba520f9e8637161d7c"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c57516e58fd17d03ebe67e181a4e4e2ccab1168f8c2976c6a334d4f819fe5944"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6dba5d19c4dfab08e58d5b36304b3f92f3bd5d42c1a3fa37b5ba5cdf6dfcbcee"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf4475b82be41b07cc5e5ff94810e6a01f276e37c2d55571e3fe175e467a1a1c"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce031db0408e487fd2775d745ce30a7cd2923667cf3b69d48d219f1d8f5ddeb6"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ff4e7cdfdb1ab5698e675ca622e72d58a6fa2a8aa58195de0c0061288e6e3ea"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3710a9751938947e6327ea9f3ea6332a09bf0ba0c09cae9cb1f250bd1f1549bc"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82357d85de703176b5587dbe6ade8ff67f9f69a41c0733cf2425378b49954de5"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47334db71978b23ebcf3c0f9f5ee98b8d65992b65c9c4f2d34c2eaf5bcaf0594"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8ce7fd6767a1cc5a92a639b391891bf1c268b03ec7e021c7d6d902285259685c"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f1a2f519ae173b5b6a2c9d5fa3116ce16e48b3462c8b96dfdded11055e3d6365"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:63bc5c4ae26e4bc6be6469943b8253c0fd4e4186c43ad46e713ea61a0ba49129"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bcb4f8ea87d03bc51ad04add8ceaf9b0f085ac045ab4d74e73bbc2dc033f0236"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-win32.whl", hash = "sha256:9ae4ef0b3f6b41bad6366fb0ea4fc1d7ed051528e113a60fa2a65a9abb5b1d99"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cee4373f4d3ad28f1ab6290684d8e2ebdb9e7a1b74fdc39e4c211995f77bec27"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0713f3adb9d03d49d365b70b84775d0a0d18e4ab08d12bc46baa6132ba78aaf6"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:de7376c29d95d6719048c194a9cf1a1b0393fbe8488a22008610b0361d834ecf"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4a51b48f42d9358460b78725283f04bddaf44a9358197b889657deba38f329db"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b295729485b06c1a0683af02a9e42d2caa9db04a373dc38a6a58cdd1e8abddf1"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ee803480535c44e7f5ad00788526da7d85525cfefaf8acf8ab9a310000be4b03"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d59d125ffbd6d552765510e3f31ed75ebac2c7470c7274195b9161a32350284"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cda06946eac330cbe6598f77bb54e690b4ca93f593dee1568ad22b04f347c15"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07afec21bbbbf8a5cc3651aa96b980afe2526e7f048fdfb7f1014d84acc8b6d8"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6b40e8d38afe634559e398cc32b1472f376a4099c75fe6299ae607e404c033b2"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b8dcd239c743aa2f9c22ce674a145e0a25cb1566c495928440a181ca1ccf6719"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:84450ba661fb96e9fd67629b93d2941c871ca86fc38d835d19d4225ff946a631"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:44aeb140295a2f0659e113b31cfe92c9061622cadbc9e2a2f7b8ef6b1e29ef4b"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1db4e7fefefd0f548d73e2e2e041f9df5c59e178b4c72fbac4cc6f535cfb1565"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-win32.whl", hash = "sha256:5726cf76c982532c1863fb64d8c6dd0e4c90b6ece9feb06c9f202417a31f7dd7"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:b197e7094f232959f8f20541ead1d9862ac5ebea1d58e9849c1bf979255dfac9"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:dd4eda173a9fcccb5f2e2bd2a9f423d180194b1bf17cf59e3269899235b2a114"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e9e3c4c9e1ed40ea53acf11e2a386383c3304212c965773704e4603d589343ed"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92a7e36b000bf022ef3dbb9c46bfe2d52c047d5e3f3343f43204263c5addc250"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54b6a92d009cbe2fb11054ba694bc9e284dad30a26757b1e372a1fdddaf21920"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ffd9493de4c922f2a38c2bf62b831dcec90ac673ed1ca182fe11b4d8e9f2a64"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35c404d74c2926d0287fbd63ed5d27eb911eb9e4a3bb2c6d294f3cfd4a9e0c23"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4796efc4faf6b53a18e3d46343535caed491776a22af773f366534056c4e1fbc"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7fdd52961feb4c96507aa649550ec2a0d527c086d284749b2f582f2d40a2e0d"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:92db3c28b5b2a273346bebb24857fda45601aef6ae1c011c0a997106581e8a88"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ab973df98fc99ab39080bfb0eb3a925181454d7c3ac8a1e695fddfae696d9e90"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4b67fdab07fdd3c10bb21edab3cbfe8cf5696f453afce75d815d9d7223fbe88b"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:aa41e526a5d4a9dfcfbab0716c7e8a1b215abd3f3df5a45cf18a12721d31cb5d"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-win32.whl", hash = "sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:dbe03226baf438ac4fda9e2d0715022fd579cb641c4cf639fa40d53b2fe6f3e2"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd9a8bd8900e65504a305bf8ae6fa9fbc66de94178c420791d0293702fce2df7"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8831399554b92b72af5932cdbbd4ddc55c55f631bb13ff8fe4e6536a06c5c51"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a14969b8691f7998e74663b77b4c36c0337cb1df552da83d5c9004a93afdb574"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dcaf7c1524c0542ee2fc82cc8ec337f7a9f7edee2532421ab200d2b920fc97cf"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425c5f215d0eecee9a56cdb703203dda90423247421bf0d67125add85d0c4455"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:d5b054862739d276e09928de37c79ddeec42a6e1bfc55863be96a36ba22926f6"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:f3e73a4255342d4eb26ef6df01e3962e73aa29baa3124a8e824c5d3364a65748"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:2f6c34da58ea9c1a9515621f4d9ac379871a8f21168ba1b5e09d74250de5ad62"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:f09cb5a7bbe1ecae6e87901a2eb23e0256bb524a79ccc53eb0b7629fbe7677c4"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:0099d79bdfcf5c1f0c2c72f91516702ebf8b0b8ddd8905f97a8aecf49712c621"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-win32.whl", hash = "sha256:9c98230f5042f4945f957d006edccc2af1e03ed5e37ce7c373f00a5a4daa6149"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:62f60aebecfc7f4b82e3f639a7d1433a20ec32824db2199a11ad4f5e146ef5ee"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:af73657b7a68211996527dbfeffbb0864e043d270580c5aef06dc4b659a4b578"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cab5d0b79d987c67f3b9e9c53f54a61360422a5a0bc075f43cab5621d530c3b6"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9289fd5dddcf57bab41d044f1756550f9e7cf0c8e373b8cdf0ce8773dc4bd417"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b493a043635eb376e50eedf7818f2f322eabbaa974e948bd8bdd29eb7ef2a51"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fa2566ca27d67c86569e8c85297aaf413ffab85a8960500f12ea34ff98e4c41"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8e538f46104c815be19c975572d74afb53f29650ea2025bbfaef359d2de2f7f"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fd30dc99682dc2c603c2b315bded2799019cea829f8bf57dc6b61efde6611c8"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2006769bd1640bdf4d5641c69a3d63b71b81445473cac5ded39740a226fa88ab"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:dc15e99b2d8a656f8e666854404f1ba54765871104e50c8e9813af8a7db07f12"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:ab2e5bef076f5a235c3774b4f4028a680432cded7cad37bba0fd90d64b187d19"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:4ec9dd88a5b71abfc74e9df5ebe7921c35cbb3b641181a531ca65cdb5e8e4dea"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:43193c5cda5d612f247172016c4bb71251c784d7a4d9314677186a838ad34858"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:aa693779a8b50cd97570e5a0f343538a8dbd3e496fa5dcb87e29406ad0299654"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-win32.whl", hash = "sha256:7706f5850360ac01d80c89bcef1640683cc12ed87f42579dab6c5d3ed6888613"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:c3e446d253bd88f6377260d07c895816ebf33ffffd56c1c792b13bff9c3e1ade"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:980b4f289d1d90ca5efcf07958d3eb38ed9c0b7676bf2831a54d4f66f9c27dfa"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f28f891ccd15c514a0981f3b9db9aa23d62fe1a99997512b0491d2ed323d229a"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8aacce6e2e1edcb6ac625fb0f8c3a9570ccc7bfba1f63419b3769ccf6a00ed0"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7af3717683bea4c87acd8c0d3d5b44d56120b26fd3f8a692bdd2d5260c620a"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ff2ed8194587faf56555927b3aa10e6fb69d931e33953943bc4f837dfee2242"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e91f541a85298cf35433bf66f3fab2a4a2cff05c127eeca4af174f6d497f0d4b"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:309a7de0a0ff3040acaebb35ec45d18db4b28232f21998851cfa709eeff49d62"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:285e96d9d53422efc0d7a17c60e59f37fbf3dfa942073f666db4ac71e8d726d0"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5d447056e2ca60382d460a604b6302d8db69476fd2015c81e7c35417cfabe4cd"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:20587d20f557fe189b7947d8e7ec5afa110ccf72a3128d61a2a387c3313f46be"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:130272c698667a982a5d0e626851ceff662565379baf0ff2cc58067b81d4f11d"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ab22fbd9765e6954bc0bcff24c25ff71dcbfdb185fcdaca49e81bac68fe724d3"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7782afc9b6b42200f7362858f9e73b1f8316afb276d316336c0ec3bd73312742"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-win32.whl", hash = "sha256:2de62e8801ddfff069cd5c504ce3bc9672b23266597d4e4f50eda28846c322f2"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:95c3c157765b031331dd4db3c775e58deaee050a3042fcad72cbc4189d7c8dca"}, - {file = "charset_normalizer-3.4.0-py3-none-any.whl", hash = "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079"}, - {file = "charset_normalizer-3.4.0.tar.gz", hash = "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-win32.whl", hash = "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f30bf9fd9be89ecb2360c7d94a711f00c09b976258846efe40db3d05828e8089"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:97f68b8d6831127e4787ad15e6757232e14e12060bec17091b85eb1486b91d8d"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7974a0b5ecd505609e3b19742b60cee7aa2aa2fb3151bc917e6e2646d7667dcf"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc54db6c8593ef7d4b2a331b58653356cf04f67c960f584edb7c3d8c97e8f39e"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:311f30128d7d333eebd7896965bfcfbd0065f1716ec92bd5638d7748eb6f936a"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:7d053096f67cd1241601111b698f5cad775f97ab25d81567d3f59219b5f1adbd"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:807f52c1f798eef6cf26beb819eeb8819b1622ddfeef9d0977a8502d4db6d534"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:dccbe65bd2f7f7ec22c4ff99ed56faa1e9f785482b9bbd7c717e26fd723a1d1e"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:2fb9bd477fdea8684f78791a6de97a953c51831ee2981f8e4f583ff3b9d9687e"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:01732659ba9b5b873fc117534143e4feefecf3b2078b0a6a2e925271bb6f4cfa"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-win32.whl", hash = "sha256:7a4f97a081603d2050bfaffdefa5b02a9ec823f8348a572e39032caa8404a487"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:7b1bef6280950ee6c177b326508f86cad7ad4dff12454483b51d8b7d673a2c5d"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ecddf25bee22fe4fe3737a399d0d177d72bc22be6913acfab364b40bce1ba83c"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c60ca7339acd497a55b0ea5d506b2a2612afb2826560416f6894e8b5770d4a9"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b7b2d86dd06bfc2ade3312a83a5c364c7ec2e3498f8734282c6c3d4b07b346b8"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd78cfcda14a1ef52584dbb008f7ac81c1328c0f58184bf9a84c49c605002da6"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e27f48bcd0957c6d4cb9d6fa6b61d192d0b13d5ef563e5f2ae35feafc0d179c"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01ad647cdd609225c5350561d084b42ddf732f4eeefe6e678765636791e78b9a"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:619a609aa74ae43d90ed2e89bdd784765de0a25ca761b93e196d938b8fd1dbbd"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:89149166622f4db9b4b6a449256291dc87a99ee53151c74cbd82a53c8c2f6ccd"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:7709f51f5f7c853f0fb938bcd3bc59cdfdc5203635ffd18bf354f6967ea0f824"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:345b0426edd4e18138d6528aed636de7a9ed169b4aaf9d61a8c19e39d26838ca"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0907f11d019260cdc3f94fbdb23ff9125f6b5d1039b76003b5b0ac9d6a6c9d5b"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-win32.whl", hash = "sha256:ea0d8d539afa5eb2728aa1932a988a9a7af94f18582ffae4bc10b3fbdad0626e"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:329ce159e82018d646c7ac45b01a430369d526569ec08516081727a20e9e4af4"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-win32.whl", hash = "sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765"}, + {file = "charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85"}, + {file = "charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3"}, ] [[package]] name = "click" -version = "8.1.7" +version = "8.1.8" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.7" files = [ - {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, - {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, + {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, + {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, ] [package.dependencies] @@ -664,15 +767,29 @@ files = [ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +[[package]] +name = "colorful" +version = "0.5.6" +description = "Terminal string styling done right, in Python." +optional = true +python-versions = "*" +files = [ + {file = "colorful-0.5.6-py2.py3-none-any.whl", hash = "sha256:eab8c1c809f5025ad2b5238a50bd691e26850da8cac8f90d660ede6ea1af9f1e"}, + {file = "colorful-0.5.6.tar.gz", hash = "sha256:b56d5c01db1dac4898308ea889edcb113fbee3e6ec5df4bacffd61d5241b5b8d"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + [[package]] name = "compressed-tensors" -version = "0.6.0" +version = "0.9.1" description = "Library for utilization of compressed safetensors of neural network models" optional = true python-versions = "*" files = [ - {file = "compressed-tensors-0.6.0.tar.gz", hash = "sha256:639ca97afc852602be0d3666b236ad6a96880de45af87851f515047eff700927"}, - {file = "compressed_tensors-0.6.0-py3-none-any.whl", hash = "sha256:1be9c466e38b992b1d462e577f7e1b2bfad5d1aa0e25e9c95ab1ee458b9e92a2"}, + {file = "compressed-tensors-0.9.1.tar.gz", hash = "sha256:3cf5cd637f0186c184dd5bbbbf941356b1225199b49c6a45bf0909d65907f686"}, + {file = "compressed_tensors-0.9.1-py3-none-any.whl", hash = "sha256:77385f879c5c092db777a7880851cd9f801bf2f9bb46bb4402f052d9e002975c"}, ] [package.dependencies] @@ -686,73 +803,73 @@ dev = ["black (==22.12.0)", "flake8 (>=3.8.3)", "isort (==5.8.0)", "nbconvert (> [[package]] name = "coverage" -version = "7.6.4" +version = "7.6.10" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.9" files = [ - {file = "coverage-7.6.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5f8ae553cba74085db385d489c7a792ad66f7f9ba2ee85bfa508aeb84cf0ba07"}, - {file = "coverage-7.6.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8165b796df0bd42e10527a3f493c592ba494f16ef3c8b531288e3d0d72c1f6f0"}, - {file = "coverage-7.6.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7c8b95bf47db6d19096a5e052ffca0a05f335bc63cef281a6e8fe864d450a72"}, - {file = "coverage-7.6.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ed9281d1b52628e81393f5eaee24a45cbd64965f41857559c2b7ff19385df51"}, - {file = "coverage-7.6.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0809082ee480bb8f7416507538243c8863ac74fd8a5d2485c46f0f7499f2b491"}, - {file = "coverage-7.6.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d541423cdd416b78626b55f123412fcf979d22a2c39fce251b350de38c15c15b"}, - {file = "coverage-7.6.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:58809e238a8a12a625c70450b48e8767cff9eb67c62e6154a642b21ddf79baea"}, - {file = "coverage-7.6.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c9b8e184898ed014884ca84c70562b4a82cbc63b044d366fedc68bc2b2f3394a"}, - {file = "coverage-7.6.4-cp310-cp310-win32.whl", hash = "sha256:6bd818b7ea14bc6e1f06e241e8234508b21edf1b242d49831831a9450e2f35fa"}, - {file = "coverage-7.6.4-cp310-cp310-win_amd64.whl", hash = "sha256:06babbb8f4e74b063dbaeb74ad68dfce9186c595a15f11f5d5683f748fa1d172"}, - {file = "coverage-7.6.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:73d2b73584446e66ee633eaad1a56aad577c077f46c35ca3283cd687b7715b0b"}, - {file = "coverage-7.6.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:51b44306032045b383a7a8a2c13878de375117946d68dcb54308111f39775a25"}, - {file = "coverage-7.6.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b3fb02fe73bed561fa12d279a417b432e5b50fe03e8d663d61b3d5990f29546"}, - {file = "coverage-7.6.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed8fe9189d2beb6edc14d3ad19800626e1d9f2d975e436f84e19efb7fa19469b"}, - {file = "coverage-7.6.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b369ead6527d025a0fe7bd3864e46dbee3aa8f652d48df6174f8d0bac9e26e0e"}, - {file = "coverage-7.6.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ade3ca1e5f0ff46b678b66201f7ff477e8fa11fb537f3b55c3f0568fbfe6e718"}, - {file = "coverage-7.6.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:27fb4a050aaf18772db513091c9c13f6cb94ed40eacdef8dad8411d92d9992db"}, - {file = "coverage-7.6.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4f704f0998911abf728a7783799444fcbbe8261c4a6c166f667937ae6a8aa522"}, - {file = "coverage-7.6.4-cp311-cp311-win32.whl", hash = "sha256:29155cd511ee058e260db648b6182c419422a0d2e9a4fa44501898cf918866cf"}, - {file = "coverage-7.6.4-cp311-cp311-win_amd64.whl", hash = "sha256:8902dd6a30173d4ef09954bfcb24b5d7b5190cf14a43170e386979651e09ba19"}, - {file = "coverage-7.6.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:12394842a3a8affa3ba62b0d4ab7e9e210c5e366fbac3e8b2a68636fb19892c2"}, - {file = "coverage-7.6.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2b6b4c83d8e8ea79f27ab80778c19bc037759aea298da4b56621f4474ffeb117"}, - {file = "coverage-7.6.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d5b8007f81b88696d06f7df0cb9af0d3b835fe0c8dbf489bad70b45f0e45613"}, - {file = "coverage-7.6.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b57b768feb866f44eeed9f46975f3d6406380275c5ddfe22f531a2bf187eda27"}, - {file = "coverage-7.6.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5915fcdec0e54ee229926868e9b08586376cae1f5faa9bbaf8faf3561b393d52"}, - {file = "coverage-7.6.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0b58c672d14f16ed92a48db984612f5ce3836ae7d72cdd161001cc54512571f2"}, - {file = "coverage-7.6.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:2fdef0d83a2d08d69b1f2210a93c416d54e14d9eb398f6ab2f0a209433db19e1"}, - {file = "coverage-7.6.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8cf717ee42012be8c0cb205dbbf18ffa9003c4cbf4ad078db47b95e10748eec5"}, - {file = "coverage-7.6.4-cp312-cp312-win32.whl", hash = "sha256:7bb92c539a624cf86296dd0c68cd5cc286c9eef2d0c3b8b192b604ce9de20a17"}, - {file = "coverage-7.6.4-cp312-cp312-win_amd64.whl", hash = "sha256:1032e178b76a4e2b5b32e19d0fd0abbce4b58e77a1ca695820d10e491fa32b08"}, - {file = "coverage-7.6.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:023bf8ee3ec6d35af9c1c6ccc1d18fa69afa1cb29eaac57cb064dbb262a517f9"}, - {file = "coverage-7.6.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b0ac3d42cb51c4b12df9c5f0dd2f13a4f24f01943627120ec4d293c9181219ba"}, - {file = "coverage-7.6.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8fe4984b431f8621ca53d9380901f62bfb54ff759a1348cd140490ada7b693c"}, - {file = "coverage-7.6.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5fbd612f8a091954a0c8dd4c0b571b973487277d26476f8480bfa4b2a65b5d06"}, - {file = "coverage-7.6.4-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dacbc52de979f2823a819571f2e3a350a7e36b8cb7484cdb1e289bceaf35305f"}, - {file = "coverage-7.6.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:dab4d16dfef34b185032580e2f2f89253d302facba093d5fa9dbe04f569c4f4b"}, - {file = "coverage-7.6.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:862264b12ebb65ad8d863d51f17758b1684560b66ab02770d4f0baf2ff75da21"}, - {file = "coverage-7.6.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5beb1ee382ad32afe424097de57134175fea3faf847b9af002cc7895be4e2a5a"}, - {file = "coverage-7.6.4-cp313-cp313-win32.whl", hash = "sha256:bf20494da9653f6410213424f5f8ad0ed885e01f7e8e59811f572bdb20b8972e"}, - {file = "coverage-7.6.4-cp313-cp313-win_amd64.whl", hash = "sha256:182e6cd5c040cec0a1c8d415a87b67ed01193ed9ad458ee427741c7d8513d963"}, - {file = "coverage-7.6.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a181e99301a0ae128493a24cfe5cfb5b488c4e0bf2f8702091473d033494d04f"}, - {file = "coverage-7.6.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:df57bdbeffe694e7842092c5e2e0bc80fff7f43379d465f932ef36f027179806"}, - {file = "coverage-7.6.4-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bcd1069e710600e8e4cf27f65c90c7843fa8edfb4520fb0ccb88894cad08b11"}, - {file = "coverage-7.6.4-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99b41d18e6b2a48ba949418db48159d7a2e81c5cc290fc934b7d2380515bd0e3"}, - {file = "coverage-7.6.4-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6b1e54712ba3474f34b7ef7a41e65bd9037ad47916ccb1cc78769bae324c01a"}, - {file = "coverage-7.6.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:53d202fd109416ce011578f321460795abfe10bb901b883cafd9b3ef851bacfc"}, - {file = "coverage-7.6.4-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:c48167910a8f644671de9f2083a23630fbf7a1cb70ce939440cd3328e0919f70"}, - {file = "coverage-7.6.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:cc8ff50b50ce532de2fa7a7daae9dd12f0a699bfcd47f20945364e5c31799fef"}, - {file = "coverage-7.6.4-cp313-cp313t-win32.whl", hash = "sha256:b8d3a03d9bfcaf5b0141d07a88456bb6a4c3ce55c080712fec8418ef3610230e"}, - {file = "coverage-7.6.4-cp313-cp313t-win_amd64.whl", hash = "sha256:f3ddf056d3ebcf6ce47bdaf56142af51bb7fad09e4af310241e9db7a3a8022e1"}, - {file = "coverage-7.6.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9cb7fa111d21a6b55cbf633039f7bc2749e74932e3aa7cb7333f675a58a58bf3"}, - {file = "coverage-7.6.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:11a223a14e91a4693d2d0755c7a043db43d96a7450b4f356d506c2562c48642c"}, - {file = "coverage-7.6.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a413a096c4cbac202433c850ee43fa326d2e871b24554da8327b01632673a076"}, - {file = "coverage-7.6.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00a1d69c112ff5149cabe60d2e2ee948752c975d95f1e1096742e6077affd376"}, - {file = "coverage-7.6.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f76846299ba5c54d12c91d776d9605ae33f8ae2b9d1d3c3703cf2db1a67f2c0"}, - {file = "coverage-7.6.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fe439416eb6380de434886b00c859304338f8b19f6f54811984f3420a2e03858"}, - {file = "coverage-7.6.4-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:0294ca37f1ba500667b1aef631e48d875ced93ad5e06fa665a3295bdd1d95111"}, - {file = "coverage-7.6.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6f01ba56b1c0e9d149f9ac85a2f999724895229eb36bd997b61e62999e9b0901"}, - {file = "coverage-7.6.4-cp39-cp39-win32.whl", hash = "sha256:bc66f0bf1d7730a17430a50163bb264ba9ded56739112368ba985ddaa9c3bd09"}, - {file = "coverage-7.6.4-cp39-cp39-win_amd64.whl", hash = "sha256:c481b47f6b5845064c65a7bc78bc0860e635a9b055af0df46fdf1c58cebf8e8f"}, - {file = "coverage-7.6.4-pp39.pp310-none-any.whl", hash = "sha256:3c65d37f3a9ebb703e710befdc489a38683a5b152242664b973a7b7b22348a4e"}, - {file = "coverage-7.6.4.tar.gz", hash = "sha256:29fc0f17b1d3fea332f8001d4558f8214af7f1d87a345f3a133c901d60347c73"}, + {file = "coverage-7.6.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5c912978f7fbf47ef99cec50c4401340436d200d41d714c7a4766f377c5b7b78"}, + {file = "coverage-7.6.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a01ec4af7dfeb96ff0078ad9a48810bb0cc8abcb0115180c6013a6b26237626c"}, + {file = "coverage-7.6.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3b204c11e2b2d883946fe1d97f89403aa1811df28ce0447439178cc7463448a"}, + {file = "coverage-7.6.10-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32ee6d8491fcfc82652a37109f69dee9a830e9379166cb73c16d8dc5c2915165"}, + {file = "coverage-7.6.10-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675cefc4c06e3b4c876b85bfb7c59c5e2218167bbd4da5075cbe3b5790a28988"}, + {file = "coverage-7.6.10-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f4f620668dbc6f5e909a0946a877310fb3d57aea8198bde792aae369ee1c23b5"}, + {file = "coverage-7.6.10-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:4eea95ef275de7abaef630c9b2c002ffbc01918b726a39f5a4353916ec72d2f3"}, + {file = "coverage-7.6.10-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e2f0280519e42b0a17550072861e0bc8a80a0870de260f9796157d3fca2733c5"}, + {file = "coverage-7.6.10-cp310-cp310-win32.whl", hash = "sha256:bc67deb76bc3717f22e765ab3e07ee9c7a5e26b9019ca19a3b063d9f4b874244"}, + {file = "coverage-7.6.10-cp310-cp310-win_amd64.whl", hash = "sha256:0f460286cb94036455e703c66988851d970fdfd8acc2a1122ab7f4f904e4029e"}, + {file = "coverage-7.6.10-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ea3c8f04b3e4af80e17bab607c386a830ffc2fb88a5484e1df756478cf70d1d3"}, + {file = "coverage-7.6.10-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:507a20fc863cae1d5720797761b42d2d87a04b3e5aeb682ef3b7332e90598f43"}, + {file = "coverage-7.6.10-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d37a84878285b903c0fe21ac8794c6dab58150e9359f1aaebbeddd6412d53132"}, + {file = "coverage-7.6.10-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a534738b47b0de1995f85f582d983d94031dffb48ab86c95bdf88dc62212142f"}, + {file = "coverage-7.6.10-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d7a2bf79378d8fb8afaa994f91bfd8215134f8631d27eba3e0e2c13546ce994"}, + {file = "coverage-7.6.10-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6713ba4b4ebc330f3def51df1d5d38fad60b66720948112f114968feb52d3f99"}, + {file = "coverage-7.6.10-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ab32947f481f7e8c763fa2c92fd9f44eeb143e7610c4ca9ecd6a36adab4081bd"}, + {file = "coverage-7.6.10-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7bbd8c8f1b115b892e34ba66a097b915d3871db7ce0e6b9901f462ff3a975377"}, + {file = "coverage-7.6.10-cp311-cp311-win32.whl", hash = "sha256:299e91b274c5c9cdb64cbdf1b3e4a8fe538a7a86acdd08fae52301b28ba297f8"}, + {file = "coverage-7.6.10-cp311-cp311-win_amd64.whl", hash = "sha256:489a01f94aa581dbd961f306e37d75d4ba16104bbfa2b0edb21d29b73be83609"}, + {file = "coverage-7.6.10-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:27c6e64726b307782fa5cbe531e7647aee385a29b2107cd87ba7c0105a5d3853"}, + {file = "coverage-7.6.10-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c56e097019e72c373bae32d946ecf9858fda841e48d82df7e81c63ac25554078"}, + {file = "coverage-7.6.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7827a5bc7bdb197b9e066cdf650b2887597ad124dd99777332776f7b7c7d0d0"}, + {file = "coverage-7.6.10-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:204a8238afe787323a8b47d8be4df89772d5c1e4651b9ffa808552bdf20e1d50"}, + {file = "coverage-7.6.10-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e67926f51821b8e9deb6426ff3164870976fe414d033ad90ea75e7ed0c2e5022"}, + {file = "coverage-7.6.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e78b270eadb5702938c3dbe9367f878249b5ef9a2fcc5360ac7bff694310d17b"}, + {file = "coverage-7.6.10-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:714f942b9c15c3a7a5fe6876ce30af831c2ad4ce902410b7466b662358c852c0"}, + {file = "coverage-7.6.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:abb02e2f5a3187b2ac4cd46b8ced85a0858230b577ccb2c62c81482ca7d18852"}, + {file = "coverage-7.6.10-cp312-cp312-win32.whl", hash = "sha256:55b201b97286cf61f5e76063f9e2a1d8d2972fc2fcfd2c1272530172fd28c359"}, + {file = "coverage-7.6.10-cp312-cp312-win_amd64.whl", hash = "sha256:e4ae5ac5e0d1e4edfc9b4b57b4cbecd5bc266a6915c500f358817a8496739247"}, + {file = "coverage-7.6.10-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:05fca8ba6a87aabdd2d30d0b6c838b50510b56cdcfc604d40760dae7153b73d9"}, + {file = "coverage-7.6.10-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9e80eba8801c386f72e0712a0453431259c45c3249f0009aff537a517b52942b"}, + {file = "coverage-7.6.10-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a372c89c939d57abe09e08c0578c1d212e7a678135d53aa16eec4430adc5e690"}, + {file = "coverage-7.6.10-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ec22b5e7fe7a0fa8509181c4aac1db48f3dd4d3a566131b313d1efc102892c18"}, + {file = "coverage-7.6.10-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26bcf5c4df41cad1b19c84af71c22cbc9ea9a547fc973f1f2cc9a290002c8b3c"}, + {file = "coverage-7.6.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4e4630c26b6084c9b3cb53b15bd488f30ceb50b73c35c5ad7871b869cb7365fd"}, + {file = "coverage-7.6.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2396e8116db77789f819d2bc8a7e200232b7a282c66e0ae2d2cd84581a89757e"}, + {file = "coverage-7.6.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:79109c70cc0882e4d2d002fe69a24aa504dec0cc17169b3c7f41a1d341a73694"}, + {file = "coverage-7.6.10-cp313-cp313-win32.whl", hash = "sha256:9e1747bab246d6ff2c4f28b4d186b205adced9f7bd9dc362051cc37c4a0c7bd6"}, + {file = "coverage-7.6.10-cp313-cp313-win_amd64.whl", hash = "sha256:254f1a3b1eef5f7ed23ef265eaa89c65c8c5b6b257327c149db1ca9d4a35f25e"}, + {file = "coverage-7.6.10-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2ccf240eb719789cedbb9fd1338055de2761088202a9a0b73032857e53f612fe"}, + {file = "coverage-7.6.10-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:0c807ca74d5a5e64427c8805de15b9ca140bba13572d6d74e262f46f50b13273"}, + {file = "coverage-7.6.10-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2bcfa46d7709b5a7ffe089075799b902020b62e7ee56ebaed2f4bdac04c508d8"}, + {file = "coverage-7.6.10-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4e0de1e902669dccbf80b0415fb6b43d27edca2fbd48c74da378923b05316098"}, + {file = "coverage-7.6.10-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f7b444c42bbc533aaae6b5a2166fd1a797cdb5eb58ee51a92bee1eb94a1e1cb"}, + {file = "coverage-7.6.10-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b330368cb99ef72fcd2dc3ed260adf67b31499584dc8a20225e85bfe6f6cfed0"}, + {file = "coverage-7.6.10-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:9a7cfb50515f87f7ed30bc882f68812fd98bc2852957df69f3003d22a2aa0abf"}, + {file = "coverage-7.6.10-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f93531882a5f68c28090f901b1d135de61b56331bba82028489bc51bdd818d2"}, + {file = "coverage-7.6.10-cp313-cp313t-win32.whl", hash = "sha256:89d76815a26197c858f53c7f6a656686ec392b25991f9e409bcef020cd532312"}, + {file = "coverage-7.6.10-cp313-cp313t-win_amd64.whl", hash = "sha256:54a5f0f43950a36312155dae55c505a76cd7f2b12d26abeebbe7a0b36dbc868d"}, + {file = "coverage-7.6.10-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:656c82b8a0ead8bba147de9a89bda95064874c91a3ed43a00e687f23cc19d53a"}, + {file = "coverage-7.6.10-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ccc2b70a7ed475c68ceb548bf69cec1e27305c1c2606a5eb7c3afff56a1b3b27"}, + {file = "coverage-7.6.10-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5e37dc41d57ceba70956fa2fc5b63c26dba863c946ace9705f8eca99daecdc4"}, + {file = "coverage-7.6.10-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0aa9692b4fdd83a4647eeb7db46410ea1322b5ed94cd1715ef09d1d5922ba87f"}, + {file = "coverage-7.6.10-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa744da1820678b475e4ba3dfd994c321c5b13381d1041fe9c608620e6676e25"}, + {file = "coverage-7.6.10-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:c0b1818063dc9e9d838c09e3a473c1422f517889436dd980f5d721899e66f315"}, + {file = "coverage-7.6.10-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:59af35558ba08b758aec4d56182b222976330ef8d2feacbb93964f576a7e7a90"}, + {file = "coverage-7.6.10-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7ed2f37cfce1ce101e6dffdfd1c99e729dd2ffc291d02d3e2d0af8b53d13840d"}, + {file = "coverage-7.6.10-cp39-cp39-win32.whl", hash = "sha256:4bcc276261505d82f0ad426870c3b12cb177752834a633e737ec5ee79bbdff18"}, + {file = "coverage-7.6.10-cp39-cp39-win_amd64.whl", hash = "sha256:457574f4599d2b00f7f637a0700a6422243b3565509457b2dbd3f50703e11f59"}, + {file = "coverage-7.6.10-pp39.pp310-none-any.whl", hash = "sha256:fd34e7b3405f0cc7ab03d54a334c17a9e802897580d964bd8c2001f4b9fd488f"}, + {file = "coverage-7.6.10.tar.gz", hash = "sha256:7fb105327c8f8f0682e29843e2ff96af9dcbe5bab8eeb4b398c6a33a16d80a23"}, ] [package.dependencies] @@ -810,48 +927,6 @@ ssh = ["bcrypt (>=3.1.5)"] test = ["certifi", "cryptography-vectors (==43.0.3)", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] test-randomorder = ["pytest-randomly"] -[[package]] -name = "datasets" -version = "2.14.4" -description = "HuggingFace community-driven open-source library of datasets" -optional = true -python-versions = ">=3.8.0" -files = [ - {file = "datasets-2.14.4-py3-none-any.whl", hash = "sha256:29336bd316a7d827ccd4da2236596279b20ca2ac78f64c04c9483da7cbc2459b"}, - {file = "datasets-2.14.4.tar.gz", hash = "sha256:ef29c2b5841de488cd343cfc26ab979bff77efa4d2285af51f1ad7db5c46a83b"}, -] - -[package.dependencies] -aiohttp = "*" -dill = ">=0.3.0,<0.3.8" -fsspec = {version = ">=2021.11.1", extras = ["http"]} -huggingface-hub = ">=0.14.0,<1.0.0" -multiprocess = "*" -numpy = ">=1.17" -packaging = "*" -pandas = "*" -pyarrow = ">=8.0.0" -pyyaml = ">=5.1" -requests = ">=2.19.0" -tqdm = ">=4.62.1" -xxhash = "*" - -[package.extras] -apache-beam = ["apache-beam (>=2.26.0,<2.44.0)"] -audio = ["librosa", "soundfile (>=0.12.1)"] -benchmarks = ["tensorflow (==2.12.0)", "torch (==2.0.1)", "transformers (==4.30.1)"] -dev = ["Pillow (>=6.2.1)", "absl-py", "apache-beam (>=2.26.0,<2.44.0)", "black (>=23.1,<24.0)", "elasticsearch (<8.0.0)", "faiss-cpu (>=1.6.4)", "joblib (<1.3.0)", "joblibspark", "librosa", "lz4", "py7zr", "pyspark (>=3.4)", "pytest", "pytest-datadir", "pytest-xdist", "pyyaml (>=5.3.1)", "rarfile (>=4.0)", "ruff (>=0.0.241)", "s3fs", "s3fs (>=2021.11.1)", "soundfile (>=0.12.1)", "sqlalchemy (<2.0.0)", "tensorflow (>=2.2.0,!=2.6.0,!=2.6.1)", "tensorflow (>=2.3,!=2.6.0,!=2.6.1)", "tensorflow-macos", "tiktoken", "torch", "transformers", "zstandard"] -docs = ["s3fs", "tensorflow (>=2.2.0,!=2.6.0,!=2.6.1)", "tensorflow-macos", "torch", "transformers"] -jax = ["jax (>=0.2.8,!=0.3.2,<=0.3.25)", "jaxlib (>=0.1.65,<=0.3.25)"] -metrics-tests = ["Werkzeug (>=1.0.1)", "accelerate", "bert-score (>=0.3.6)", "jiwer", "langdetect", "mauve-text", "nltk", "requests-file (>=1.5.1)", "rouge-score", "sacrebleu", "sacremoses", "scikit-learn", "scipy", "sentencepiece", "seqeval", "six (>=1.15.0,<1.16.0)", "spacy (>=3.0.0)", "texttable (>=1.6.3)", "tldextract", "tldextract (>=3.1.0)", "toml (>=0.10.1)", "typer (<0.5.0)"] -quality = ["black (>=23.1,<24.0)", "pyyaml (>=5.3.1)", "ruff (>=0.0.241)"] -s3 = ["s3fs"] -tensorflow = ["tensorflow (>=2.2.0,!=2.6.0,!=2.6.1)", "tensorflow-macos"] -tensorflow-gpu = ["tensorflow-gpu (>=2.2.0,!=2.6.0,!=2.6.1)"] -tests = ["Pillow (>=6.2.1)", "absl-py", "apache-beam (>=2.26.0,<2.44.0)", "elasticsearch (<8.0.0)", "faiss-cpu (>=1.6.4)", "joblib (<1.3.0)", "joblibspark", "librosa", "lz4", "py7zr", "pyspark (>=3.4)", "pytest", "pytest-datadir", "pytest-xdist", "rarfile (>=4.0)", "s3fs (>=2021.11.1)", "soundfile (>=0.12.1)", "sqlalchemy (<2.0.0)", "tensorflow (>=2.3,!=2.6.0,!=2.6.1)", "tensorflow-macos", "tiktoken", "torch", "transformers", "zstandard"] -torch = ["torch"] -vision = ["Pillow (>=6.2.1)"] - [[package]] name = "deprecation" version = "2.1.0" @@ -866,19 +941,38 @@ files = [ [package.dependencies] packaging = "*" +[[package]] +name = "depyf" +version = "0.18.0" +description = "Decompile python functions, from bytecode to source code!" +optional = true +python-versions = ">=3.7" +files = [ + {file = "depyf-0.18.0-py3-none-any.whl", hash = "sha256:007294d5bac19a38a0767d747be0f49b9ffdcea0394a822644142df22b33a3e1"}, + {file = "depyf-0.18.0.tar.gz", hash = "sha256:b99f0c383be949ae45d5d606fe444c71f375b55a57b8d6b20e7856670d52130d"}, +] + +[package.dependencies] +astor = "*" +dill = "*" + +[package.extras] +dev = ["autopep8", "flake8", "pytest"] + [[package]] name = "dill" -version = "0.3.7" +version = "0.3.9" description = "serialize all of Python" optional = true -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "dill-0.3.7-py3-none-any.whl", hash = "sha256:76b122c08ef4ce2eedcd4d1abd8e641114bfc6c2867f49f3c41facf65bf19f5e"}, - {file = "dill-0.3.7.tar.gz", hash = "sha256:cc1c8b182eb3013e24bd475ff2e9295af86c1a38eb1aff128dac8962a9ce3c03"}, + {file = "dill-0.3.9-py3-none-any.whl", hash = "sha256:468dff3b89520b474c0397703366b7b95eebe6303f108adf9b19da1f702be87a"}, + {file = "dill-0.3.9.tar.gz", hash = "sha256:81aa267dddf68cbfe8029c42ca9ec6a4ab3b22371d1c450abc54422577b4512c"}, ] [package.extras] graph = ["objgraph (>=1.7.2)"] +profile = ["gprof2dot (>=2022.7.29)"] [[package]] name = "diskcache" @@ -891,6 +985,17 @@ files = [ {file = "diskcache-5.6.3.tar.gz", hash = "sha256:2c3a3fa2743d8535d832ec61c2054a1641f41775aa7c556758a109941e33e4fc"}, ] +[[package]] +name = "distlib" +version = "0.3.9" +description = "Distribution utilities" +optional = true +python-versions = "*" +files = [ + {file = "distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87"}, + {file = "distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403"}, +] + [[package]] name = "distro" version = "1.9.0" @@ -940,13 +1045,13 @@ test = ["pytest (>=6)"] [[package]] name = "fastapi" -version = "0.115.5" +version = "0.115.6" description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" optional = false python-versions = ">=3.8" files = [ - {file = "fastapi-0.115.5-py3-none-any.whl", hash = "sha256:596b95adbe1474da47049e802f9a65ab2ffa9c2b07e7efee70eb8a66c9f2f796"}, - {file = "fastapi-0.115.5.tar.gz", hash = "sha256:0e7a4d0dc0d01c68df21887cce0945e72d3c48b9f4f79dfe7a7d53aa08fbb289"}, + {file = "fastapi-0.115.6-py3-none-any.whl", hash = "sha256:e9240b29e36fa8f4bb7290316988e90c381e5092e0cbe84e7818cc3713bcf305"}, + {file = "fastapi-0.115.6.tar.gz", hash = "sha256:9ec46f7addc14ea472958a96aae5b5de65f39721a46aaf5705c480d9a8b76654"}, ] [package.dependencies] @@ -1077,18 +1182,15 @@ files = [ [[package]] name = "fsspec" -version = "2024.10.0" +version = "2024.12.0" description = "File-system specification" optional = false python-versions = ">=3.8" files = [ - {file = "fsspec-2024.10.0-py3-none-any.whl", hash = "sha256:03b9a6785766a4de40368b88906366755e2819e758b83705c88cd7cb5fe81871"}, - {file = "fsspec-2024.10.0.tar.gz", hash = "sha256:eda2d8a4116d4f2429db8550f2457da57279247dd930bb12f821b58391359493"}, + {file = "fsspec-2024.12.0-py3-none-any.whl", hash = "sha256:b520aed47ad9804237ff878b504267a3b0b441e97508bd6d2d8774e3db85cee2"}, + {file = "fsspec-2024.12.0.tar.gz", hash = "sha256:670700c977ed2fb51e0d9f9253177ed20cbde4a3e5c0283cc5385b5870c8533f"}, ] -[package.dependencies] -aiohttp = {version = "<4.0.0a0 || >4.0.0a0,<4.0.0a1 || >4.0.0a1", optional = true, markers = "extra == \"http\""} - [package.extras] abfs = ["adlfs"] adl = ["adlfs"] @@ -1135,13 +1237,13 @@ tqdm = ">=4.27" [[package]] name = "google-api-core" -version = "2.21.0" +version = "2.24.0" description = "Google API client core library" optional = false python-versions = ">=3.7" files = [ - {file = "google_api_core-2.21.0-py3-none-any.whl", hash = "sha256:6869eacb2a37720380ba5898312af79a4d30b8bca1548fb4093e0697dc4bdf5d"}, - {file = "google_api_core-2.21.0.tar.gz", hash = "sha256:4a152fd11a9f774ea606388d423b68aa7e6d6a0ffe4c8266f74979613ec09f81"}, + {file = "google_api_core-2.24.0-py3-none-any.whl", hash = "sha256:10d82ac0fca69c82a25b3efdeefccf6f28e02ebb97925a8cce8edbfe379929d9"}, + {file = "google_api_core-2.24.0.tar.gz", hash = "sha256:e255640547a597a4da010876d333208ddac417d60add22b6851a0c66a831fcaf"}, ] [package.dependencies] @@ -1159,13 +1261,13 @@ grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] [[package]] name = "google-auth" -version = "2.35.0" +version = "2.37.0" description = "Google Authentication Library" optional = false python-versions = ">=3.7" files = [ - {file = "google_auth-2.35.0-py2.py3-none-any.whl", hash = "sha256:25df55f327ef021de8be50bad0dfd4a916ad0de96da86cd05661c9297723ad3f"}, - {file = "google_auth-2.35.0.tar.gz", hash = "sha256:f4c64ed4e01e8e8b646ef34c018f8bf3338df0c8e37d8b3bba40e7f574a3278a"}, + {file = "google_auth-2.37.0-py2.py3-none-any.whl", hash = "sha256:42664f18290a6be591be5329a96fe30184be1a1badb7292a7f686a9659de9ca0"}, + {file = "google_auth-2.37.0.tar.gz", hash = "sha256:0054623abf1f9c83492c63d3f47e77f0a544caa3d40b2d98e099a611c2dd5d00"}, ] [package.dependencies] @@ -1176,6 +1278,7 @@ rsa = ">=3.1.4,<5" [package.extras] aiohttp = ["aiohttp (>=3.6.2,<4.0.0.dev0)", "requests (>=2.20.0,<3.0.0.dev0)"] enterprise-cert = ["cryptography", "pyopenssl"] +pyjwt = ["cryptography (>=38.0.3)", "pyjwt (>=2.0)"] pyopenssl = ["cryptography (>=38.0.3)", "pyopenssl (>=20.0.0)"] reauth = ["pyu2f (>=0.1.5)"] requests = ["requests (>=2.20.0,<3.0.0.dev0)"] @@ -1200,13 +1303,13 @@ grpc = ["grpcio (>=1.38.0,<2.0dev)", "grpcio-status (>=1.38.0,<2.0.dev0)"] [[package]] name = "google-cloud-storage" -version = "2.18.2" +version = "2.19.0" description = "Google Cloud Storage API client library" optional = false python-versions = ">=3.7" files = [ - {file = "google_cloud_storage-2.18.2-py2.py3-none-any.whl", hash = "sha256:97a4d45c368b7d401ed48c4fdfe86e1e1cb96401c9e199e419d289e2c0370166"}, - {file = "google_cloud_storage-2.18.2.tar.gz", hash = "sha256:aaf7acd70cdad9f274d29332673fcab98708d0e1f4dceb5a5356aaef06af4d99"}, + {file = "google_cloud_storage-2.19.0-py2.py3-none-any.whl", hash = "sha256:aeb971b5c29cf8ab98445082cbfe7b161a1f48ed275822f59ed3f1524ea54fba"}, + {file = "google_cloud_storage-2.19.0.tar.gz", hash = "sha256:cd05e9e7191ba6cb68934d8eb76054d9be4562aa89dbc4236feee4d7d51342b2"}, ] [package.dependencies] @@ -1280,13 +1383,13 @@ requests = ["requests (>=2.18.0,<3.0.0dev)"] [[package]] name = "googleapis-common-protos" -version = "1.65.0" +version = "1.66.0" description = "Common protobufs used in Google APIs" optional = false python-versions = ">=3.7" files = [ - {file = "googleapis_common_protos-1.65.0-py2.py3-none-any.whl", hash = "sha256:2972e6c496f435b92590fd54045060867f3fe9be2c82ab148fc8885035479a63"}, - {file = "googleapis_common_protos-1.65.0.tar.gz", hash = "sha256:334a29d07cddc3aa01dee4988f9afd9b2916ee2ff49d6b757155dc0d197852c0"}, + {file = "googleapis_common_protos-1.66.0-py2.py3-none-any.whl", hash = "sha256:d7abcd75fabb2e0ec9f74466401f6c119a0b498e27370e9be4c94cb7e382b8ed"}, + {file = "googleapis_common_protos-1.66.0.tar.gz", hash = "sha256:c3e7b33d15fdca5374cc0a7346dd92ffa847425cc4ea941d970f13680052ec8c"}, ] [package.dependencies] @@ -1314,70 +1417,70 @@ testing = ["protobuf (>=4.21.9)"] [[package]] name = "grpcio" -version = "1.67.0" +version = "1.69.0" description = "HTTP/2-based RPC framework" optional = false python-versions = ">=3.8" files = [ - {file = "grpcio-1.67.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:bd79929b3bb96b54df1296cd3bf4d2b770bd1df6c2bdf549b49bab286b925cdc"}, - {file = "grpcio-1.67.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:16724ffc956ea42967f5758c2f043faef43cb7e48a51948ab593570570d1e68b"}, - {file = "grpcio-1.67.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:2b7183c80b602b0ad816315d66f2fb7887614ead950416d60913a9a71c12560d"}, - {file = "grpcio-1.67.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:efe32b45dd6d118f5ea2e5deaed417d8a14976325c93812dd831908522b402c9"}, - {file = "grpcio-1.67.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe89295219b9c9e47780a0f1c75ca44211e706d1c598242249fe717af3385ec8"}, - {file = "grpcio-1.67.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aa8d025fae1595a207b4e47c2e087cb88d47008494db258ac561c00877d4c8f8"}, - {file = "grpcio-1.67.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f95e15db43e75a534420e04822df91f645664bf4ad21dfaad7d51773c80e6bb4"}, - {file = "grpcio-1.67.0-cp310-cp310-win32.whl", hash = "sha256:a6b9a5c18863fd4b6624a42e2712103fb0f57799a3b29651c0e5b8119a519d65"}, - {file = "grpcio-1.67.0-cp310-cp310-win_amd64.whl", hash = "sha256:b6eb68493a05d38b426604e1dc93bfc0137c4157f7ab4fac5771fd9a104bbaa6"}, - {file = "grpcio-1.67.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:e91d154689639932305b6ea6f45c6e46bb51ecc8ea77c10ef25aa77f75443ad4"}, - {file = "grpcio-1.67.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:cb204a742997277da678611a809a8409657b1398aaeebf73b3d9563b7d154c13"}, - {file = "grpcio-1.67.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:ae6de510f670137e755eb2a74b04d1041e7210af2444103c8c95f193340d17ee"}, - {file = "grpcio-1.67.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:74b900566bdf68241118f2918d312d3bf554b2ce0b12b90178091ea7d0a17b3d"}, - {file = "grpcio-1.67.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4e95e43447a02aa603abcc6b5e727d093d161a869c83b073f50b9390ecf0fa8"}, - {file = "grpcio-1.67.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:0bb94e66cd8f0baf29bd3184b6aa09aeb1a660f9ec3d85da615c5003154bc2bf"}, - {file = "grpcio-1.67.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:82e5bd4b67b17c8c597273663794a6a46a45e44165b960517fe6d8a2f7f16d23"}, - {file = "grpcio-1.67.0-cp311-cp311-win32.whl", hash = "sha256:7fc1d2b9fd549264ae585026b266ac2db53735510a207381be509c315b4af4e8"}, - {file = "grpcio-1.67.0-cp311-cp311-win_amd64.whl", hash = "sha256:ac11ecb34a86b831239cc38245403a8de25037b448464f95c3315819e7519772"}, - {file = "grpcio-1.67.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:227316b5631260e0bef8a3ce04fa7db4cc81756fea1258b007950b6efc90c05d"}, - {file = "grpcio-1.67.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d90cfdafcf4b45a7a076e3e2a58e7bc3d59c698c4f6470b0bb13a4d869cf2273"}, - {file = "grpcio-1.67.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:77196216d5dd6f99af1c51e235af2dd339159f657280e65ce7e12c1a8feffd1d"}, - {file = "grpcio-1.67.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:15c05a26a0f7047f720da41dc49406b395c1470eef44ff7e2c506a47ac2c0591"}, - {file = "grpcio-1.67.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3840994689cc8cbb73d60485c594424ad8adb56c71a30d8948d6453083624b52"}, - {file = "grpcio-1.67.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:5a1e03c3102b6451028d5dc9f8591131d6ab3c8a0e023d94c28cb930ed4b5f81"}, - {file = "grpcio-1.67.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:682968427a63d898759474e3b3178d42546e878fdce034fd7474ef75143b64e3"}, - {file = "grpcio-1.67.0-cp312-cp312-win32.whl", hash = "sha256:d01793653248f49cf47e5695e0a79805b1d9d4eacef85b310118ba1dfcd1b955"}, - {file = "grpcio-1.67.0-cp312-cp312-win_amd64.whl", hash = "sha256:985b2686f786f3e20326c4367eebdaed3e7aa65848260ff0c6644f817042cb15"}, - {file = "grpcio-1.67.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:8c9a35b8bc50db35ab8e3e02a4f2a35cfba46c8705c3911c34ce343bd777813a"}, - {file = "grpcio-1.67.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:42199e704095b62688998c2d84c89e59a26a7d5d32eed86d43dc90e7a3bd04aa"}, - {file = "grpcio-1.67.0-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:c4c425f440fb81f8d0237c07b9322fc0fb6ee2b29fbef5f62a322ff8fcce240d"}, - {file = "grpcio-1.67.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:323741b6699cd2b04a71cb38f502db98f90532e8a40cb675393d248126a268af"}, - {file = "grpcio-1.67.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:662c8e105c5e5cee0317d500eb186ed7a93229586e431c1bf0c9236c2407352c"}, - {file = "grpcio-1.67.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:f6bd2ab135c64a4d1e9e44679a616c9bc944547357c830fafea5c3caa3de5153"}, - {file = "grpcio-1.67.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:2f55c1e0e2ae9bdd23b3c63459ee4c06d223b68aeb1961d83c48fb63dc29bc03"}, - {file = "grpcio-1.67.0-cp313-cp313-win32.whl", hash = "sha256:fd6bc27861e460fe28e94226e3673d46e294ca4673d46b224428d197c5935e69"}, - {file = "grpcio-1.67.0-cp313-cp313-win_amd64.whl", hash = "sha256:cf51d28063338608cd8d3cd64677e922134837902b70ce00dad7f116e3998210"}, - {file = "grpcio-1.67.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:7f200aca719c1c5dc72ab68be3479b9dafccdf03df530d137632c534bb6f1ee3"}, - {file = "grpcio-1.67.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0892dd200ece4822d72dd0952f7112c542a487fc48fe77568deaaa399c1e717d"}, - {file = "grpcio-1.67.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:f4d613fbf868b2e2444f490d18af472ccb47660ea3df52f068c9c8801e1f3e85"}, - {file = "grpcio-1.67.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c69bf11894cad9da00047f46584d5758d6ebc9b5950c0dc96fec7e0bce5cde9"}, - {file = "grpcio-1.67.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9bca3ca0c5e74dea44bf57d27e15a3a3996ce7e5780d61b7c72386356d231db"}, - {file = "grpcio-1.67.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:014dfc020e28a0d9be7e93a91f85ff9f4a87158b7df9952fe23cc42d29d31e1e"}, - {file = "grpcio-1.67.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d4ea4509d42c6797539e9ec7496c15473177ce9abc89bc5c71e7abe50fc25737"}, - {file = "grpcio-1.67.0-cp38-cp38-win32.whl", hash = "sha256:9d75641a2fca9ae1ae86454fd25d4c298ea8cc195dbc962852234d54a07060ad"}, - {file = "grpcio-1.67.0-cp38-cp38-win_amd64.whl", hash = "sha256:cff8e54d6a463883cda2fab94d2062aad2f5edd7f06ae3ed030f2a74756db365"}, - {file = "grpcio-1.67.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:62492bd534979e6d7127b8a6b29093161a742dee3875873e01964049d5250a74"}, - {file = "grpcio-1.67.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eef1dce9d1a46119fd09f9a992cf6ab9d9178b696382439446ca5f399d7b96fe"}, - {file = "grpcio-1.67.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:f623c57a5321461c84498a99dddf9d13dac0e40ee056d884d6ec4ebcab647a78"}, - {file = "grpcio-1.67.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54d16383044e681f8beb50f905249e4e7261dd169d4aaf6e52eab67b01cbbbe2"}, - {file = "grpcio-1.67.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2a44e572fb762c668e4812156b81835f7aba8a721b027e2d4bb29fb50ff4d33"}, - {file = "grpcio-1.67.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:391df8b0faac84d42f5b8dfc65f5152c48ed914e13c522fd05f2aca211f8bfad"}, - {file = "grpcio-1.67.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cfd9306511fdfc623a1ba1dc3bc07fbd24e6cfbe3c28b4d1e05177baa2f99617"}, - {file = "grpcio-1.67.0-cp39-cp39-win32.whl", hash = "sha256:30d47dbacfd20cbd0c8be9bfa52fdb833b395d4ec32fe5cff7220afc05d08571"}, - {file = "grpcio-1.67.0-cp39-cp39-win_amd64.whl", hash = "sha256:f55f077685f61f0fbd06ea355142b71e47e4a26d2d678b3ba27248abfe67163a"}, - {file = "grpcio-1.67.0.tar.gz", hash = "sha256:e090b2553e0da1c875449c8e75073dd4415dd71c9bde6a406240fdf4c0ee467c"}, + {file = "grpcio-1.69.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:2060ca95a8db295ae828d0fc1c7f38fb26ccd5edf9aa51a0f44251f5da332e97"}, + {file = "grpcio-1.69.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:2e52e107261fd8fa8fa457fe44bfadb904ae869d87c1280bf60f93ecd3e79278"}, + {file = "grpcio-1.69.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:316463c0832d5fcdb5e35ff2826d9aa3f26758d29cdfb59a368c1d6c39615a11"}, + {file = "grpcio-1.69.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:26c9a9c4ac917efab4704b18eed9082ed3b6ad19595f047e8173b5182fec0d5e"}, + {file = "grpcio-1.69.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90b3646ced2eae3a0599658eeccc5ba7f303bf51b82514c50715bdd2b109e5ec"}, + {file = "grpcio-1.69.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:3b75aea7c6cb91b341c85e7c1d9db1e09e1dd630b0717f836be94971e015031e"}, + {file = "grpcio-1.69.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5cfd14175f9db33d4b74d63de87c64bb0ee29ce475ce3c00c01ad2a3dc2a9e51"}, + {file = "grpcio-1.69.0-cp310-cp310-win32.whl", hash = "sha256:9031069d36cb949205293cf0e243abd5e64d6c93e01b078c37921493a41b72dc"}, + {file = "grpcio-1.69.0-cp310-cp310-win_amd64.whl", hash = "sha256:cc89b6c29f3dccbe12d7a3b3f1b3999db4882ae076c1c1f6df231d55dbd767a5"}, + {file = "grpcio-1.69.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:8de1b192c29b8ce45ee26a700044717bcbbd21c697fa1124d440548964328561"}, + {file = "grpcio-1.69.0-cp311-cp311-macosx_10_14_universal2.whl", hash = "sha256:7e76accf38808f5c5c752b0ab3fd919eb14ff8fafb8db520ad1cc12afff74de6"}, + {file = "grpcio-1.69.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:d5658c3c2660417d82db51e168b277e0ff036d0b0f859fa7576c0ffd2aec1442"}, + {file = "grpcio-1.69.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5494d0e52bf77a2f7eb17c6da662886ca0a731e56c1c85b93505bece8dc6cf4c"}, + {file = "grpcio-1.69.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ed866f9edb574fd9be71bf64c954ce1b88fc93b2a4cbf94af221e9426eb14d6"}, + {file = "grpcio-1.69.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c5ba38aeac7a2fe353615c6b4213d1fbb3a3c34f86b4aaa8be08baaaee8cc56d"}, + {file = "grpcio-1.69.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f79e05f5bbf551c4057c227d1b041ace0e78462ac8128e2ad39ec58a382536d2"}, + {file = "grpcio-1.69.0-cp311-cp311-win32.whl", hash = "sha256:bf1f8be0da3fcdb2c1e9f374f3c2d043d606d69f425cd685110dd6d0d2d61258"}, + {file = "grpcio-1.69.0-cp311-cp311-win_amd64.whl", hash = "sha256:fb9302afc3a0e4ba0b225cd651ef8e478bf0070cf11a529175caecd5ea2474e7"}, + {file = "grpcio-1.69.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:fc18a4de8c33491ad6f70022af5c460b39611e39578a4d84de0fe92f12d5d47b"}, + {file = "grpcio-1.69.0-cp312-cp312-macosx_10_14_universal2.whl", hash = "sha256:0f0270bd9ffbff6961fe1da487bdcd594407ad390cc7960e738725d4807b18c4"}, + {file = "grpcio-1.69.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:dc48f99cc05e0698e689b51a05933253c69a8c8559a47f605cff83801b03af0e"}, + {file = "grpcio-1.69.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e925954b18d41aeb5ae250262116d0970893b38232689c4240024e4333ac084"}, + {file = "grpcio-1.69.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87d222569273720366f68a99cb62e6194681eb763ee1d3b1005840678d4884f9"}, + {file = "grpcio-1.69.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:b62b0f41e6e01a3e5082000b612064c87c93a49b05f7602fe1b7aa9fd5171a1d"}, + {file = "grpcio-1.69.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:db6f9fd2578dbe37db4b2994c94a1d9c93552ed77dca80e1657bb8a05b898b55"}, + {file = "grpcio-1.69.0-cp312-cp312-win32.whl", hash = "sha256:b192b81076073ed46f4b4dd612b8897d9a1e39d4eabd822e5da7b38497ed77e1"}, + {file = "grpcio-1.69.0-cp312-cp312-win_amd64.whl", hash = "sha256:1227ff7836f7b3a4ab04e5754f1d001fa52a730685d3dc894ed8bc262cc96c01"}, + {file = "grpcio-1.69.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:a78a06911d4081a24a1761d16215a08e9b6d4d29cdbb7e427e6c7e17b06bcc5d"}, + {file = "grpcio-1.69.0-cp313-cp313-macosx_10_14_universal2.whl", hash = "sha256:dc5a351927d605b2721cbb46158e431dd49ce66ffbacb03e709dc07a491dde35"}, + {file = "grpcio-1.69.0-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:3629d8a8185f5139869a6a17865d03113a260e311e78fbe313f1a71603617589"}, + {file = "grpcio-1.69.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9a281878feeb9ae26db0622a19add03922a028d4db684658f16d546601a4870"}, + {file = "grpcio-1.69.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cc614e895177ab7e4b70f154d1a7c97e152577ea101d76026d132b7aaba003b"}, + {file = "grpcio-1.69.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:1ee76cd7e2e49cf9264f6812d8c9ac1b85dda0eaea063af07292400f9191750e"}, + {file = "grpcio-1.69.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:0470fa911c503af59ec8bc4c82b371ee4303ececbbdc055f55ce48e38b20fd67"}, + {file = "grpcio-1.69.0-cp313-cp313-win32.whl", hash = "sha256:b650f34aceac8b2d08a4c8d7dc3e8a593f4d9e26d86751ebf74ebf5107d927de"}, + {file = "grpcio-1.69.0-cp313-cp313-win_amd64.whl", hash = "sha256:028337786f11fecb5d7b7fa660475a06aabf7e5e52b5ac2df47414878c0ce7ea"}, + {file = "grpcio-1.69.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:b7f693db593d6bf285e015d5538bf1c86cf9c60ed30b6f7da04a00ed052fe2f3"}, + {file = "grpcio-1.69.0-cp38-cp38-macosx_10_14_universal2.whl", hash = "sha256:8b94e83f66dbf6fd642415faca0608590bc5e8d30e2c012b31d7d1b91b1de2fd"}, + {file = "grpcio-1.69.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:b634851b92c090763dde61df0868c730376cdb73a91bcc821af56ae043b09596"}, + {file = "grpcio-1.69.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bf5f680d3ed08c15330d7830d06bc65f58ca40c9999309517fd62880d70cb06e"}, + {file = "grpcio-1.69.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:200e48a6e7b00f804cf00a1c26292a5baa96507c7749e70a3ec10ca1a288936e"}, + {file = "grpcio-1.69.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:45a4704339b6e5b24b0e136dea9ad3815a94f30eb4f1e1d44c4ac484ef11d8dd"}, + {file = "grpcio-1.69.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:85d347cb8237751b23539981dbd2d9d8f6e9ff90082b427b13022b948eb6347a"}, + {file = "grpcio-1.69.0-cp38-cp38-win32.whl", hash = "sha256:60e5de105dc02832dc8f120056306d0ef80932bcf1c0e2b4ca3b676de6dc6505"}, + {file = "grpcio-1.69.0-cp38-cp38-win_amd64.whl", hash = "sha256:282f47d0928e40f25d007f24eb8fa051cb22551e3c74b8248bc9f9bea9c35fe0"}, + {file = "grpcio-1.69.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:dd034d68a2905464c49479b0c209c773737a4245d616234c79c975c7c90eca03"}, + {file = "grpcio-1.69.0-cp39-cp39-macosx_10_14_universal2.whl", hash = "sha256:01f834732c22a130bdf3dc154d1053bdbc887eb3ccb7f3e6285cfbfc33d9d5cc"}, + {file = "grpcio-1.69.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:a7f4ed0dcf202a70fe661329f8874bc3775c14bb3911d020d07c82c766ce0eb1"}, + {file = "grpcio-1.69.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cd7ea241b10bc5f0bb0f82c0d7896822b7ed122b3ab35c9851b440c1ccf81588"}, + {file = "grpcio-1.69.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f03dc9b4da4c0dc8a1db7a5420f575251d7319b7a839004d8916257ddbe4816"}, + {file = "grpcio-1.69.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:ca71d73a270dff052fe4edf74fef142d6ddd1f84175d9ac4a14b7280572ac519"}, + {file = "grpcio-1.69.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5ccbed100dc43704e94ccff9e07680b540d64e4cc89213ab2832b51b4f68a520"}, + {file = "grpcio-1.69.0-cp39-cp39-win32.whl", hash = "sha256:1514341def9c6ec4b7f0b9628be95f620f9d4b99331b7ef0a1845fd33d9b579c"}, + {file = "grpcio-1.69.0-cp39-cp39-win_amd64.whl", hash = "sha256:c1fea55d26d647346acb0069b08dca70984101f2dc95066e003019207212e303"}, + {file = "grpcio-1.69.0.tar.gz", hash = "sha256:936fa44241b5379c5afc344e1260d467bee495747eaf478de825bab2791da6f5"}, ] [package.extras] -protobuf = ["grpcio-tools (>=1.67.0)"] +protobuf = ["grpcio-tools (>=1.69.0)"] [[package]] name = "h11" @@ -1392,77 +1495,47 @@ files = [ [[package]] name = "hf-transfer" -version = "0.1.8" +version = "0.1.9" description = "Speed up file transfers with the Hugging Face Hub." optional = false python-versions = ">=3.7" files = [ - {file = "hf_transfer-0.1.8-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:70858f9e94286738ed300484a45beb5cfee6a7ddac4c5886f9c6fce7823ac5ab"}, - {file = "hf_transfer-0.1.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:38adc73f0a8526319d90f7cc5dc2d5e4bb66f487a513d94b98aa6725be732e4a"}, - {file = "hf_transfer-0.1.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:44d2f0c08198d8d899fe9d66e86aee2dd844bd7ce33888f261373fcec81d2a54"}, - {file = "hf_transfer-0.1.8-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1de2a4ef36f9e60b3d3bec00193c0aafd75771709f2ca51b9b162373f5af3d32"}, - {file = "hf_transfer-0.1.8-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e319269e3606a5ff2979296841766649ac73598a4a8eee2a968f86c8071fea5a"}, - {file = "hf_transfer-0.1.8-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0f6026cf3be6a53ea42f92172f60c1c0675baaa9073f865e671b661dde5fd157"}, - {file = "hf_transfer-0.1.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f865c33ada5bd3650c2b46e59979f2d7755c3f517f8d0facc78576a0c7d26406"}, - {file = "hf_transfer-0.1.8-cp310-none-win32.whl", hash = "sha256:2054730e8d8ed21917c64be7199e06424b2bd08df1c43a72766afaed7992f2d3"}, - {file = "hf_transfer-0.1.8-cp310-none-win_amd64.whl", hash = "sha256:2b4f1a9446ba31170b5b1eca4e916504d18378a6b5fe959896bdac8a736a5ecb"}, - {file = "hf_transfer-0.1.8-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:e27c15fcc5869ad7e52bbc0bdec6106b288d1c463f8d2da92f28615a3b181361"}, - {file = "hf_transfer-0.1.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:871a0032d011ebc6409a73a8406b98b84ff2cd3ed7d9e1af8cdf4d660b9fab9b"}, - {file = "hf_transfer-0.1.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:686fa756e1e0214bb6327d33c66732c52274d94a8460beb50604ad988b391cf6"}, - {file = "hf_transfer-0.1.8-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:36a03b1b2911b0cf15b1b9d971a34b32dadcc4f2fd979aaff5979d6ce4017c34"}, - {file = "hf_transfer-0.1.8-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:079db90c81f41f4cf3227dfaaa855a9b8e9aef45bc7c2be29ce7232cd83ff881"}, - {file = "hf_transfer-0.1.8-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac08a4524127fdd14c234d4bcbe49d1c498acf5335c781714823179bcc8dc039"}, - {file = "hf_transfer-0.1.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:837432e73cb17274a6782b6216e8ce058aa325a475dc44a5a6a753d48b86d18a"}, - {file = "hf_transfer-0.1.8-cp311-none-win32.whl", hash = "sha256:b180f9823dde35aba9bc0f1d0c04ac8a873baebd3732a7ffe4f11940abc7df0d"}, - {file = "hf_transfer-0.1.8-cp311-none-win_amd64.whl", hash = "sha256:37907d2135cebcf8b6d419bb575148d89c224f16b69357f027bd29d0e85c6529"}, - {file = "hf_transfer-0.1.8-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:baf948f4f493949309cbe60529620b9b0aef854a22b6e526753364acc57c09b6"}, - {file = "hf_transfer-0.1.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0bce5c8bdefa478c5d5eaa646cc4ce1df5cfe764d98572ad0c6b8773e98d49f6"}, - {file = "hf_transfer-0.1.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54d6f8a1a86128d651a3799e1267c343d60f81f2c565d7c5416eb8e674e4cf0e"}, - {file = "hf_transfer-0.1.8-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f79fd1b0c2ed93efb4c5f684118d7a762ecdd218e170df8208c4e13d3dcd4959"}, - {file = "hf_transfer-0.1.8-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:414df35692670683bf5623498ef9d88a8df5d77e9516515da6e2b34d1054c11f"}, - {file = "hf_transfer-0.1.8-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3c9798d5f951f66b96d40a7a53910260cb5874fda56cf5944dddb7c571f37ec3"}, - {file = "hf_transfer-0.1.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:060c661691f85a61392e57579c80eb64b5ee277434e81fb582f605c1c8ff05d5"}, - {file = "hf_transfer-0.1.8-cp312-none-win32.whl", hash = "sha256:f7840e32379820c3e1571a480238e05ea043e970c99d2e999578004a2eb17788"}, - {file = "hf_transfer-0.1.8-cp312-none-win_amd64.whl", hash = "sha256:9a3204ec423cc5e659872e8179f8704ad9ce2abb1e6a991f8838aedf1dc07830"}, - {file = "hf_transfer-0.1.8-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09949e86ad63ee139e463fd0dfaf401515ae70445854199f61d545514c65f744"}, - {file = "hf_transfer-0.1.8-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bf1a74552845b93ea972e6e7131ef54e56056aa54137e93a40faf3fbcb2442ff"}, - {file = "hf_transfer-0.1.8-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:959bcb3afb4ee6f2a07031a947dba98ec0b64c001bc914fbd8fc32e13a287162"}, - {file = "hf_transfer-0.1.8-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e01eecdb8162bd61dab9090fbd9f8034dd8b5755ef727a21ca8a057f80cb91ee"}, - {file = "hf_transfer-0.1.8-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50650a38e9d31f5ad8f010e4598bf304ecd99c17162e7d93f67e031571b864ee"}, - {file = "hf_transfer-0.1.8-cp37-none-win32.whl", hash = "sha256:e29b9d1d378138f2f4eae0e93ca94af3b5d45f4532eef69f1ab97fe06f9c9d9e"}, - {file = "hf_transfer-0.1.8-cp37-none-win_amd64.whl", hash = "sha256:cfd6cef43ae883103117a371f8ebae4e7f9637bc6fb480f1be5568e2fe22a8a7"}, - {file = "hf_transfer-0.1.8-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:92a68f7a0043cca8a0de4decc760dca177530944cbab502afac503bd1b2fa01a"}, - {file = "hf_transfer-0.1.8-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e3138e408179f80a5480598e32f8e1abb564915cbde4d3bc8da52811c75dc3ea"}, - {file = "hf_transfer-0.1.8-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4544d148930ad34442d43b8fa911c8479c04a95b858b1d1f91e0b7da77082fad"}, - {file = "hf_transfer-0.1.8-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a851794b9f029965664f8c3002c957fccf21685e9397ceb4f9f19c986dee8ad3"}, - {file = "hf_transfer-0.1.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:791aaf87c5319ac83edb6ab2994b3db19924c49d6ff667dd3d8a610b455ff70a"}, - {file = "hf_transfer-0.1.8-cp38-none-win32.whl", hash = "sha256:8f71e5d35d3a3160dcca12fdcc8119033aeacaa6a32838a7ad9f9cb1008bbe58"}, - {file = "hf_transfer-0.1.8-cp38-none-win_amd64.whl", hash = "sha256:543287b4ceb1e25501580b99690f7f0df9d3631d29306f37cbd97e918c732944"}, - {file = "hf_transfer-0.1.8-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:7ce02a18bd0bb2343e707ac85b68c946bc37623ee24150c69158f6b2b2c7a98f"}, - {file = "hf_transfer-0.1.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:64d7f8dbd64ba183ed1df75d47c84e075ff666ceaa335bff1de16b09eaac5b80"}, - {file = "hf_transfer-0.1.8-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e7858694e11419ae27e542fb8fc0d0e54d46ff7768fe73bc359d70b8f5aa578"}, - {file = "hf_transfer-0.1.8-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bed116cd9d1edfa32c0136d7cb8e5f1afd2b32df43c49085d428f108fc8e1c8f"}, - {file = "hf_transfer-0.1.8-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e385d0da9c6b3472ab29285d2d46c9f9903205b8d108f88a82f3f85aafae0ab"}, - {file = "hf_transfer-0.1.8-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:98f75fa4b86ef15433cd907807ac77d1fb39d7e7b790bfd39c7ae9c385bf0200"}, - {file = "hf_transfer-0.1.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1a63ad947d2901425ac0a3ed70c3696dfde27fadb0482ed763bdd5cc946b278"}, - {file = "hf_transfer-0.1.8-cp39-none-win32.whl", hash = "sha256:3e74096915813ae842ea6a5bdf10c0fef960aa51a35a560955b3e61cdfe3db57"}, - {file = "hf_transfer-0.1.8-cp39-none-win_amd64.whl", hash = "sha256:05ea16307bf4a5eb097cbc6e5057e4eb5e080a138af23ef639fd38857723c288"}, - {file = "hf_transfer-0.1.8-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:928ff036c3e98e10dcfbdb4fcdfc4592d37a5cc8e365a7ba8dfd4337e849d675"}, - {file = "hf_transfer-0.1.8-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d49ba3ce67035f460ae1924fe2feafec155cb535eec7f31ed5109c19064cd294"}, - {file = "hf_transfer-0.1.8-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b01f5872c62cfee3ec9ca5c738818296f69f8adf84b4d8d15f2a5601d9dda339"}, - {file = "hf_transfer-0.1.8-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:659d4212d50847a5165666bf43d67727679b4f694ef9c413613cc27093136527"}, - {file = "hf_transfer-0.1.8.tar.gz", hash = "sha256:26d229468152e7a3ec12664cac86b8c2800695fd85f9c9a96677a775cc04f0b3"}, + {file = "hf_transfer-0.1.9-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:6e94e8822da79573c9b6ae4d6b2f847c59a7a06c5327d7db20751b68538dc4f6"}, + {file = "hf_transfer-0.1.9-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3ebc4ab9023414880c8b1d3c38174d1c9989eb5022d37e814fa91a3060123eb0"}, + {file = "hf_transfer-0.1.9-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8674026f21ed369aa2a0a4b46000aca850fc44cd2b54af33a172ce5325b4fc82"}, + {file = "hf_transfer-0.1.9-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3a736dfbb2c84f5a2c975478ad200c0c8bfcb58a25a35db402678fb87ce17fa4"}, + {file = "hf_transfer-0.1.9-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:504b8427fd785dd8546d53b9fafe6e436bd7a3adf76b9dce556507650a7b4567"}, + {file = "hf_transfer-0.1.9-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2c7fc1b85f4d0f76e452765d7648c9f4bfd0aedb9ced2ae1ebfece2d8cfaf8e2"}, + {file = "hf_transfer-0.1.9-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d991376f0eac70a60f0cbc95602aa708a6f7c8617f28b4945c1431d67b8e3c8"}, + {file = "hf_transfer-0.1.9-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e6ac4eddcd99575ed3735ed911ddf9d1697e2bd13aa3f0ad7e3904dd4863842e"}, + {file = "hf_transfer-0.1.9-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:57fd9880da1ee0f47250f735f791fab788f0aa1ee36afc49f761349869c8b4d9"}, + {file = "hf_transfer-0.1.9-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:5d561f0520f493c66b016d99ceabe69c23289aa90be38dd802d2aef279f15751"}, + {file = "hf_transfer-0.1.9-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a5b366d34cd449fe9b20ef25941e6eef0460a2f74e7389f02e673e1f88ebd538"}, + {file = "hf_transfer-0.1.9-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:e66acf91df4a8b72f60223059df3003062a5ae111757187ed1a06750a30e911b"}, + {file = "hf_transfer-0.1.9-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:8669dbcc7a3e2e8d61d42cd24da9c50d57770bd74b445c65123291ca842a7e7a"}, + {file = "hf_transfer-0.1.9-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8fd0167c4407a3bc4cdd0307e65ada2294ec04f1813d8a69a5243e379b22e9d8"}, + {file = "hf_transfer-0.1.9-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ee8b10afedcb75f71091bcc197c526a6ebf5c58bbbadb34fdeee6160f55f619f"}, + {file = "hf_transfer-0.1.9-cp38-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5828057e313de59300dd1abb489444bc452efe3f479d3c55b31a8f680936ba42"}, + {file = "hf_transfer-0.1.9-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc6bd19e1cc177c66bdef15ef8636ad3bde79d5a4f608c158021153b4573509d"}, + {file = "hf_transfer-0.1.9-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdca9bfb89e6f8f281890cc61a8aff2d3cecaff7e1a4d275574d96ca70098557"}, + {file = "hf_transfer-0.1.9-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:89a23f58b7b7effbc047b8ca286f131b17728c99a9f972723323003ffd1bb916"}, + {file = "hf_transfer-0.1.9-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:dc7fff1345980d6c0ebb92c811d24afa4b98b3e07ed070c8e38cc91fd80478c5"}, + {file = "hf_transfer-0.1.9-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:1a6bd16c667ebe89a069ca163060127a794fa3a3525292c900b8c8cc47985b0d"}, + {file = "hf_transfer-0.1.9-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:d2fde99d502093ade3ab1b53f80da18480e9902aa960dab7f74fb1b9e5bc5746"}, + {file = "hf_transfer-0.1.9-cp38-abi3-win32.whl", hash = "sha256:435cc3cdc8524ce57b074032b8fd76eed70a4224d2091232fa6a8cef8fd6803e"}, + {file = "hf_transfer-0.1.9-cp38-abi3-win_amd64.whl", hash = "sha256:16f208fc678911c37e11aa7b586bc66a37d02e636208f18b6bc53d29b5df40ad"}, + {file = "hf_transfer-0.1.9.tar.gz", hash = "sha256:035572865dab29d17e783fbf1e84cf1cb24f3fcf8f1b17db1cfc7fdf139f02bf"}, ] [[package]] name = "httpcore" -version = "1.0.6" +version = "1.0.7" description = "A minimal low-level HTTP client." optional = false python-versions = ">=3.8" files = [ - {file = "httpcore-1.0.6-py3-none-any.whl", hash = "sha256:27b59625743b85577a8c0e10e55b50b5368a4f2cfe8cc7bcfa9cf00829c2682f"}, - {file = "httpcore-1.0.6.tar.gz", hash = "sha256:73f6dbd6eb8c21bbf7ef8efad555481853f5f6acdeaff1edb0694289269ee17f"}, + {file = "httpcore-1.0.7-py3-none-any.whl", hash = "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd"}, + {file = "httpcore-1.0.7.tar.gz", hash = "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c"}, ] [package.dependencies] @@ -1662,13 +1735,13 @@ files = [ [[package]] name = "jinja2" -version = "3.1.4" +version = "3.1.5" description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" files = [ - {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"}, - {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"}, + {file = "jinja2-3.1.5-py3-none-any.whl", hash = "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb"}, + {file = "jinja2-3.1.5.tar.gz", hash = "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb"}, ] [package.dependencies] @@ -1679,84 +1752,87 @@ i18n = ["Babel (>=2.7)"] [[package]] name = "jiter" -version = "0.6.1" +version = "0.8.2" description = "Fast iterable JSON parser." optional = true python-versions = ">=3.8" files = [ - {file = "jiter-0.6.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:d08510593cb57296851080018006dfc394070178d238b767b1879dc1013b106c"}, - {file = "jiter-0.6.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:adef59d5e2394ebbad13b7ed5e0306cceb1df92e2de688824232a91588e77aa7"}, - {file = "jiter-0.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b3e02f7a27f2bcc15b7d455c9df05df8ffffcc596a2a541eeda9a3110326e7a3"}, - {file = "jiter-0.6.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed69a7971d67b08f152c17c638f0e8c2aa207e9dd3a5fcd3cba294d39b5a8d2d"}, - {file = "jiter-0.6.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b2019d966e98f7c6df24b3b8363998575f47d26471bfb14aade37630fae836a1"}, - {file = "jiter-0.6.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:36c0b51a285b68311e207a76c385650322734c8717d16c2eb8af75c9d69506e7"}, - {file = "jiter-0.6.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:220e0963b4fb507c525c8f58cde3da6b1be0bfddb7ffd6798fb8f2531226cdb1"}, - {file = "jiter-0.6.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:aa25c7a9bf7875a141182b9c95aed487add635da01942ef7ca726e42a0c09058"}, - {file = "jiter-0.6.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e90552109ca8ccd07f47ca99c8a1509ced93920d271bb81780a973279974c5ab"}, - {file = "jiter-0.6.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:67723a011964971864e0b484b0ecfee6a14de1533cff7ffd71189e92103b38a8"}, - {file = "jiter-0.6.1-cp310-none-win32.whl", hash = "sha256:33af2b7d2bf310fdfec2da0177eab2fedab8679d1538d5b86a633ebfbbac4edd"}, - {file = "jiter-0.6.1-cp310-none-win_amd64.whl", hash = "sha256:7cea41c4c673353799906d940eee8f2d8fd1d9561d734aa921ae0f75cb9732f4"}, - {file = "jiter-0.6.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:b03c24e7da7e75b170c7b2b172d9c5e463aa4b5c95696a368d52c295b3f6847f"}, - {file = "jiter-0.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:47fee1be677b25d0ef79d687e238dc6ac91a8e553e1a68d0839f38c69e0ee491"}, - {file = "jiter-0.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25f0d2f6e01a8a0fb0eab6d0e469058dab2be46ff3139ed2d1543475b5a1d8e7"}, - {file = "jiter-0.6.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0b809e39e342c346df454b29bfcc7bca3d957f5d7b60e33dae42b0e5ec13e027"}, - {file = "jiter-0.6.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e9ac7c2f092f231f5620bef23ce2e530bd218fc046098747cc390b21b8738a7a"}, - {file = "jiter-0.6.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e51a2d80d5fe0ffb10ed2c82b6004458be4a3f2b9c7d09ed85baa2fbf033f54b"}, - {file = "jiter-0.6.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3343d4706a2b7140e8bd49b6c8b0a82abf9194b3f0f5925a78fc69359f8fc33c"}, - {file = "jiter-0.6.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:82521000d18c71e41c96960cb36e915a357bc83d63a8bed63154b89d95d05ad1"}, - {file = "jiter-0.6.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:3c843e7c1633470708a3987e8ce617ee2979ee18542d6eb25ae92861af3f1d62"}, - {file = "jiter-0.6.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a2e861658c3fe849efc39b06ebb98d042e4a4c51a8d7d1c3ddc3b1ea091d0784"}, - {file = "jiter-0.6.1-cp311-none-win32.whl", hash = "sha256:7d72fc86474862c9c6d1f87b921b70c362f2b7e8b2e3c798bb7d58e419a6bc0f"}, - {file = "jiter-0.6.1-cp311-none-win_amd64.whl", hash = "sha256:3e36a320634f33a07794bb15b8da995dccb94f944d298c8cfe2bd99b1b8a574a"}, - {file = "jiter-0.6.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:1fad93654d5a7dcce0809aff66e883c98e2618b86656aeb2129db2cd6f26f867"}, - {file = "jiter-0.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4e6e340e8cd92edab7f6a3a904dbbc8137e7f4b347c49a27da9814015cc0420c"}, - {file = "jiter-0.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:691352e5653af84ed71763c3c427cff05e4d658c508172e01e9c956dfe004aba"}, - {file = "jiter-0.6.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:defee3949313c1f5b55e18be45089970cdb936eb2a0063f5020c4185db1b63c9"}, - {file = "jiter-0.6.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:26d2bdd5da097e624081c6b5d416d3ee73e5b13f1703bcdadbb1881f0caa1933"}, - {file = "jiter-0.6.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18aa9d1626b61c0734b973ed7088f8a3d690d0b7f5384a5270cd04f4d9f26c86"}, - {file = "jiter-0.6.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a3567c8228afa5ddcce950631c6b17397ed178003dc9ee7e567c4c4dcae9fa0"}, - {file = "jiter-0.6.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e5c0507131c922defe3f04c527d6838932fcdfd69facebafd7d3574fa3395314"}, - {file = "jiter-0.6.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:540fcb224d7dc1bcf82f90f2ffb652df96f2851c031adca3c8741cb91877143b"}, - {file = "jiter-0.6.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e7b75436d4fa2032b2530ad989e4cb0ca74c655975e3ff49f91a1a3d7f4e1df2"}, - {file = "jiter-0.6.1-cp312-none-win32.whl", hash = "sha256:883d2ced7c21bf06874fdeecab15014c1c6d82216765ca6deef08e335fa719e0"}, - {file = "jiter-0.6.1-cp312-none-win_amd64.whl", hash = "sha256:91e63273563401aadc6c52cca64a7921c50b29372441adc104127b910e98a5b6"}, - {file = "jiter-0.6.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:852508a54fe3228432e56019da8b69208ea622a3069458252f725d634e955b31"}, - {file = "jiter-0.6.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f491cc69ff44e5a1e8bc6bf2b94c1f98d179e1aaf4a554493c171a5b2316b701"}, - {file = "jiter-0.6.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc56c8f0b2a28ad4d8047f3ae62d25d0e9ae01b99940ec0283263a04724de1f3"}, - {file = "jiter-0.6.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:51b58f7a0d9e084a43b28b23da2b09fc5e8df6aa2b6a27de43f991293cab85fd"}, - {file = "jiter-0.6.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5f79ce15099154c90ef900d69c6b4c686b64dfe23b0114e0971f2fecd306ec6c"}, - {file = "jiter-0.6.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:03a025b52009f47e53ea619175d17e4ded7c035c6fbd44935cb3ada11e1fd592"}, - {file = "jiter-0.6.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c74a8d93718137c021d9295248a87c2f9fdc0dcafead12d2930bc459ad40f885"}, - {file = "jiter-0.6.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:40b03b75f903975f68199fc4ec73d546150919cb7e534f3b51e727c4d6ccca5a"}, - {file = "jiter-0.6.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:825651a3f04cf92a661d22cad61fc913400e33aa89b3e3ad9a6aa9dc8a1f5a71"}, - {file = "jiter-0.6.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:928bf25eb69ddb292ab8177fe69d3fbf76c7feab5fce1c09265a7dccf25d3991"}, - {file = "jiter-0.6.1-cp313-none-win32.whl", hash = "sha256:352cd24121e80d3d053fab1cc9806258cad27c53cad99b7a3cac57cf934b12e4"}, - {file = "jiter-0.6.1-cp313-none-win_amd64.whl", hash = "sha256:be7503dd6f4bf02c2a9bacb5cc9335bc59132e7eee9d3e931b13d76fd80d7fda"}, - {file = "jiter-0.6.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:31d8e00e1fb4c277df8ab6f31a671f509ebc791a80e5c61fdc6bc8696aaa297c"}, - {file = "jiter-0.6.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:77c296d65003cd7ee5d7b0965f6acbe6cffaf9d1fa420ea751f60ef24e85fed5"}, - {file = "jiter-0.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aeeb0c0325ef96c12a48ea7e23e2e86fe4838e6e0a995f464cf4c79fa791ceeb"}, - {file = "jiter-0.6.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a31c6fcbe7d6c25d6f1cc6bb1cba576251d32795d09c09961174fe461a1fb5bd"}, - {file = "jiter-0.6.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59e2b37f3b9401fc9e619f4d4badcab2e8643a721838bcf695c2318a0475ae42"}, - {file = "jiter-0.6.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bae5ae4853cb9644144e9d0755854ce5108d470d31541d83f70ca7ecdc2d1637"}, - {file = "jiter-0.6.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9df588e9c830b72d8db1dd7d0175af6706b0904f682ea9b1ca8b46028e54d6e9"}, - {file = "jiter-0.6.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:15f8395e835cf561c85c1adee72d899abf2733d9df72e9798e6d667c9b5c1f30"}, - {file = "jiter-0.6.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5a99d4e0b5fc3b05ea732d67eb2092fe894e95a90e6e413f2ea91387e228a307"}, - {file = "jiter-0.6.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a311df1fa6be0ccd64c12abcd85458383d96e542531bafbfc0a16ff6feda588f"}, - {file = "jiter-0.6.1-cp38-none-win32.whl", hash = "sha256:81116a6c272a11347b199f0e16b6bd63f4c9d9b52bc108991397dd80d3c78aba"}, - {file = "jiter-0.6.1-cp38-none-win_amd64.whl", hash = "sha256:13f9084e3e871a7c0b6e710db54444088b1dd9fbefa54d449b630d5e73bb95d0"}, - {file = "jiter-0.6.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:f1c53615fcfec3b11527c08d19cff6bc870da567ce4e57676c059a3102d3a082"}, - {file = "jiter-0.6.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f791b6a4da23238c17a81f44f5b55d08a420c5692c1fda84e301a4b036744eb1"}, - {file = "jiter-0.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c97e90fec2da1d5f68ef121444c2c4fa72eabf3240829ad95cf6bbeca42a301"}, - {file = "jiter-0.6.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3cbc1a66b4e41511209e97a2866898733c0110b7245791ac604117b7fb3fedb7"}, - {file = "jiter-0.6.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e4e85f9e12cd8418ab10e1fcf0e335ae5bb3da26c4d13a0fd9e6a17a674783b6"}, - {file = "jiter-0.6.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08be33db6dcc374c9cc19d3633af5e47961a7b10d4c61710bd39e48d52a35824"}, - {file = "jiter-0.6.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:677be9550004f5e010d673d3b2a2b815a8ea07a71484a57d3f85dde7f14cf132"}, - {file = "jiter-0.6.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e8bd065be46c2eecc328e419d6557bbc37844c88bb07b7a8d2d6c91c7c4dedc9"}, - {file = "jiter-0.6.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bd95375ce3609ec079a97c5d165afdd25693302c071ca60c7ae1cf826eb32022"}, - {file = "jiter-0.6.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:db459ed22d0208940d87f614e1f0ea5a946d29a3cfef71f7e1aab59b6c6b2afb"}, - {file = "jiter-0.6.1-cp39-none-win32.whl", hash = "sha256:d71c962f0971347bd552940ab96aa42ceefcd51b88c4ced8a27398182efa8d80"}, - {file = "jiter-0.6.1-cp39-none-win_amd64.whl", hash = "sha256:d465db62d2d10b489b7e7a33027c4ae3a64374425d757e963f86df5b5f2e7fc5"}, - {file = "jiter-0.6.1.tar.gz", hash = "sha256:e19cd21221fc139fb032e4112986656cb2739e9fe6d84c13956ab30ccc7d4449"}, + {file = "jiter-0.8.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:ca8577f6a413abe29b079bc30f907894d7eb07a865c4df69475e868d73e71c7b"}, + {file = "jiter-0.8.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b25bd626bde7fb51534190c7e3cb97cee89ee76b76d7585580e22f34f5e3f393"}, + {file = "jiter-0.8.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5c826a221851a8dc028eb6d7d6429ba03184fa3c7e83ae01cd6d3bd1d4bd17d"}, + {file = "jiter-0.8.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d35c864c2dff13dfd79fb070fc4fc6235d7b9b359efe340e1261deb21b9fcb66"}, + {file = "jiter-0.8.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f557c55bc2b7676e74d39d19bcb8775ca295c7a028246175d6a8b431e70835e5"}, + {file = "jiter-0.8.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:580ccf358539153db147e40751a0b41688a5ceb275e6f3e93d91c9467f42b2e3"}, + {file = "jiter-0.8.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af102d3372e917cffce49b521e4c32c497515119dc7bd8a75665e90a718bbf08"}, + {file = "jiter-0.8.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cadcc978f82397d515bb2683fc0d50103acff2a180552654bb92d6045dec2c49"}, + {file = "jiter-0.8.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ba5bdf56969cad2019d4e8ffd3f879b5fdc792624129741d3d83fc832fef8c7d"}, + {file = "jiter-0.8.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3b94a33a241bee9e34b8481cdcaa3d5c2116f575e0226e421bed3f7a6ea71cff"}, + {file = "jiter-0.8.2-cp310-cp310-win32.whl", hash = "sha256:6e5337bf454abddd91bd048ce0dca5134056fc99ca0205258766db35d0a2ea43"}, + {file = "jiter-0.8.2-cp310-cp310-win_amd64.whl", hash = "sha256:4a9220497ca0cb1fe94e3f334f65b9b5102a0b8147646118f020d8ce1de70105"}, + {file = "jiter-0.8.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:2dd61c5afc88a4fda7d8b2cf03ae5947c6ac7516d32b7a15bf4b49569a5c076b"}, + {file = "jiter-0.8.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a6c710d657c8d1d2adbbb5c0b0c6bfcec28fd35bd6b5f016395f9ac43e878a15"}, + {file = "jiter-0.8.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9584de0cd306072635fe4b89742bf26feae858a0683b399ad0c2509011b9dc0"}, + {file = "jiter-0.8.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5a90a923338531b7970abb063cfc087eebae6ef8ec8139762007188f6bc69a9f"}, + {file = "jiter-0.8.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d21974d246ed0181558087cd9f76e84e8321091ebfb3a93d4c341479a736f099"}, + {file = "jiter-0.8.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:32475a42b2ea7b344069dc1e81445cfc00b9d0e3ca837f0523072432332e9f74"}, + {file = "jiter-0.8.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b9931fd36ee513c26b5bf08c940b0ac875de175341cbdd4fa3be109f0492586"}, + {file = "jiter-0.8.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ce0820f4a3a59ddced7fce696d86a096d5cc48d32a4183483a17671a61edfddc"}, + {file = "jiter-0.8.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:8ffc86ae5e3e6a93765d49d1ab47b6075a9c978a2b3b80f0f32628f39caa0c88"}, + {file = "jiter-0.8.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5127dc1abd809431172bc3fbe8168d6b90556a30bb10acd5ded41c3cfd6f43b6"}, + {file = "jiter-0.8.2-cp311-cp311-win32.whl", hash = "sha256:66227a2c7b575720c1871c8800d3a0122bb8ee94edb43a5685aa9aceb2782d44"}, + {file = "jiter-0.8.2-cp311-cp311-win_amd64.whl", hash = "sha256:cde031d8413842a1e7501e9129b8e676e62a657f8ec8166e18a70d94d4682855"}, + {file = "jiter-0.8.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:e6ec2be506e7d6f9527dae9ff4b7f54e68ea44a0ef6b098256ddf895218a2f8f"}, + {file = "jiter-0.8.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:76e324da7b5da060287c54f2fabd3db5f76468006c811831f051942bf68c9d44"}, + {file = "jiter-0.8.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:180a8aea058f7535d1c84183c0362c710f4750bef66630c05f40c93c2b152a0f"}, + {file = "jiter-0.8.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:025337859077b41548bdcbabe38698bcd93cfe10b06ff66617a48ff92c9aec60"}, + {file = "jiter-0.8.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ecff0dc14f409599bbcafa7e470c00b80f17abc14d1405d38ab02e4b42e55b57"}, + {file = "jiter-0.8.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ffd9fee7d0775ebaba131f7ca2e2d83839a62ad65e8e02fe2bd8fc975cedeb9e"}, + {file = "jiter-0.8.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14601dcac4889e0a1c75ccf6a0e4baf70dbc75041e51bcf8d0e9274519df6887"}, + {file = "jiter-0.8.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:92249669925bc1c54fcd2ec73f70f2c1d6a817928480ee1c65af5f6b81cdf12d"}, + {file = "jiter-0.8.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e725edd0929fa79f8349ab4ec7f81c714df51dc4e991539a578e5018fa4a7152"}, + {file = "jiter-0.8.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bf55846c7b7a680eebaf9c3c48d630e1bf51bdf76c68a5f654b8524335b0ad29"}, + {file = "jiter-0.8.2-cp312-cp312-win32.whl", hash = "sha256:7efe4853ecd3d6110301665a5178b9856be7e2a9485f49d91aa4d737ad2ae49e"}, + {file = "jiter-0.8.2-cp312-cp312-win_amd64.whl", hash = "sha256:83c0efd80b29695058d0fd2fa8a556490dbce9804eac3e281f373bbc99045f6c"}, + {file = "jiter-0.8.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:ca1f08b8e43dc3bd0594c992fb1fd2f7ce87f7bf0d44358198d6da8034afdf84"}, + {file = "jiter-0.8.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5672a86d55416ccd214c778efccf3266b84f87b89063b582167d803246354be4"}, + {file = "jiter-0.8.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58dc9bc9767a1101f4e5e22db1b652161a225874d66f0e5cb8e2c7d1c438b587"}, + {file = "jiter-0.8.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:37b2998606d6dadbb5ccda959a33d6a5e853252d921fec1792fc902351bb4e2c"}, + {file = "jiter-0.8.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4ab9a87f3784eb0e098f84a32670cfe4a79cb6512fd8f42ae3d0709f06405d18"}, + {file = "jiter-0.8.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:79aec8172b9e3c6d05fd4b219d5de1ac616bd8da934107325a6c0d0e866a21b6"}, + {file = "jiter-0.8.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:711e408732d4e9a0208008e5892c2966b485c783cd2d9a681f3eb147cf36c7ef"}, + {file = "jiter-0.8.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:653cf462db4e8c41995e33d865965e79641ef45369d8a11f54cd30888b7e6ff1"}, + {file = "jiter-0.8.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:9c63eaef32b7bebac8ebebf4dabebdbc6769a09c127294db6babee38e9f405b9"}, + {file = "jiter-0.8.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:eb21aaa9a200d0a80dacc7a81038d2e476ffe473ffdd9c91eb745d623561de05"}, + {file = "jiter-0.8.2-cp313-cp313-win32.whl", hash = "sha256:789361ed945d8d42850f919342a8665d2dc79e7e44ca1c97cc786966a21f627a"}, + {file = "jiter-0.8.2-cp313-cp313-win_amd64.whl", hash = "sha256:ab7f43235d71e03b941c1630f4b6e3055d46b6cb8728a17663eaac9d8e83a865"}, + {file = "jiter-0.8.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b426f72cd77da3fec300ed3bc990895e2dd6b49e3bfe6c438592a3ba660e41ca"}, + {file = "jiter-0.8.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2dd880785088ff2ad21ffee205e58a8c1ddabc63612444ae41e5e4b321b39c0"}, + {file = "jiter-0.8.2-cp313-cp313t-win_amd64.whl", hash = "sha256:3ac9f578c46f22405ff7f8b1f5848fb753cc4b8377fbec8470a7dc3997ca7566"}, + {file = "jiter-0.8.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:9e1fa156ee9454642adb7e7234a383884452532bc9d53d5af2d18d98ada1d79c"}, + {file = "jiter-0.8.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0cf5dfa9956d96ff2efb0f8e9c7d055904012c952539a774305aaaf3abdf3d6c"}, + {file = "jiter-0.8.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e52bf98c7e727dd44f7c4acb980cb988448faeafed8433c867888268899b298b"}, + {file = "jiter-0.8.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a2ecaa3c23e7a7cf86d00eda3390c232f4d533cd9ddea4b04f5d0644faf642c5"}, + {file = "jiter-0.8.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:08d4c92bf480e19fc3f2717c9ce2aa31dceaa9163839a311424b6862252c943e"}, + {file = "jiter-0.8.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:99d9a1eded738299ba8e106c6779ce5c3893cffa0e32e4485d680588adae6db8"}, + {file = "jiter-0.8.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d20be8b7f606df096e08b0b1b4a3c6f0515e8dac296881fe7461dfa0fb5ec817"}, + {file = "jiter-0.8.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d33f94615fcaf872f7fd8cd98ac3b429e435c77619777e8a449d9d27e01134d1"}, + {file = "jiter-0.8.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:317b25e98a35ffec5c67efe56a4e9970852632c810d35b34ecdd70cc0e47b3b6"}, + {file = "jiter-0.8.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fc9043259ee430ecd71d178fccabd8c332a3bf1e81e50cae43cc2b28d19e4cb7"}, + {file = "jiter-0.8.2-cp38-cp38-win32.whl", hash = "sha256:fc5adda618205bd4678b146612ce44c3cbfdee9697951f2c0ffdef1f26d72b63"}, + {file = "jiter-0.8.2-cp38-cp38-win_amd64.whl", hash = "sha256:cd646c827b4f85ef4a78e4e58f4f5854fae0caf3db91b59f0d73731448a970c6"}, + {file = "jiter-0.8.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:e41e75344acef3fc59ba4765df29f107f309ca9e8eace5baacabd9217e52a5ee"}, + {file = "jiter-0.8.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7f22b16b35d5c1df9dfd58843ab2cd25e6bf15191f5a236bed177afade507bfc"}, + {file = "jiter-0.8.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7200b8f7619d36aa51c803fd52020a2dfbea36ffec1b5e22cab11fd34d95a6d"}, + {file = "jiter-0.8.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:70bf4c43652cc294040dbb62256c83c8718370c8b93dd93d934b9a7bf6c4f53c"}, + {file = "jiter-0.8.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f9d471356dc16f84ed48768b8ee79f29514295c7295cb41e1133ec0b2b8d637d"}, + {file = "jiter-0.8.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:859e8eb3507894093d01929e12e267f83b1d5f6221099d3ec976f0c995cb6bd9"}, + {file = "jiter-0.8.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eaa58399c01db555346647a907b4ef6d4f584b123943be6ed5588c3f2359c9f4"}, + {file = "jiter-0.8.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8f2d5ed877f089862f4c7aacf3a542627c1496f972a34d0474ce85ee7d939c27"}, + {file = "jiter-0.8.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:03c9df035d4f8d647f8c210ddc2ae0728387275340668fb30d2421e17d9a0841"}, + {file = "jiter-0.8.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8bd2a824d08d8977bb2794ea2682f898ad3d8837932e3a74937e93d62ecbb637"}, + {file = "jiter-0.8.2-cp39-cp39-win32.whl", hash = "sha256:ca29b6371ebc40e496995c94b988a101b9fbbed48a51190a4461fcb0a68b4a36"}, + {file = "jiter-0.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:1c0dfbd1be3cbefc7510102370d86e35d1d53e5a93d48519688b1bf0f761160a"}, + {file = "jiter-0.8.2.tar.gz", hash = "sha256:cd73d3e740666d0e639f678adb176fad25c1bcbdae88d8d7b857e1783bb4212d"}, ] [[package]] @@ -1807,7 +1883,7 @@ referencing = ">=0.31.0" [[package]] name = "kserve" -version = "0.14.0" +version = "0.15.0rc0" description = "KServe Python SDK" optional = false python-versions = ">=3.9,<3.13" @@ -1899,44 +1975,34 @@ nearley = ["js2py"] regex = ["regex"] [[package]] -name = "llvmlite" -version = "0.43.0" -description = "lightweight wrapper around basic LLVM functionality" +name = "linkify-it-py" +version = "2.0.3" +description = "Links recognition library with FULL unicode support." optional = true -python-versions = ">=3.9" +python-versions = ">=3.7" files = [ - {file = "llvmlite-0.43.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a289af9a1687c6cf463478f0fa8e8aa3b6fb813317b0d70bf1ed0759eab6f761"}, - {file = "llvmlite-0.43.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6d4fd101f571a31acb1559ae1af30f30b1dc4b3186669f92ad780e17c81e91bc"}, - {file = "llvmlite-0.43.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7d434ec7e2ce3cc8f452d1cd9a28591745de022f931d67be688a737320dfcead"}, - {file = "llvmlite-0.43.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6912a87782acdff6eb8bf01675ed01d60ca1f2551f8176a300a886f09e836a6a"}, - {file = "llvmlite-0.43.0-cp310-cp310-win_amd64.whl", hash = "sha256:14f0e4bf2fd2d9a75a3534111e8ebeb08eda2f33e9bdd6dfa13282afacdde0ed"}, - {file = "llvmlite-0.43.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3e8d0618cb9bfe40ac38a9633f2493d4d4e9fcc2f438d39a4e854f39cc0f5f98"}, - {file = "llvmlite-0.43.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e0a9a1a39d4bf3517f2af9d23d479b4175ead205c592ceeb8b89af48a327ea57"}, - {file = "llvmlite-0.43.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1da416ab53e4f7f3bc8d4eeba36d801cc1894b9fbfbf2022b29b6bad34a7df2"}, - {file = "llvmlite-0.43.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:977525a1e5f4059316b183fb4fd34fa858c9eade31f165427a3977c95e3ee749"}, - {file = "llvmlite-0.43.0-cp311-cp311-win_amd64.whl", hash = "sha256:d5bd550001d26450bd90777736c69d68c487d17bf371438f975229b2b8241a91"}, - {file = "llvmlite-0.43.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f99b600aa7f65235a5a05d0b9a9f31150c390f31261f2a0ba678e26823ec38f7"}, - {file = "llvmlite-0.43.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:35d80d61d0cda2d767f72de99450766250560399edc309da16937b93d3b676e7"}, - {file = "llvmlite-0.43.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eccce86bba940bae0d8d48ed925f21dbb813519169246e2ab292b5092aba121f"}, - {file = "llvmlite-0.43.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df6509e1507ca0760787a199d19439cc887bfd82226f5af746d6977bd9f66844"}, - {file = "llvmlite-0.43.0-cp312-cp312-win_amd64.whl", hash = "sha256:7a2872ee80dcf6b5dbdc838763d26554c2a18aa833d31a2635bff16aafefb9c9"}, - {file = "llvmlite-0.43.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9cd2a7376f7b3367019b664c21f0c61766219faa3b03731113ead75107f3b66c"}, - {file = "llvmlite-0.43.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:18e9953c748b105668487b7c81a3e97b046d8abf95c4ddc0cd3c94f4e4651ae8"}, - {file = "llvmlite-0.43.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74937acd22dc11b33946b67dca7680e6d103d6e90eeaaaf932603bec6fe7b03a"}, - {file = "llvmlite-0.43.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc9efc739cc6ed760f795806f67889923f7274276f0eb45092a1473e40d9b867"}, - {file = "llvmlite-0.43.0-cp39-cp39-win_amd64.whl", hash = "sha256:47e147cdda9037f94b399bf03bfd8a6b6b1f2f90be94a454e3386f006455a9b4"}, - {file = "llvmlite-0.43.0.tar.gz", hash = "sha256:ae2b5b5c3ef67354824fb75517c8db5fbe93bc02cd9671f3c62271626bc041d5"}, + {file = "linkify-it-py-2.0.3.tar.gz", hash = "sha256:68cda27e162e9215c17d786649d1da0021a451bdc436ef9e0fa0ba5234b9b048"}, + {file = "linkify_it_py-2.0.3-py3-none-any.whl", hash = "sha256:6bcbc417b0ac14323382aef5c5192c0075bf8a9d6b41820a2b66371eac6b6d79"}, ] +[package.dependencies] +uc-micro-py = "*" + +[package.extras] +benchmark = ["pytest", "pytest-benchmark"] +dev = ["black", "flake8", "isort", "pre-commit", "pyproject-flake8"] +doc = ["myst-parser", "sphinx", "sphinx-book-theme"] +test = ["coverage", "pytest", "pytest-cov"] + [[package]] name = "lm-format-enforcer" -version = "0.10.6" +version = "0.10.9" description = "Enforce the output format (JSON Schema, Regex etc) of a language model" optional = true python-versions = "<4.0,>=3.8" files = [ - {file = "lm_format_enforcer-0.10.6-py3-none-any.whl", hash = "sha256:cad5a0cfbc9708332b57586a43d2945612fbcf40f9098f3ba95740d1a467a896"}, - {file = "lm_format_enforcer-0.10.6.tar.gz", hash = "sha256:ca2f061e9cf22fbe9f5ada2bc774c2bd369e8649b806fb5d9080eee37449f4a6"}, + {file = "lm_format_enforcer-0.10.9-py3-none-any.whl", hash = "sha256:6f3602d3470f54b3ba10d356ea34cc136afbd13394a360949dd8d943a2f2471e"}, + {file = "lm_format_enforcer-0.10.9.tar.gz", hash = "sha256:3e0bfeaf9fac9f69c8947da554db9a19a76d0be6e85075055f2c70d0aca420da"}, ] [package.dependencies] @@ -1945,6 +2011,32 @@ packaging = "*" pydantic = ">=1.10.8" pyyaml = "*" +[[package]] +name = "markdown-it-py" +version = "3.0.0" +description = "Python port of markdown-it. Markdown parsing, done right!" +optional = true +python-versions = ">=3.8" +files = [ + {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, + {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, +] + +[package.dependencies] +linkify-it-py = {version = ">=1,<3", optional = true, markers = "extra == \"linkify\""} +mdit-py-plugins = {version = "*", optional = true, markers = "extra == \"plugins\""} +mdurl = ">=0.1,<1.0" + +[package.extras] +benchmarking = ["psutil", "pytest", "pytest-benchmark"] +code-style = ["pre-commit (>=3.0,<4.0)"] +compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] +linkify = ["linkify-it-py (>=1,<3)"] +plugins = ["mdit-py-plugins"] +profiling = ["gprof2dot"] +rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] + [[package]] name = "markupsafe" version = "3.0.2" @@ -2015,6 +2107,100 @@ files = [ {file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"}, ] +[[package]] +name = "mdit-py-plugins" +version = "0.4.2" +description = "Collection of plugins for markdown-it-py" +optional = true +python-versions = ">=3.8" +files = [ + {file = "mdit_py_plugins-0.4.2-py3-none-any.whl", hash = "sha256:0c673c3f889399a33b95e88d2f0d111b4447bdfea7f237dab2d488f459835636"}, + {file = "mdit_py_plugins-0.4.2.tar.gz", hash = "sha256:5f2cd1fdb606ddf152d37ec30e46101a60512bc0e5fa1a7002c36647b09e26b5"}, +] + +[package.dependencies] +markdown-it-py = ">=1.0.0,<4.0.0" + +[package.extras] +code-style = ["pre-commit"] +rtd = ["myst-parser", "sphinx-book-theme"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] + +[[package]] +name = "mdurl" +version = "0.1.2" +description = "Markdown URL utilities" +optional = true +python-versions = ">=3.7" +files = [ + {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, + {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, +] + +[[package]] +name = "memray" +version = "1.15.0" +description = "A memory profiler for Python applications" +optional = true +python-versions = ">=3.7.0" +files = [ + {file = "memray-1.15.0-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:9b623c0c651d611dd068236566a8a202250e3d59307c3a3f241acc47835e73eb"}, + {file = "memray-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:74765f92887b7eed152e3b9f14c147c43bf0247417b18c7ea0dec173cd01633c"}, + {file = "memray-1.15.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a5c6be5f9c2280b5ba077cbfec4706f209f9c0c2cd3a53d949ab9f4ee1f6a255"}, + {file = "memray-1.15.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:68bdad519b644539440914e1f6a04995631d0e31311ebe0977d949f2125bb579"}, + {file = "memray-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4964c6bd555a0f1755dfdb97a8d9864e646054594449c66757441f7d7682405"}, + {file = "memray-1.15.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:92212b85c7d843126e4d343c8ca024f4a57537017b9ac7611864963b322aafae"}, + {file = "memray-1.15.0-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:cb8997e113378b9ac8bbd9b17f4f867fc5c1eea1980d873be3ebe4c2f1176784"}, + {file = "memray-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8ee45d919d81bfeb33677357dd5d248f3cad1d56be2ebd1853d4615a9f965b11"}, + {file = "memray-1.15.0-cp311-cp311-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a6b740aad69e7e5f82ffff53a8edef1313ff0b5e9b7253912da16e905dcb1dcb"}, + {file = "memray-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0045611f2da496e35d37a5ddfa2b6a74bbc82e47087924c07b3f520448297b26"}, + {file = "memray-1.15.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca5688e33a833de604d0e2de01b5bf11a4ac1d768998f8831a375a343dc7acaf"}, + {file = "memray-1.15.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4bbad938c3fdcebe0cf3c568fb8f8633ab37ab08ad4db167e0991e214d6f595b"}, + {file = "memray-1.15.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f4eb50295bd87a091a85ec71f0ee612c5d709df490fea8a3adc4410f5da4f695"}, + {file = "memray-1.15.0-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:d13554a25129593872b5fbcd55ac34453239e51d9b6ace258329596ccce22bb3"}, + {file = "memray-1.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8cfe15962a9002ede8b1f8b4f045d95855100a8a60a9bf0d9f2b92950f914189"}, + {file = "memray-1.15.0-cp312-cp312-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e84b39adca05e720bdbf950cc92ef4bafefa2d6160111e5fc427cf59c6c16d1a"}, + {file = "memray-1.15.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7745d2c58dfc33ef77f8827053cb957131420051b67e2d5642b605d0e65a586"}, + {file = "memray-1.15.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:412225d85db0ec22142a82646d85ecc1e8680d33adbfd15789c7eaa356ad4107"}, + {file = "memray-1.15.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d25ab7a7e32fedab46219121dfb6ec3e42c66984b217572fdd4cddc37359c521"}, + {file = "memray-1.15.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fb885f92833279d34addc607831352e91267b8e547ea861ad561a3dba64f6757"}, + {file = "memray-1.15.0-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:c1308e6a5fc5bc4e183bc0fdf5e241ddd9fb374338f32d77a4d5e74ccf611ef1"}, + {file = "memray-1.15.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0794227dfa4b86a56137211fd5b8ec131e0bc4a5dc41c2f5a318ca56a22c9331"}, + {file = "memray-1.15.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f184e82debd4f0c8ecf8e6034efddccdd9fac22909553a7f094eabf0902cd53f"}, + {file = "memray-1.15.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3493c5ac1ae1353fd0d24481bc9f30da8960ef703bf4af966cefff9dd1234d38"}, + {file = "memray-1.15.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:145a3062d8bf631aa8dc4b0928585e201282634afc369799dae1a0b9ece59fd4"}, + {file = "memray-1.15.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:59a4ade09cfe46e85cdb3a1976e9768e4674a6e448533c415dbe84e5a834f7c3"}, + {file = "memray-1.15.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:bb9870f41fe0c4cd4612fded51174f5b837f3bc6364c57b4a60e65016ccc1f7a"}, + {file = "memray-1.15.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:26cb3cac3810bbe9e701d40463c185cf9e7faac3965a0e2b309df1a9fc18cd9a"}, + {file = "memray-1.15.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:317287025cabd541f9fdec615b3c7ff394798113feea0edb92d31bc9f06eafd0"}, + {file = "memray-1.15.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:850eba1e3063d97172b0990a14f61782682baeb48f6ae039c0bb86b2f4d19b75"}, + {file = "memray-1.15.0-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:aa5150e3b58ba6184fac2a97426ee66f996dffe0571bbf09bffe23836318772e"}, + {file = "memray-1.15.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:753632eed43161131bb632799dc53b7ccb7e6341b8ca8ef4ad68ff8da81e766a"}, + {file = "memray-1.15.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:727190a81516e1955932c307ac6a55a3aedb5799bc2edf6a8fbf49852e851f0c"}, + {file = "memray-1.15.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:413b145445110900a99fb78b1fb6932c2e3ffadd35df5b258f8ac0a25e0aaf90"}, + {file = "memray-1.15.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2518a298ffa6c5a2ddfa6a36d196aa4aef5bb33c5d95a26565aac6a7f5fcb0c0"}, + {file = "memray-1.15.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ae46cb726c4c06121614995b877365680f196fa4549698aa5026c494a40e1a24"}, + {file = "memray-1.15.0-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:ce28c6a4d89349c43d76ad35ff1c21057230086cfcf18c6f4c2305df108bf0cd"}, + {file = "memray-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:671c2fd8c835caad80c2023baf6cdc4326c0f6dd4ae8bf1d7dbf6ad700c13625"}, + {file = "memray-1.15.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:8404f3969e071e35364fd99d238da8ef245cf7ee2c790f3d46cd5b41cbac0541"}, + {file = "memray-1.15.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a3e4c940deae29ea64d8dd4ffaee804f541a413c3c3c061a469837ed35d486b7"}, + {file = "memray-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:36720d9ee97dee6cd51b230cbd2556cc3e0215c5a569b97c1faebc927ac3c505"}, + {file = "memray-1.15.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cba7727bfdee596f71323195af0262508ed0aec7ebbf67d98de0b959d9b8cf02"}, + {file = "memray-1.15.0.tar.gz", hash = "sha256:1beffa2bcba3dbe0f095d547927286eca46e272798b83026dd1b5db58e16ed56"}, +] + +[package.dependencies] +jinja2 = ">=2.9" +rich = ">=11.2.0" +textual = ">=0.41.0" + +[package.extras] +benchmark = ["asv"] +dev = ["Cython", "IPython", "asv", "black", "bump2version", "check-manifest", "flake8", "furo", "greenlet", "ipython", "isort", "mypy", "packaging", "pytest", "pytest-cov", "pytest-textual-snapshot", "setuptools", "sphinx", "sphinx-argparse", "textual (>=0.43,!=0.65.2,!=0.66)", "towncrier"] +docs = ["IPython", "bump2version", "furo", "sphinx", "sphinx-argparse", "towncrier"] +lint = ["black", "check-manifest", "flake8", "isort", "mypy"] +test = ["Cython", "greenlet", "ipython", "packaging", "pytest", "pytest-cov", "pytest-textual-snapshot", "setuptools", "textual (>=0.43,!=0.65.2,!=0.66)"] + [[package]] name = "mistral-common" version = "1.5.1" @@ -2059,13 +2245,13 @@ tests = ["pytest (>=4.6)"] [[package]] name = "msal" -version = "1.31.0" +version = "1.31.1" description = "The Microsoft Authentication Library (MSAL) for Python library enables your app to access the Microsoft Cloud by supporting authentication of users with Microsoft Azure Active Directory accounts (AAD) and Microsoft Accounts (MSA) using industry standard OAuth2 and OpenID Connect." optional = false python-versions = ">=3.7" files = [ - {file = "msal-1.31.0-py3-none-any.whl", hash = "sha256:96bc37cff82ebe4b160d5fc0f1196f6ca8b50e274ecd0ec5bf69c438514086e7"}, - {file = "msal-1.31.0.tar.gz", hash = "sha256:2c4f189cf9cc8f00c80045f66d39b7c0f3ed45873fd3d1f2af9f22db2e12ff4b"}, + {file = "msal-1.31.1-py3-none-any.whl", hash = "sha256:29d9882de247e96db01386496d59f29035e5e841bcac892e6d7bf4390bf6bd17"}, + {file = "msal-1.31.1.tar.gz", hash = "sha256:11b5e6a3f802ffd3a72107203e20c4eac6ef53401961b880af2835b723d80578"}, ] [package.dependencies] @@ -2166,54 +2352,54 @@ files = [ [[package]] name = "msgspec" -version = "0.18.6" +version = "0.19.0" description = "A fast serialization and validation library, with builtin support for JSON, MessagePack, YAML, and TOML." optional = true -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "msgspec-0.18.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:77f30b0234eceeff0f651119b9821ce80949b4d667ad38f3bfed0d0ebf9d6d8f"}, - {file = "msgspec-0.18.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1a76b60e501b3932782a9da039bd1cd552b7d8dec54ce38332b87136c64852dd"}, - {file = "msgspec-0.18.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:06acbd6edf175bee0e36295d6b0302c6de3aaf61246b46f9549ca0041a9d7177"}, - {file = "msgspec-0.18.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40a4df891676d9c28a67c2cc39947c33de516335680d1316a89e8f7218660410"}, - {file = "msgspec-0.18.6-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:a6896f4cd5b4b7d688018805520769a8446df911eb93b421c6c68155cdf9dd5a"}, - {file = "msgspec-0.18.6-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3ac4dd63fd5309dd42a8c8c36c1563531069152be7819518be0a9d03be9788e4"}, - {file = "msgspec-0.18.6-cp310-cp310-win_amd64.whl", hash = "sha256:fda4c357145cf0b760000c4ad597e19b53adf01382b711f281720a10a0fe72b7"}, - {file = "msgspec-0.18.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e77e56ffe2701e83a96e35770c6adb655ffc074d530018d1b584a8e635b4f36f"}, - {file = "msgspec-0.18.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d5351afb216b743df4b6b147691523697ff3a2fc5f3d54f771e91219f5c23aaa"}, - {file = "msgspec-0.18.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3232fabacef86fe8323cecbe99abbc5c02f7698e3f5f2e248e3480b66a3596b"}, - {file = "msgspec-0.18.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3b524df6ea9998bbc99ea6ee4d0276a101bcc1aa8d14887bb823914d9f60d07"}, - {file = "msgspec-0.18.6-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:37f67c1d81272131895bb20d388dd8d341390acd0e192a55ab02d4d6468b434c"}, - {file = "msgspec-0.18.6-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d0feb7a03d971c1c0353de1a8fe30bb6579c2dc5ccf29b5f7c7ab01172010492"}, - {file = "msgspec-0.18.6-cp311-cp311-win_amd64.whl", hash = "sha256:41cf758d3f40428c235c0f27bc6f322d43063bc32da7b9643e3f805c21ed57b4"}, - {file = "msgspec-0.18.6-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d86f5071fe33e19500920333c11e2267a31942d18fed4d9de5bc2fbab267d28c"}, - {file = "msgspec-0.18.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce13981bfa06f5eb126a3a5a38b1976bddb49a36e4f46d8e6edecf33ccf11df1"}, - {file = "msgspec-0.18.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e97dec6932ad5e3ee1e3c14718638ba333befc45e0661caa57033cd4cc489466"}, - {file = "msgspec-0.18.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad237100393f637b297926cae1868b0d500f764ccd2f0623a380e2bcfb2809ca"}, - {file = "msgspec-0.18.6-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db1d8626748fa5d29bbd15da58b2d73af25b10aa98abf85aab8028119188ed57"}, - {file = "msgspec-0.18.6-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:d70cb3d00d9f4de14d0b31d38dfe60c88ae16f3182988246a9861259c6722af6"}, - {file = "msgspec-0.18.6-cp312-cp312-win_amd64.whl", hash = "sha256:1003c20bfe9c6114cc16ea5db9c5466e49fae3d7f5e2e59cb70693190ad34da0"}, - {file = "msgspec-0.18.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f7d9faed6dfff654a9ca7d9b0068456517f63dbc3aa704a527f493b9200b210a"}, - {file = "msgspec-0.18.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9da21f804c1a1471f26d32b5d9bc0480450ea77fbb8d9db431463ab64aaac2cf"}, - {file = "msgspec-0.18.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46eb2f6b22b0e61c137e65795b97dc515860bf6ec761d8fb65fdb62aa094ba61"}, - {file = "msgspec-0.18.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8355b55c80ac3e04885d72db515817d9fbb0def3bab936bba104e99ad22cf46"}, - {file = "msgspec-0.18.6-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9080eb12b8f59e177bd1eb5c21e24dd2ba2fa88a1dbc9a98e05ad7779b54c681"}, - {file = "msgspec-0.18.6-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:cc001cf39becf8d2dcd3f413a4797c55009b3a3cdbf78a8bf5a7ca8fdb76032c"}, - {file = "msgspec-0.18.6-cp38-cp38-win_amd64.whl", hash = "sha256:fac5834e14ac4da1fca373753e0c4ec9c8069d1fe5f534fa5208453b6065d5be"}, - {file = "msgspec-0.18.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:974d3520fcc6b824a6dedbdf2b411df31a73e6e7414301abac62e6b8d03791b4"}, - {file = "msgspec-0.18.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fd62e5818731a66aaa8e9b0a1e5543dc979a46278da01e85c3c9a1a4f047ef7e"}, - {file = "msgspec-0.18.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7481355a1adcf1f08dedd9311193c674ffb8bf7b79314b4314752b89a2cf7f1c"}, - {file = "msgspec-0.18.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6aa85198f8f154cf35d6f979998f6dadd3dc46a8a8c714632f53f5d65b315c07"}, - {file = "msgspec-0.18.6-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0e24539b25c85c8f0597274f11061c102ad6b0c56af053373ba4629772b407be"}, - {file = "msgspec-0.18.6-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c61ee4d3be03ea9cd089f7c8e36158786cd06e51fbb62529276452bbf2d52ece"}, - {file = "msgspec-0.18.6-cp39-cp39-win_amd64.whl", hash = "sha256:b5c390b0b0b7da879520d4ae26044d74aeee5144f83087eb7842ba59c02bc090"}, - {file = "msgspec-0.18.6.tar.gz", hash = "sha256:a59fc3b4fcdb972d09138cb516dbde600c99d07c38fd9372a6ef500d2d031b4e"}, + {file = "msgspec-0.19.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d8dd848ee7ca7c8153462557655570156c2be94e79acec3561cf379581343259"}, + {file = "msgspec-0.19.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0553bbc77662e5708fe66aa75e7bd3e4b0f209709c48b299afd791d711a93c36"}, + {file = "msgspec-0.19.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe2c4bf29bf4e89790b3117470dea2c20b59932772483082c468b990d45fb947"}, + {file = "msgspec-0.19.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00e87ecfa9795ee5214861eab8326b0e75475c2e68a384002aa135ea2a27d909"}, + {file = "msgspec-0.19.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3c4ec642689da44618f68c90855a10edbc6ac3ff7c1d94395446c65a776e712a"}, + {file = "msgspec-0.19.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2719647625320b60e2d8af06b35f5b12d4f4d281db30a15a1df22adb2295f633"}, + {file = "msgspec-0.19.0-cp310-cp310-win_amd64.whl", hash = "sha256:695b832d0091edd86eeb535cd39e45f3919f48d997685f7ac31acb15e0a2ed90"}, + {file = "msgspec-0.19.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:aa77046904db764b0462036bc63ef71f02b75b8f72e9c9dd4c447d6da1ed8f8e"}, + {file = "msgspec-0.19.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:047cfa8675eb3bad68722cfe95c60e7afabf84d1bd8938979dd2b92e9e4a9551"}, + {file = "msgspec-0.19.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e78f46ff39a427e10b4a61614a2777ad69559cc8d603a7c05681f5a595ea98f7"}, + {file = "msgspec-0.19.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c7adf191e4bd3be0e9231c3b6dc20cf1199ada2af523885efc2ed218eafd011"}, + {file = "msgspec-0.19.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f04cad4385e20be7c7176bb8ae3dca54a08e9756cfc97bcdb4f18560c3042063"}, + {file = "msgspec-0.19.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:45c8fb410670b3b7eb884d44a75589377c341ec1392b778311acdbfa55187716"}, + {file = "msgspec-0.19.0-cp311-cp311-win_amd64.whl", hash = "sha256:70eaef4934b87193a27d802534dc466778ad8d536e296ae2f9334e182ac27b6c"}, + {file = "msgspec-0.19.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f98bd8962ad549c27d63845b50af3f53ec468b6318400c9f1adfe8b092d7b62f"}, + {file = "msgspec-0.19.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:43bbb237feab761b815ed9df43b266114203f53596f9b6e6f00ebd79d178cdf2"}, + {file = "msgspec-0.19.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4cfc033c02c3e0aec52b71710d7f84cb3ca5eb407ab2ad23d75631153fdb1f12"}, + {file = "msgspec-0.19.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d911c442571605e17658ca2b416fd8579c5050ac9adc5e00c2cb3126c97f73bc"}, + {file = "msgspec-0.19.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:757b501fa57e24896cf40a831442b19a864f56d253679f34f260dcb002524a6c"}, + {file = "msgspec-0.19.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5f0f65f29b45e2816d8bded36e6b837a4bf5fb60ec4bc3c625fa2c6da4124537"}, + {file = "msgspec-0.19.0-cp312-cp312-win_amd64.whl", hash = "sha256:067f0de1c33cfa0b6a8206562efdf6be5985b988b53dd244a8e06f993f27c8c0"}, + {file = "msgspec-0.19.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f12d30dd6266557aaaf0aa0f9580a9a8fbeadfa83699c487713e355ec5f0bd86"}, + {file = "msgspec-0.19.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82b2c42c1b9ebc89e822e7e13bbe9d17ede0c23c187469fdd9505afd5a481314"}, + {file = "msgspec-0.19.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19746b50be214a54239aab822964f2ac81e38b0055cca94808359d779338c10e"}, + {file = "msgspec-0.19.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:60ef4bdb0ec8e4ad62e5a1f95230c08efb1f64f32e6e8dd2ced685bcc73858b5"}, + {file = "msgspec-0.19.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac7f7c377c122b649f7545810c6cd1b47586e3aa3059126ce3516ac7ccc6a6a9"}, + {file = "msgspec-0.19.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a5bc1472223a643f5ffb5bf46ccdede7f9795078194f14edd69e3aab7020d327"}, + {file = "msgspec-0.19.0-cp313-cp313-win_amd64.whl", hash = "sha256:317050bc0f7739cb30d257ff09152ca309bf5a369854bbf1e57dffc310c1f20f"}, + {file = "msgspec-0.19.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:15c1e86fff77184c20a2932cd9742bf33fe23125fa3fcf332df9ad2f7d483044"}, + {file = "msgspec-0.19.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3b5541b2b3294e5ffabe31a09d604e23a88533ace36ac288fa32a420aa38d229"}, + {file = "msgspec-0.19.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f5c043ace7962ef188746e83b99faaa9e3e699ab857ca3f367b309c8e2c6b12"}, + {file = "msgspec-0.19.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca06aa08e39bf57e39a258e1996474f84d0dd8130d486c00bec26d797b8c5446"}, + {file = "msgspec-0.19.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:e695dad6897896e9384cf5e2687d9ae9feaef50e802f93602d35458e20d1fb19"}, + {file = "msgspec-0.19.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:3be5c02e1fee57b54130316a08fe40cca53af92999a302a6054cd451700ea7db"}, + {file = "msgspec-0.19.0-cp39-cp39-win_amd64.whl", hash = "sha256:0684573a821be3c749912acf5848cce78af4298345cb2d7a8b8948a0a5a27cfe"}, + {file = "msgspec-0.19.0.tar.gz", hash = "sha256:604037e7cd475345848116e89c553aa9a233259733ab51986ac924ab1b976f8e"}, ] [package.extras] -dev = ["attrs", "coverage", "furo", "gcovr", "ipython", "msgpack", "mypy", "pre-commit", "pyright", "pytest", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "tomli", "tomli-w"] +dev = ["attrs", "coverage", "eval-type-backport", "furo", "ipython", "msgpack", "mypy", "pre-commit", "pyright", "pytest", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "tomli", "tomli_w"] doc = ["furo", "ipython", "sphinx", "sphinx-copybutton", "sphinx-design"] -test = ["attrs", "msgpack", "mypy", "pyright", "pytest", "pyyaml", "tomli", "tomli-w"] -toml = ["tomli", "tomli-w"] +test = ["attrs", "eval-type-backport", "msgpack", "pytest", "pyyaml", "tomli", "tomli_w"] +toml = ["tomli", "tomli_w"] yaml = ["pyyaml"] [[package]] @@ -2320,34 +2506,6 @@ files = [ [package.dependencies] typing-extensions = {version = ">=4.1.0", markers = "python_version < \"3.11\""} -[[package]] -name = "multiprocess" -version = "0.70.15" -description = "better multiprocessing and multithreading in Python" -optional = true -python-versions = ">=3.7" -files = [ - {file = "multiprocess-0.70.15-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:aa36c7ed16f508091438687fe9baa393a7a8e206731d321e443745e743a0d4e5"}, - {file = "multiprocess-0.70.15-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:20e024018c46d0d1602024c613007ac948f9754659e3853b0aa705e83f6931d8"}, - {file = "multiprocess-0.70.15-pp37-pypy37_pp73-manylinux_2_24_i686.whl", hash = "sha256:e576062981c91f0fe8a463c3d52506e598dfc51320a8dd8d78b987dfca91c5db"}, - {file = "multiprocess-0.70.15-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:e73f497e6696a0f5433ada2b3d599ae733b87a6e8b008e387c62ac9127add177"}, - {file = "multiprocess-0.70.15-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:73db2e7b32dcc7f9b0f075c2ffa45c90b6729d3f1805f27e88534c8d321a1be5"}, - {file = "multiprocess-0.70.15-pp38-pypy38_pp73-manylinux_2_24_i686.whl", hash = "sha256:4271647bd8a49c28ecd6eb56a7fdbd3c212c45529ad5303b40b3c65fc6928e5f"}, - {file = "multiprocess-0.70.15-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:cf981fb998d6ec3208cb14f0cf2e9e80216e834f5d51fd09ebc937c32b960902"}, - {file = "multiprocess-0.70.15-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:18f9f2c7063346d1617bd1684fdcae8d33380ae96b99427260f562e1a1228b67"}, - {file = "multiprocess-0.70.15-pp39-pypy39_pp73-manylinux_2_24_i686.whl", hash = "sha256:0eac53214d664c49a34695e5824872db4006b1a465edd7459a251809c3773370"}, - {file = "multiprocess-0.70.15-pp39-pypy39_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:1a51dd34096db47fb21fa2b839e615b051d51b97af9a67afbcdaa67186b44883"}, - {file = "multiprocess-0.70.15-py310-none-any.whl", hash = "sha256:7dd58e33235e83cf09d625e55cffd7b0f0eede7ee9223cdd666a87624f60c21a"}, - {file = "multiprocess-0.70.15-py311-none-any.whl", hash = "sha256:134f89053d82c9ed3b73edd3a2531eb791e602d4f4156fc92a79259590bd9670"}, - {file = "multiprocess-0.70.15-py37-none-any.whl", hash = "sha256:f7d4a1629bccb433114c3b4885f69eccc200994323c80f6feee73b0edc9199c5"}, - {file = "multiprocess-0.70.15-py38-none-any.whl", hash = "sha256:bee9afba476c91f9ebee7beeee0601face9eff67d822e893f9a893725fbd6316"}, - {file = "multiprocess-0.70.15-py39-none-any.whl", hash = "sha256:3e0953f5d52b4c76f1c973eaf8214554d146f2be5decb48e928e55c7a2d19338"}, - {file = "multiprocess-0.70.15.tar.gz", hash = "sha256:f20eed3036c0ef477b07a4177cf7c1ba520d9a2677870a4f47fe026f0cd6787e"}, -] - -[package.dependencies] -dill = ">=0.3.7" - [[package]] name = "mypy" version = "0.991" @@ -2438,40 +2596,6 @@ doc = ["nb2plots (>=0.7)", "nbconvert (<7.9)", "numpydoc (>=1.6)", "pillow (>=9. extra = ["lxml (>=4.6)", "pydot (>=1.4.2)", "pygraphviz (>=1.11)", "sympy (>=1.10)"] test = ["pytest (>=7.2)", "pytest-cov (>=4.0)"] -[[package]] -name = "numba" -version = "0.60.0" -description = "compiling Python code using LLVM" -optional = true -python-versions = ">=3.9" -files = [ - {file = "numba-0.60.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5d761de835cd38fb400d2c26bb103a2726f548dc30368853121d66201672e651"}, - {file = "numba-0.60.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:159e618ef213fba758837f9837fb402bbe65326e60ba0633dbe6c7f274d42c1b"}, - {file = "numba-0.60.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1527dc578b95c7c4ff248792ec33d097ba6bef9eda466c948b68dfc995c25781"}, - {file = "numba-0.60.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fe0b28abb8d70f8160798f4de9d486143200f34458d34c4a214114e445d7124e"}, - {file = "numba-0.60.0-cp310-cp310-win_amd64.whl", hash = "sha256:19407ced081d7e2e4b8d8c36aa57b7452e0283871c296e12d798852bc7d7f198"}, - {file = "numba-0.60.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a17b70fc9e380ee29c42717e8cc0bfaa5556c416d94f9aa96ba13acb41bdece8"}, - {file = "numba-0.60.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3fb02b344a2a80efa6f677aa5c40cd5dd452e1b35f8d1c2af0dfd9ada9978e4b"}, - {file = "numba-0.60.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5f4fde652ea604ea3c86508a3fb31556a6157b2c76c8b51b1d45eb40c8598703"}, - {file = "numba-0.60.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4142d7ac0210cc86432b818338a2bc368dc773a2f5cf1e32ff7c5b378bd63ee8"}, - {file = "numba-0.60.0-cp311-cp311-win_amd64.whl", hash = "sha256:cac02c041e9b5bc8cf8f2034ff6f0dbafccd1ae9590dc146b3a02a45e53af4e2"}, - {file = "numba-0.60.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d7da4098db31182fc5ffe4bc42c6f24cd7d1cb8a14b59fd755bfee32e34b8404"}, - {file = "numba-0.60.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:38d6ea4c1f56417076ecf8fc327c831ae793282e0ff51080c5094cb726507b1c"}, - {file = "numba-0.60.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:62908d29fb6a3229c242e981ca27e32a6e606cc253fc9e8faeb0e48760de241e"}, - {file = "numba-0.60.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0ebaa91538e996f708f1ab30ef4d3ddc344b64b5227b67a57aa74f401bb68b9d"}, - {file = "numba-0.60.0-cp312-cp312-win_amd64.whl", hash = "sha256:f75262e8fe7fa96db1dca93d53a194a38c46da28b112b8a4aca168f0df860347"}, - {file = "numba-0.60.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:01ef4cd7d83abe087d644eaa3d95831b777aa21d441a23703d649e06b8e06b74"}, - {file = "numba-0.60.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:819a3dfd4630d95fd574036f99e47212a1af41cbcb019bf8afac63ff56834449"}, - {file = "numba-0.60.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0b983bd6ad82fe868493012487f34eae8bf7dd94654951404114f23c3466d34b"}, - {file = "numba-0.60.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c151748cd269ddeab66334bd754817ffc0cabd9433acb0f551697e5151917d25"}, - {file = "numba-0.60.0-cp39-cp39-win_amd64.whl", hash = "sha256:3031547a015710140e8c87226b4cfe927cac199835e5bf7d4fe5cb64e814e3ab"}, - {file = "numba-0.60.0.tar.gz", hash = "sha256:5df6158e5584eece5fc83294b949fd30b9f1125df7708862205217e068aabf16"}, -] - -[package.dependencies] -llvmlite = "==0.43.*" -numpy = ">=1.22,<2.1" - [[package]] name = "numpy" version = "1.26.4" @@ -2519,46 +2643,50 @@ files = [ [[package]] name = "nvidia-cublas-cu12" -version = "12.1.3.1" +version = "12.4.5.8" description = "CUBLAS native runtime libraries" optional = false python-versions = ">=3" files = [ - {file = "nvidia_cublas_cu12-12.1.3.1-py3-none-manylinux1_x86_64.whl", hash = "sha256:ee53ccca76a6fc08fb9701aa95b6ceb242cdaab118c3bb152af4e579af792728"}, - {file = "nvidia_cublas_cu12-12.1.3.1-py3-none-win_amd64.whl", hash = "sha256:2b964d60e8cf11b5e1073d179d85fa340c120e99b3067558f3cf98dd69d02906"}, + {file = "nvidia_cublas_cu12-12.4.5.8-py3-none-manylinux2014_aarch64.whl", hash = "sha256:0f8aa1706812e00b9f19dfe0cdb3999b092ccb8ca168c0db5b8ea712456fd9b3"}, + {file = "nvidia_cublas_cu12-12.4.5.8-py3-none-manylinux2014_x86_64.whl", hash = "sha256:2fc8da60df463fdefa81e323eef2e36489e1c94335b5358bcb38360adf75ac9b"}, + {file = "nvidia_cublas_cu12-12.4.5.8-py3-none-win_amd64.whl", hash = "sha256:5a796786da89203a0657eda402bcdcec6180254a8ac22d72213abc42069522dc"}, ] [[package]] name = "nvidia-cuda-cupti-cu12" -version = "12.1.105" +version = "12.4.127" description = "CUDA profiling tools runtime libs." optional = false python-versions = ">=3" files = [ - {file = "nvidia_cuda_cupti_cu12-12.1.105-py3-none-manylinux1_x86_64.whl", hash = "sha256:e54fde3983165c624cb79254ae9818a456eb6e87a7fd4d56a2352c24ee542d7e"}, - {file = "nvidia_cuda_cupti_cu12-12.1.105-py3-none-win_amd64.whl", hash = "sha256:bea8236d13a0ac7190bd2919c3e8e6ce1e402104276e6f9694479e48bb0eb2a4"}, + {file = "nvidia_cuda_cupti_cu12-12.4.127-py3-none-manylinux2014_aarch64.whl", hash = "sha256:79279b35cf6f91da114182a5ce1864997fd52294a87a16179ce275773799458a"}, + {file = "nvidia_cuda_cupti_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:9dec60f5ac126f7bb551c055072b69d85392b13311fcc1bcda2202d172df30fb"}, + {file = "nvidia_cuda_cupti_cu12-12.4.127-py3-none-win_amd64.whl", hash = "sha256:5688d203301ab051449a2b1cb6690fbe90d2b372f411521c86018b950f3d7922"}, ] [[package]] name = "nvidia-cuda-nvrtc-cu12" -version = "12.1.105" +version = "12.4.127" description = "NVRTC native runtime libraries" optional = false python-versions = ">=3" files = [ - {file = "nvidia_cuda_nvrtc_cu12-12.1.105-py3-none-manylinux1_x86_64.whl", hash = "sha256:339b385f50c309763ca65456ec75e17bbefcbbf2893f462cb8b90584cd27a1c2"}, - {file = "nvidia_cuda_nvrtc_cu12-12.1.105-py3-none-win_amd64.whl", hash = "sha256:0a98a522d9ff138b96c010a65e145dc1b4850e9ecb75a0172371793752fd46ed"}, + {file = "nvidia_cuda_nvrtc_cu12-12.4.127-py3-none-manylinux2014_aarch64.whl", hash = "sha256:0eedf14185e04b76aa05b1fea04133e59f465b6f960c0cbf4e37c3cb6b0ea198"}, + {file = "nvidia_cuda_nvrtc_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a178759ebb095827bd30ef56598ec182b85547f1508941a3d560eb7ea1fbf338"}, + {file = "nvidia_cuda_nvrtc_cu12-12.4.127-py3-none-win_amd64.whl", hash = "sha256:a961b2f1d5f17b14867c619ceb99ef6fcec12e46612711bcec78eb05068a60ec"}, ] [[package]] name = "nvidia-cuda-runtime-cu12" -version = "12.1.105" +version = "12.4.127" description = "CUDA Runtime native Libraries" optional = false python-versions = ">=3" files = [ - {file = "nvidia_cuda_runtime_cu12-12.1.105-py3-none-manylinux1_x86_64.whl", hash = "sha256:6e258468ddf5796e25f1dc591a31029fa317d97a0a94ed93468fc86301d61e40"}, - {file = "nvidia_cuda_runtime_cu12-12.1.105-py3-none-win_amd64.whl", hash = "sha256:dfb46ef84d73fababab44cf03e3b83f80700d27ca300e537f85f636fac474344"}, + {file = "nvidia_cuda_runtime_cu12-12.4.127-py3-none-manylinux2014_aarch64.whl", hash = "sha256:961fe0e2e716a2a1d967aab7caee97512f71767f852f67432d572e36cb3a11f3"}, + {file = "nvidia_cuda_runtime_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:64403288fa2136ee8e467cdc9c9427e0434110899d07c779f25b5c068934faa5"}, + {file = "nvidia_cuda_runtime_cu12-12.4.127-py3-none-win_amd64.whl", hash = "sha256:09c2e35f48359752dfa822c09918211844a3d93c100a715d79b59591130c5e1e"}, ] [[package]] @@ -2577,35 +2705,41 @@ nvidia-cublas-cu12 = "*" [[package]] name = "nvidia-cufft-cu12" -version = "11.0.2.54" +version = "11.2.1.3" description = "CUFFT native runtime libraries" optional = false python-versions = ">=3" files = [ - {file = "nvidia_cufft_cu12-11.0.2.54-py3-none-manylinux1_x86_64.whl", hash = "sha256:794e3948a1aa71fd817c3775866943936774d1c14e7628c74f6f7417224cdf56"}, - {file = "nvidia_cufft_cu12-11.0.2.54-py3-none-win_amd64.whl", hash = "sha256:d9ac353f78ff89951da4af698f80870b1534ed69993f10a4cf1d96f21357e253"}, + {file = "nvidia_cufft_cu12-11.2.1.3-py3-none-manylinux2014_aarch64.whl", hash = "sha256:5dad8008fc7f92f5ddfa2101430917ce2ffacd86824914c82e28990ad7f00399"}, + {file = "nvidia_cufft_cu12-11.2.1.3-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f083fc24912aa410be21fa16d157fed2055dab1cc4b6934a0e03cba69eb242b9"}, + {file = "nvidia_cufft_cu12-11.2.1.3-py3-none-win_amd64.whl", hash = "sha256:d802f4954291101186078ccbe22fc285a902136f974d369540fd4a5333d1440b"}, ] +[package.dependencies] +nvidia-nvjitlink-cu12 = "*" + [[package]] name = "nvidia-curand-cu12" -version = "10.3.2.106" +version = "10.3.5.147" description = "CURAND native runtime libraries" optional = false python-versions = ">=3" files = [ - {file = "nvidia_curand_cu12-10.3.2.106-py3-none-manylinux1_x86_64.whl", hash = "sha256:9d264c5036dde4e64f1de8c50ae753237c12e0b1348738169cd0f8a536c0e1e0"}, - {file = "nvidia_curand_cu12-10.3.2.106-py3-none-win_amd64.whl", hash = "sha256:75b6b0c574c0037839121317e17fd01f8a69fd2ef8e25853d826fec30bdba74a"}, + {file = "nvidia_curand_cu12-10.3.5.147-py3-none-manylinux2014_aarch64.whl", hash = "sha256:1f173f09e3e3c76ab084aba0de819c49e56614feae5c12f69883f4ae9bb5fad9"}, + {file = "nvidia_curand_cu12-10.3.5.147-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a88f583d4e0bb643c49743469964103aa59f7f708d862c3ddb0fc07f851e3b8b"}, + {file = "nvidia_curand_cu12-10.3.5.147-py3-none-win_amd64.whl", hash = "sha256:f307cc191f96efe9e8f05a87096abc20d08845a841889ef78cb06924437f6771"}, ] [[package]] name = "nvidia-cusolver-cu12" -version = "11.4.5.107" +version = "11.6.1.9" description = "CUDA solver native runtime libraries" optional = false python-versions = ">=3" files = [ - {file = "nvidia_cusolver_cu12-11.4.5.107-py3-none-manylinux1_x86_64.whl", hash = "sha256:8a7ec542f0412294b15072fa7dab71d31334014a69f953004ea7a118206fe0dd"}, - {file = "nvidia_cusolver_cu12-11.4.5.107-py3-none-win_amd64.whl", hash = "sha256:74e0c3a24c78612192a74fcd90dd117f1cf21dea4822e66d89e8ea80e3cd2da5"}, + {file = "nvidia_cusolver_cu12-11.6.1.9-py3-none-manylinux2014_aarch64.whl", hash = "sha256:d338f155f174f90724bbde3758b7ac375a70ce8e706d70b018dd3375545fc84e"}, + {file = "nvidia_cusolver_cu12-11.6.1.9-py3-none-manylinux2014_x86_64.whl", hash = "sha256:19e33fa442bcfd085b3086c4ebf7e8debc07cfe01e11513cc6d332fd918ac260"}, + {file = "nvidia_cusolver_cu12-11.6.1.9-py3-none-win_amd64.whl", hash = "sha256:e77314c9d7b694fcebc84f58989f3aa4fb4cb442f12ca1a9bde50f5e8f6d1b9c"}, ] [package.dependencies] @@ -2615,13 +2749,14 @@ nvidia-nvjitlink-cu12 = "*" [[package]] name = "nvidia-cusparse-cu12" -version = "12.1.0.106" +version = "12.3.1.170" description = "CUSPARSE native runtime libraries" optional = false python-versions = ">=3" files = [ - {file = "nvidia_cusparse_cu12-12.1.0.106-py3-none-manylinux1_x86_64.whl", hash = "sha256:f3b50f42cf363f86ab21f720998517a659a48131e8d538dc02f8768237bd884c"}, - {file = "nvidia_cusparse_cu12-12.1.0.106-py3-none-win_amd64.whl", hash = "sha256:b798237e81b9719373e8fae8d4f091b70a0cf09d9d85c95a557e11df2d8e9a5a"}, + {file = "nvidia_cusparse_cu12-12.3.1.170-py3-none-manylinux2014_aarch64.whl", hash = "sha256:9d32f62896231ebe0480efd8a7f702e143c98cfaa0e8a76df3386c1ba2b54df3"}, + {file = "nvidia_cusparse_cu12-12.3.1.170-py3-none-manylinux2014_x86_64.whl", hash = "sha256:ea4f11a2904e2a8dc4b1833cc1b5181cde564edd0d5cd33e3c168eff2d1863f1"}, + {file = "nvidia_cusparse_cu12-12.3.1.170-py3-none-win_amd64.whl", hash = "sha256:9bc90fb087bc7b4c15641521f31c0371e9a612fc2ba12c338d3ae032e6b6797f"}, ] [package.dependencies] @@ -2640,13 +2775,12 @@ files = [ [[package]] name = "nvidia-nccl-cu12" -version = "2.20.5" +version = "2.21.5" description = "NVIDIA Collective Communication Library (NCCL) Runtime" optional = false python-versions = ">=3" files = [ - {file = "nvidia_nccl_cu12-2.20.5-py3-none-manylinux2014_aarch64.whl", hash = "sha256:1fc150d5c3250b170b29410ba682384b14581db722b2531b0d8d33c595f33d01"}, - {file = "nvidia_nccl_cu12-2.20.5-py3-none-manylinux2014_x86_64.whl", hash = "sha256:057f6bf9685f75215d0c53bf3ac4a10b3e6578351de307abad9e18a99182af56"}, + {file = "nvidia_nccl_cu12-2.21.5-py3-none-manylinux2014_x86_64.whl", hash = "sha256:8579076d30a8c24988834445f8d633c697d42397e92ffc3f63fa26766d25e0a0"}, ] [[package]] @@ -2656,20 +2790,20 @@ description = "Nvidia JIT LTO Library" optional = false python-versions = ">=3" files = [ - {file = "nvidia_nvjitlink_cu12-12.4.127-py3-none-manylinux2014_aarch64.whl", hash = "sha256:4abe7fef64914ccfa909bc2ba39739670ecc9e820c83ccc7a6ed414122599b83"}, {file = "nvidia_nvjitlink_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:06b3b9b25bf3f8af351d664978ca26a16d2c5127dbd53c0497e28d1fb9611d57"}, {file = "nvidia_nvjitlink_cu12-12.4.127-py3-none-win_amd64.whl", hash = "sha256:fd9020c501d27d135f983c6d3e244b197a7ccad769e34df53a42e276b0e25fa1"}, ] [[package]] name = "nvidia-nvtx-cu12" -version = "12.1.105" +version = "12.4.127" description = "NVIDIA Tools Extension" optional = false python-versions = ">=3" files = [ - {file = "nvidia_nvtx_cu12-12.1.105-py3-none-manylinux1_x86_64.whl", hash = "sha256:dc21cf308ca5691e7c04d962e213f8a4aa9bbfa23d95412f452254c2caeb09e5"}, - {file = "nvidia_nvtx_cu12-12.1.105-py3-none-win_amd64.whl", hash = "sha256:65f4d98982b31b60026e0e6de73fbdfc09d08a96f4656dd3665ca616a11e1e82"}, + {file = "nvidia_nvtx_cu12-12.4.127-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7959ad635db13edf4fc65c06a6e9f9e55fc2f92596db928d169c0bb031e88ef3"}, + {file = "nvidia_nvtx_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:781e950d9b9f60d8241ccea575b32f5105a5baf4c2351cab5256a24869f12a1a"}, + {file = "nvidia_nvtx_cu12-12.4.127-py3-none-win_amd64.whl", hash = "sha256:641dccaaa1139f3ffb0d3164b4b84f9d253397e38246a4f2f36728b48566d485"}, ] [[package]] @@ -2690,13 +2824,13 @@ signedtoken = ["cryptography (>=3.0.0)", "pyjwt (>=2.0.0,<3)"] [[package]] name = "openai" -version = "1.52.2" +version = "1.59.5" description = "The official Python library for the openai API" optional = true -python-versions = ">=3.7.1" +python-versions = ">=3.8" files = [ - {file = "openai-1.52.2-py3-none-any.whl", hash = "sha256:57e9e37bc407f39bb6ec3a27d7e8fb9728b2779936daa1fcf95df17d3edfaccc"}, - {file = "openai-1.52.2.tar.gz", hash = "sha256:87b7d0f69d85f5641678d414b7ee3082363647a5c66a462ed7f3ccb59582da0d"}, + {file = "openai-1.59.5-py3-none-any.whl", hash = "sha256:e646b44856b0dda9345d3c43639e056334d792d1690e99690313c0ef7ca4d8cc"}, + {file = "openai-1.59.5.tar.gz", hash = "sha256:9886e77c02dad9dc6a7b67a11ab372a56842a9b5d376aa476672175ab10e83a0"}, ] [package.dependencies] @@ -2711,6 +2845,34 @@ typing-extensions = ">=4.11,<5" [package.extras] datalib = ["numpy (>=1)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)"] +realtime = ["websockets (>=13,<15)"] + +[[package]] +name = "opencensus" +version = "0.11.4" +description = "A stats collection and distributed tracing framework" +optional = true +python-versions = "*" +files = [ + {file = "opencensus-0.11.4-py2.py3-none-any.whl", hash = "sha256:a18487ce68bc19900336e0ff4655c5a116daf10c1b3685ece8d971bddad6a864"}, + {file = "opencensus-0.11.4.tar.gz", hash = "sha256:cbef87d8b8773064ab60e5c2a1ced58bbaa38a6d052c41aec224958ce544eff2"}, +] + +[package.dependencies] +google-api-core = {version = ">=1.0.0,<3.0.0", markers = "python_version >= \"3.6\""} +opencensus-context = ">=0.1.3" +six = ">=1.16,<2.0" + +[[package]] +name = "opencensus-context" +version = "0.1.3" +description = "OpenCensus Runtime Context" +optional = true +python-versions = "*" +files = [ + {file = "opencensus-context-0.1.3.tar.gz", hash = "sha256:a03108c3c10d8c80bb5ddf5c8a1f033161fa61972a9917f9b9b3a18517f0088c"}, + {file = "opencensus_context-0.1.3-py2.py3-none-any.whl", hash = "sha256:073bb0590007af276853009fac7e4bab1d523c3f03baf4cb4511ca38967c6039"}, +] [[package]] name = "opencv-python-headless" @@ -2740,114 +2902,178 @@ numpy = [ [[package]] name = "orjson" -version = "3.10.10" +version = "3.10.14" description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" optional = false python-versions = ">=3.8" files = [ - {file = "orjson-3.10.10-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:b788a579b113acf1c57e0a68e558be71d5d09aa67f62ca1f68e01117e550a998"}, - {file = "orjson-3.10.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:804b18e2b88022c8905bb79bd2cbe59c0cd014b9328f43da8d3b28441995cda4"}, - {file = "orjson-3.10.10-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9972572a1d042ec9ee421b6da69f7cc823da5962237563fa548ab17f152f0b9b"}, - {file = "orjson-3.10.10-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc6993ab1c2ae7dd0711161e303f1db69062955ac2668181bfdf2dd410e65258"}, - {file = "orjson-3.10.10-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d78e4cacced5781b01d9bc0f0cd8b70b906a0e109825cb41c1b03f9c41e4ce86"}, - {file = "orjson-3.10.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e6eb2598df518281ba0cbc30d24c5b06124ccf7e19169e883c14e0831217a0bc"}, - {file = "orjson-3.10.10-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:23776265c5215ec532de6238a52707048401a568f0fa0d938008e92a147fe2c7"}, - {file = "orjson-3.10.10-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8cc2a654c08755cef90b468ff17c102e2def0edd62898b2486767204a7f5cc9c"}, - {file = "orjson-3.10.10-cp310-none-win32.whl", hash = "sha256:081b3fc6a86d72efeb67c13d0ea7c030017bd95f9868b1e329a376edc456153b"}, - {file = "orjson-3.10.10-cp310-none-win_amd64.whl", hash = "sha256:ff38c5fb749347768a603be1fb8a31856458af839f31f064c5aa74aca5be9efe"}, - {file = "orjson-3.10.10-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:879e99486c0fbb256266c7c6a67ff84f46035e4f8749ac6317cc83dacd7f993a"}, - {file = "orjson-3.10.10-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:019481fa9ea5ff13b5d5d95e6fd5ab25ded0810c80b150c2c7b1cc8660b662a7"}, - {file = "orjson-3.10.10-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0dd57eff09894938b4c86d4b871a479260f9e156fa7f12f8cad4b39ea8028bb5"}, - {file = "orjson-3.10.10-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dbde6d70cd95ab4d11ea8ac5e738e30764e510fc54d777336eec09bb93b8576c"}, - {file = "orjson-3.10.10-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b2625cb37b8fb42e2147404e5ff7ef08712099197a9cd38895006d7053e69d6"}, - {file = "orjson-3.10.10-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dbf3c20c6a7db69df58672a0d5815647ecf78c8e62a4d9bd284e8621c1fe5ccb"}, - {file = "orjson-3.10.10-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:75c38f5647e02d423807d252ce4528bf6a95bd776af999cb1fb48867ed01d1f6"}, - {file = "orjson-3.10.10-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:23458d31fa50ec18e0ec4b0b4343730928296b11111df5f547c75913714116b2"}, - {file = "orjson-3.10.10-cp311-none-win32.whl", hash = "sha256:2787cd9dedc591c989f3facd7e3e86508eafdc9536a26ec277699c0aa63c685b"}, - {file = "orjson-3.10.10-cp311-none-win_amd64.whl", hash = "sha256:6514449d2c202a75183f807bc755167713297c69f1db57a89a1ef4a0170ee269"}, - {file = "orjson-3.10.10-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:8564f48f3620861f5ef1e080ce7cd122ee89d7d6dacf25fcae675ff63b4d6e05"}, - {file = "orjson-3.10.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5bf161a32b479034098c5b81f2608f09167ad2fa1c06abd4e527ea6bf4837a9"}, - {file = "orjson-3.10.10-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:68b65c93617bcafa7f04b74ae8bc2cc214bd5cb45168a953256ff83015c6747d"}, - {file = "orjson-3.10.10-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e8e28406f97fc2ea0c6150f4c1b6e8261453318930b334abc419214c82314f85"}, - {file = "orjson-3.10.10-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e4d0d9fe174cc7a5bdce2e6c378bcdb4c49b2bf522a8f996aa586020e1b96cee"}, - {file = "orjson-3.10.10-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3be81c42f1242cbed03cbb3973501fcaa2675a0af638f8be494eaf37143d999"}, - {file = "orjson-3.10.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:65f9886d3bae65be026219c0a5f32dbbe91a9e6272f56d092ab22561ad0ea33b"}, - {file = "orjson-3.10.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:730ed5350147db7beb23ddaf072f490329e90a1d059711d364b49fe352ec987b"}, - {file = "orjson-3.10.10-cp312-none-win32.whl", hash = "sha256:a8f4bf5f1c85bea2170800020d53a8877812892697f9c2de73d576c9307a8a5f"}, - {file = "orjson-3.10.10-cp312-none-win_amd64.whl", hash = "sha256:384cd13579a1b4cd689d218e329f459eb9ddc504fa48c5a83ef4889db7fd7a4f"}, - {file = "orjson-3.10.10-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:44bffae68c291f94ff5a9b4149fe9d1bdd4cd0ff0fb575bcea8351d48db629a1"}, - {file = "orjson-3.10.10-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e27b4c6437315df3024f0835887127dac2a0a3ff643500ec27088d2588fa5ae1"}, - {file = "orjson-3.10.10-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bca84df16d6b49325a4084fd8b2fe2229cb415e15c46c529f868c3387bb1339d"}, - {file = "orjson-3.10.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c14ce70e8f39bd71f9f80423801b5d10bf93d1dceffdecd04df0f64d2c69bc01"}, - {file = "orjson-3.10.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:24ac62336da9bda1bd93c0491eff0613003b48d3cb5d01470842e7b52a40d5b4"}, - {file = "orjson-3.10.10-cp313-none-win32.whl", hash = "sha256:eb0a42831372ec2b05acc9ee45af77bcaccbd91257345f93780a8e654efc75db"}, - {file = "orjson-3.10.10-cp313-none-win_amd64.whl", hash = "sha256:f0c4f37f8bf3f1075c6cc8dd8a9f843689a4b618628f8812d0a71e6968b95ffd"}, - {file = "orjson-3.10.10-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:829700cc18503efc0cf502d630f612884258020d98a317679cd2054af0259568"}, - {file = "orjson-3.10.10-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e0ceb5e0e8c4f010ac787d29ae6299846935044686509e2f0f06ed441c1ca949"}, - {file = "orjson-3.10.10-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0c25908eb86968613216f3db4d3003f1c45d78eb9046b71056ca327ff92bdbd4"}, - {file = "orjson-3.10.10-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:218cb0bc03340144b6328a9ff78f0932e642199ac184dd74b01ad691f42f93ff"}, - {file = "orjson-3.10.10-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e2277ec2cea3775640dc81ab5195bb5b2ada2fe0ea6eee4677474edc75ea6785"}, - {file = "orjson-3.10.10-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:848ea3b55ab5ccc9d7bbd420d69432628b691fba3ca8ae3148c35156cbd282aa"}, - {file = "orjson-3.10.10-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:e3e67b537ac0c835b25b5f7d40d83816abd2d3f4c0b0866ee981a045287a54f3"}, - {file = "orjson-3.10.10-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:7948cfb909353fce2135dcdbe4521a5e7e1159484e0bb024c1722f272488f2b8"}, - {file = "orjson-3.10.10-cp38-none-win32.whl", hash = "sha256:78bee66a988f1a333dc0b6257503d63553b1957889c17b2c4ed72385cd1b96ae"}, - {file = "orjson-3.10.10-cp38-none-win_amd64.whl", hash = "sha256:f1d647ca8d62afeb774340a343c7fc023efacfd3a39f70c798991063f0c681dd"}, - {file = "orjson-3.10.10-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:5a059afddbaa6dd733b5a2d76a90dbc8af790b993b1b5cb97a1176ca713b5df8"}, - {file = "orjson-3.10.10-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f9b5c59f7e2a1a410f971c5ebc68f1995822837cd10905ee255f96074537ee6"}, - {file = "orjson-3.10.10-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d5ef198bafdef4aa9d49a4165ba53ffdc0a9e1c7b6f76178572ab33118afea25"}, - {file = "orjson-3.10.10-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aaf29ce0bb5d3320824ec3d1508652421000ba466abd63bdd52c64bcce9eb1fa"}, - {file = "orjson-3.10.10-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dddd5516bcc93e723d029c1633ae79c4417477b4f57dad9bfeeb6bc0315e654a"}, - {file = "orjson-3.10.10-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a12f2003695b10817f0fa8b8fca982ed7f5761dcb0d93cff4f2f9f6709903fd7"}, - {file = "orjson-3.10.10-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:672f9874a8a8fb9bb1b771331d31ba27f57702c8106cdbadad8bda5d10bc1019"}, - {file = "orjson-3.10.10-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1dcbb0ca5fafb2b378b2c74419480ab2486326974826bbf6588f4dc62137570a"}, - {file = "orjson-3.10.10-cp39-none-win32.whl", hash = "sha256:d9bbd3a4b92256875cb058c3381b782649b9a3c68a4aa9a2fff020c2f9cfc1be"}, - {file = "orjson-3.10.10-cp39-none-win_amd64.whl", hash = "sha256:766f21487a53aee8524b97ca9582d5c6541b03ab6210fbaf10142ae2f3ced2aa"}, - {file = "orjson-3.10.10.tar.gz", hash = "sha256:37949383c4df7b4337ce82ee35b6d7471e55195efa7dcb45ab8226ceadb0fe3b"}, + {file = "orjson-3.10.14-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:849ea7845a55f09965826e816cdc7689d6cf74fe9223d79d758c714af955bcb6"}, + {file = "orjson-3.10.14-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5947b139dfa33f72eecc63f17e45230a97e741942955a6c9e650069305eb73d"}, + {file = "orjson-3.10.14-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cde6d76910d3179dae70f164466692f4ea36da124d6fb1a61399ca589e81d69a"}, + {file = "orjson-3.10.14-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c6dfbaeb7afa77ca608a50e2770a0461177b63a99520d4928e27591b142c74b1"}, + {file = "orjson-3.10.14-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fa45e489ef80f28ff0e5ba0a72812b8cfc7c1ef8b46a694723807d1b07c89ebb"}, + {file = "orjson-3.10.14-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f5007abfdbb1d866e2aa8990bd1c465f0f6da71d19e695fc278282be12cffa5"}, + {file = "orjson-3.10.14-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1b49e2af011c84c3f2d541bb5cd1e3c7c2df672223e7e3ea608f09cf295e5f8a"}, + {file = "orjson-3.10.14-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:164ac155109226b3a2606ee6dda899ccfbe6e7e18b5bdc3fbc00f79cc074157d"}, + {file = "orjson-3.10.14-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:6b1225024cf0ef5d15934b5ffe9baf860fe8bc68a796513f5ea4f5056de30bca"}, + {file = "orjson-3.10.14-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:d6546e8073dc382e60fcae4a001a5a1bc46da5eab4a4878acc2d12072d6166d5"}, + {file = "orjson-3.10.14-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9f1d2942605c894162252d6259b0121bf1cb493071a1ea8cb35d79cb3e6ac5bc"}, + {file = "orjson-3.10.14-cp310-cp310-win32.whl", hash = "sha256:397083806abd51cf2b3bbbf6c347575374d160331a2d33c5823e22249ad3118b"}, + {file = "orjson-3.10.14-cp310-cp310-win_amd64.whl", hash = "sha256:fa18f949d3183a8d468367056be989666ac2bef3a72eece0bade9cdb733b3c28"}, + {file = "orjson-3.10.14-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:f506fd666dd1ecd15a832bebc66c4df45c1902fd47526292836c339f7ba665a9"}, + {file = "orjson-3.10.14-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efe5fd254cfb0eeee13b8ef7ecb20f5d5a56ddda8a587f3852ab2cedfefdb5f6"}, + {file = "orjson-3.10.14-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4ddc8c866d7467f5ee2991397d2ea94bcf60d0048bdd8ca555740b56f9042725"}, + {file = "orjson-3.10.14-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3af8e42ae4363773658b8d578d56dedffb4f05ceeb4d1d4dd3fb504950b45526"}, + {file = "orjson-3.10.14-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:84dd83110503bc10e94322bf3ffab8bc49150176b49b4984dc1cce4c0a993bf9"}, + {file = "orjson-3.10.14-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36f5bfc0399cd4811bf10ec7a759c7ab0cd18080956af8ee138097d5b5296a95"}, + {file = "orjson-3.10.14-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:868943660fb2a1e6b6b965b74430c16a79320b665b28dd4511d15ad5038d37d5"}, + {file = "orjson-3.10.14-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:33449c67195969b1a677533dee9d76e006001213a24501333624623e13c7cc8e"}, + {file = "orjson-3.10.14-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:e4c9f60f9fb0b5be66e416dcd8c9d94c3eabff3801d875bdb1f8ffc12cf86905"}, + {file = "orjson-3.10.14-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:0de4d6315cfdbd9ec803b945c23b3a68207fd47cbe43626036d97e8e9561a436"}, + {file = "orjson-3.10.14-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:83adda3db595cb1a7e2237029b3249c85afbe5c747d26b41b802e7482cb3933e"}, + {file = "orjson-3.10.14-cp311-cp311-win32.whl", hash = "sha256:998019ef74a4997a9d741b1473533cdb8faa31373afc9849b35129b4b8ec048d"}, + {file = "orjson-3.10.14-cp311-cp311-win_amd64.whl", hash = "sha256:9d034abdd36f0f0f2240f91492684e5043d46f290525d1117712d5b8137784eb"}, + {file = "orjson-3.10.14-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:2ad4b7e367efba6dc3f119c9a0fcd41908b7ec0399a696f3cdea7ec477441b09"}, + {file = "orjson-3.10.14-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f496286fc85e93ce0f71cc84fc1c42de2decf1bf494094e188e27a53694777a7"}, + {file = "orjson-3.10.14-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c7f189bbfcded40e41a6969c1068ba305850ba016665be71a217918931416fbf"}, + {file = "orjson-3.10.14-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8cc8204f0b75606869c707da331058ddf085de29558b516fc43c73ee5ee2aadb"}, + {file = "orjson-3.10.14-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:deaa2899dff7f03ab667e2ec25842d233e2a6a9e333efa484dfe666403f3501c"}, + {file = "orjson-3.10.14-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1c3ea52642c9714dc6e56de8a451a066f6d2707d273e07fe8a9cc1ba073813d"}, + {file = "orjson-3.10.14-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9d3f9ed72e7458ded9a1fb1b4d4ed4c4fdbaf82030ce3f9274b4dc1bff7ace2b"}, + {file = "orjson-3.10.14-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:07520685d408a2aba514c17ccc16199ff2934f9f9e28501e676c557f454a37fe"}, + {file = "orjson-3.10.14-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:76344269b550ea01488d19a2a369ab572c1ac4449a72e9f6ac0d70eb1cbfb953"}, + {file = "orjson-3.10.14-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e2979d0f2959990620f7e62da6cd954e4620ee815539bc57a8ae46e2dacf90e3"}, + {file = "orjson-3.10.14-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:03f61ca3674555adcb1aa717b9fc87ae936aa7a63f6aba90a474a88701278780"}, + {file = "orjson-3.10.14-cp312-cp312-win32.whl", hash = "sha256:d5075c54edf1d6ad81d4c6523ce54a748ba1208b542e54b97d8a882ecd810fd1"}, + {file = "orjson-3.10.14-cp312-cp312-win_amd64.whl", hash = "sha256:175cafd322e458603e8ce73510a068d16b6e6f389c13f69bf16de0e843d7d406"}, + {file = "orjson-3.10.14-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:0905ca08a10f7e0e0c97d11359609300eb1437490a7f32bbaa349de757e2e0c7"}, + {file = "orjson-3.10.14-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:92d13292249f9f2a3e418cbc307a9fbbef043c65f4bd8ba1eb620bc2aaba3d15"}, + {file = "orjson-3.10.14-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90937664e776ad316d64251e2fa2ad69265e4443067668e4727074fe39676414"}, + {file = "orjson-3.10.14-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9ed3d26c4cb4f6babaf791aa46a029265850e80ec2a566581f5c2ee1a14df4f1"}, + {file = "orjson-3.10.14-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:56ee546c2bbe9599aba78169f99d1dc33301853e897dbaf642d654248280dc6e"}, + {file = "orjson-3.10.14-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:901e826cb2f1bdc1fcef3ef59adf0c451e8f7c0b5deb26c1a933fb66fb505eae"}, + {file = "orjson-3.10.14-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:26336c0d4b2d44636e1e1e6ed1002f03c6aae4a8a9329561c8883f135e9ff010"}, + {file = "orjson-3.10.14-cp313-cp313-win32.whl", hash = "sha256:e2bc525e335a8545c4e48f84dd0328bc46158c9aaeb8a1c2276546e94540ea3d"}, + {file = "orjson-3.10.14-cp313-cp313-win_amd64.whl", hash = "sha256:eca04dfd792cedad53dc9a917da1a522486255360cb4e77619343a20d9f35364"}, + {file = "orjson-3.10.14-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9a0fba3b8a587a54c18585f077dcab6dd251c170d85cfa4d063d5746cd595a0f"}, + {file = "orjson-3.10.14-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:175abf3d20e737fec47261d278f95031736a49d7832a09ab684026528c4d96db"}, + {file = "orjson-3.10.14-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:29ca1a93e035d570e8b791b6c0feddd403c6a5388bfe870bf2aa6bba1b9d9b8e"}, + {file = "orjson-3.10.14-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f77202c80e8ab5a1d1e9faf642343bee5aaf332061e1ada4e9147dbd9eb00c46"}, + {file = "orjson-3.10.14-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6e2ec73b7099b6a29b40a62e08a23b936423bd35529f8f55c42e27acccde7954"}, + {file = "orjson-3.10.14-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2d1679df9f9cd9504f8dff24555c1eaabba8aad7f5914f28dab99e3c2552c9d"}, + {file = "orjson-3.10.14-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:691ab9a13834310a263664313e4f747ceb93662d14a8bdf20eb97d27ed488f16"}, + {file = "orjson-3.10.14-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:b11ed82054fce82fb74cea33247d825d05ad6a4015ecfc02af5fbce442fbf361"}, + {file = "orjson-3.10.14-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:e70a1d62b8288677d48f3bea66c21586a5f999c64ecd3878edb7393e8d1b548d"}, + {file = "orjson-3.10.14-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:16642f10c1ca5611251bd835de9914a4b03095e28a34c8ba6a5500b5074338bd"}, + {file = "orjson-3.10.14-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:3871bad546aa66c155e3f36f99c459780c2a392d502a64e23fb96d9abf338511"}, + {file = "orjson-3.10.14-cp38-cp38-win32.whl", hash = "sha256:0293a88815e9bb5c90af4045f81ed364d982f955d12052d989d844d6c4e50945"}, + {file = "orjson-3.10.14-cp38-cp38-win_amd64.whl", hash = "sha256:6169d3868b190d6b21adc8e61f64e3db30f50559dfbdef34a1cd6c738d409dfc"}, + {file = "orjson-3.10.14-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:06d4ec218b1ec1467d8d64da4e123b4794c781b536203c309ca0f52819a16c03"}, + {file = "orjson-3.10.14-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:962c2ec0dcaf22b76dee9831fdf0c4a33d4bf9a257a2bc5d4adc00d5c8ad9034"}, + {file = "orjson-3.10.14-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:21d3be4132f71ef1360385770474f29ea1538a242eef72ac4934fe142800e37f"}, + {file = "orjson-3.10.14-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c28ed60597c149a9e3f5ad6dd9cebaee6fb2f0e3f2d159a4a2b9b862d4748860"}, + {file = "orjson-3.10.14-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7e947f70167fe18469f2023644e91ab3d24f9aed69a5e1c78e2c81b9cea553fb"}, + {file = "orjson-3.10.14-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64410696c97a35af2432dea7bdc4ce32416458159430ef1b4beb79fd30093ad6"}, + {file = "orjson-3.10.14-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8050a5d81c022561ee29cd2739de5b4445f3c72f39423fde80a63299c1892c52"}, + {file = "orjson-3.10.14-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:b49a28e30d3eca86db3fe6f9b7f4152fcacbb4a467953cd1b42b94b479b77956"}, + {file = "orjson-3.10.14-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:ca041ad20291a65d853a9523744eebc3f5a4b2f7634e99f8fe88320695ddf766"}, + {file = "orjson-3.10.14-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:d313a2998b74bb26e9e371851a173a9b9474764916f1fc7971095699b3c6e964"}, + {file = "orjson-3.10.14-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7796692136a67b3e301ef9052bde6fe8e7bd5200da766811a3a608ffa62aaff0"}, + {file = "orjson-3.10.14-cp39-cp39-win32.whl", hash = "sha256:eee4bc767f348fba485ed9dc576ca58b0a9eac237f0e160f7a59bce628ed06b3"}, + {file = "orjson-3.10.14-cp39-cp39-win_amd64.whl", hash = "sha256:96a1c0ee30fb113b3ae3c748fd75ca74a157ff4c58476c47db4d61518962a011"}, + {file = "orjson-3.10.14.tar.gz", hash = "sha256:cf31f6f071a6b8e7aa1ead1fa27b935b48d00fbfa6a28ce856cfff2d5dd68eed"}, ] [[package]] name = "outlines" -version = "0.0.46" +version = "0.1.11" description = "Probabilistic Generative Model Programming" optional = true -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "outlines-0.0.46-py3-none-any.whl", hash = "sha256:fea7b2ccd2dd82ddf11849a55ef14c4d7b72f12136a67d1b105b81639c8ca2b0"}, - {file = "outlines-0.0.46.tar.gz", hash = "sha256:cd272418a268e0a25b7189180dfdcf9fe1b99f50ac1dfb9ffd83c896c5b3fa3c"}, + {file = "outlines-0.1.11-py3-none-any.whl", hash = "sha256:f5a5f2242ed9802d3aab7a92789bf4008d734c576be9258cc0a297f690124727"}, + {file = "outlines-0.1.11.tar.gz", hash = "sha256:0997bd9da1cc050e430bd08995dc7d4bd855918bafa4531e49d3f37110a23aba"}, ] [package.dependencies] +airportsdata = "*" cloudpickle = "*" -datasets = "*" diskcache = "*" interegular = "*" jinja2 = "*" jsonschema = "*" lark = "*" -nest-asyncio = "*" -numba = "*" -numpy = "<2.0.0" -pyairports = "*" +nest_asyncio = "*" +numpy = "*" +outlines_core = "0.1.26" pycountry = "*" pydantic = ">=2.0" referencing = "*" requests = "*" +torch = "*" tqdm = "*" -typing-extensions = "*" +typing_extensions = "*" [package.extras] +exllamav2 = ["exllamav2"] +llamacpp = ["datasets", "llama-cpp-python", "numpy (<2)", "transformers"] +mlxlm = ["datasets", "mlx-lm"] +openai = ["openai"] serve = ["fastapi", "pydantic (>=2.0)", "uvicorn", "vllm (>=0.3.0)"] -test = ["accelerate", "beartype (<0.16.0)", "coverage[toml] (>=5.1)", "diff-cover", "huggingface-hub", "llama-cpp-python", "mlx-lm", "openai (>=1.0.0)", "pre-commit", "pytest", "pytest-benchmark", "pytest-cov", "pytest-mock", "responses", "torch", "transformers", "vllm"] +test = ["accelerate", "beartype (<0.16.0)", "coverage[toml] (>=5.1)", "datasets", "diff-cover", "exllamav2", "huggingface_hub", "jax", "llama-cpp-python", "mlx-lm (>=0.19.2)", "openai (>=1.0.0)", "pillow", "pre-commit", "pytest", "pytest-benchmark", "pytest-cov", "pytest-mock", "responses", "transformers", "vllm"] +transformers = ["accelerate", "datasets", "numpy (<2)", "transformers"] +vllm = ["numpy (<2)", "transformers", "vllm"] + +[[package]] +name = "outlines-core" +version = "0.1.26" +description = "Structured Text Generation in Rust" +optional = true +python-versions = ">=3.8" +files = [ + {file = "outlines_core-0.1.26-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:6a962a7452e7ac170fa04d405342cadae2d28fafa5b1830cef7aa610257ed32f"}, + {file = "outlines_core-0.1.26-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:15a3684fa29564da2db03934cf0097bef3e871f70d3af0ef2b52fdb886da2e09"}, + {file = "outlines_core-0.1.26-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:64e01c0cfa9ba371634d7c3f6ea1862397cef98e4509fe98e3f57faa721a72d6"}, + {file = "outlines_core-0.1.26-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a3c4196148e47f455f1ace78e329d5b97e531cbc406456d681592952adae7e17"}, + {file = "outlines_core-0.1.26-cp310-cp310-win32.whl", hash = "sha256:f38d290a7f6e5e12cbfcaee03269dfc0dbda49b360024b4279d1aba251fdc346"}, + {file = "outlines_core-0.1.26-cp310-cp310-win_amd64.whl", hash = "sha256:11ff56af56cb54c563b7f25d86cd9ee77f3fed825f1d4dccd9449bb1e4e89538"}, + {file = "outlines_core-0.1.26-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:b6787b07b7c673fc3087d2b537719ecac8e03b10a47d032dd1926985c32885b0"}, + {file = "outlines_core-0.1.26-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e0ea28a76da31d25b6f53242bf13e1b59a0241badf82353c88f55e1cf81b128"}, + {file = "outlines_core-0.1.26-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a8932044a3d9329be53a226118850638f85b4d7842f9b863d0a123f23de220cd"}, + {file = "outlines_core-0.1.26-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a84b7cd2fb6268bf990dd3d479ffb4fa0bace6f571cb85b15b6cdb44b84f5b69"}, + {file = "outlines_core-0.1.26-cp311-cp311-win32.whl", hash = "sha256:f19765c151abfc970996368080aeea6d2a19e927817fe4e2af6726e639be3de4"}, + {file = "outlines_core-0.1.26-cp311-cp311-win_amd64.whl", hash = "sha256:3f59aeccea21ed6ff3cf52102fd163f26d279821c20e5127ddd18d4ea4d0c8d2"}, + {file = "outlines_core-0.1.26-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f54633bca50055d42ea4d94ae06dcbe52d3d76a9b621b75723b1177d0d952953"}, + {file = "outlines_core-0.1.26-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9525321b48700dcaaabf60bcdc951e45f9357ba3fb3e1bfc81b662d7d4170e7c"}, + {file = "outlines_core-0.1.26-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00f409f72c11f6ffadb57066950dd384d5388015028c1a1a615c9a64988dae3e"}, + {file = "outlines_core-0.1.26-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e86a1bb46adc5cbf6dfd7a7fe4105e0e2a4c6e041732a053126b41c521a1f223"}, + {file = "outlines_core-0.1.26-cp312-cp312-win32.whl", hash = "sha256:19f462f6b00935708677ad27cb4df55e0e17f6ffe713ab750f5f2683b090f95d"}, + {file = "outlines_core-0.1.26-cp312-cp312-win_amd64.whl", hash = "sha256:9b36bff12779e58883747116893a17b3551bbd10865878b951b03a44d112229a"}, + {file = "outlines_core-0.1.26-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:7b7849cf40028319ebb9d8ba0fe4c590ef5888eebe524a81b3af30aaa06ea21c"}, + {file = "outlines_core-0.1.26-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2f8641aab4a6bd84516907492ce82099503129da01b3c29c1dc9ad50320bae77"}, + {file = "outlines_core-0.1.26-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bba56604efdbc5932c7a8a88c2b8b0d0c740ab883b0012fb5464a9736796802b"}, + {file = "outlines_core-0.1.26-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cc8c87d89bd267356f8149c9066cbb98970425ec162997fbf195c3f1feb7009"}, + {file = "outlines_core-0.1.26-cp39-cp39-win32.whl", hash = "sha256:9d792a43ed9d8a4e1b38f4d83fe99db442d57aad4404c2edf98b710892eda47e"}, + {file = "outlines_core-0.1.26-cp39-cp39-win_amd64.whl", hash = "sha256:ad8564ecd7b64bcb840596c5049ff1c1a96346de494302ffcc0f2b188c15675e"}, + {file = "outlines_core-0.1.26.tar.gz", hash = "sha256:481c4301341e77cc8f1832d616784adb4d461b4fec65878e7c0d2cba7163a189"}, +] + +[package.dependencies] +interegular = "*" +jsonschema = "*" + +[package.extras] +test = ["accelerate", "asv", "beartype (<0.16.0)", "coverage[toml] (>=5.1)", "datasets", "diff-cover", "huggingface_hub", "numpy", "pillow", "pre-commit", "psutil", "pydantic", "pytest", "pytest-benchmark", "pytest-cov", "pytest-mock", "scipy", "setuptools-rust", "torch", "transformers"] [[package]] name = "packaging" -version = "24.1" +version = "24.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, - {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, + {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, + {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, ] [[package]] @@ -2938,13 +3164,13 @@ xml = ["lxml (>=4.9.2)"] [[package]] name = "partial-json-parser" -version = "0.2.1.1.post4" +version = "0.2.1.1.post5" description = "Parse partial JSON generated by LLM" optional = true python-versions = ">=3.6" files = [ - {file = "partial_json_parser-0.2.1.1.post4-py3-none-any.whl", hash = "sha256:960144ff29ab9358b69c17dca6e867687438f0dd206ea113677040f14f9ccedd"}, - {file = "partial_json_parser-0.2.1.1.post4.tar.gz", hash = "sha256:a38f11ceb89a86fbfbd0acad4170d417dcb317621c6ec9ab5f2b6652f3f460bf"}, + {file = "partial_json_parser-0.2.1.1.post5-py3-none-any.whl", hash = "sha256:627715aaa3cb3fb60a65b0d62223243acaa6c70846520a90326fef3a2f0b61ca"}, + {file = "partial_json_parser-0.2.1.1.post5.tar.gz", hash = "sha256:992710ac67e90b367921d52727698928040f7713ba7ecb33b96371ea7aec82ca"}, ] [package.extras] @@ -3139,109 +3365,93 @@ starlette = ">=0.30.0,<1.0.0" [[package]] name = "propcache" -version = "0.2.0" +version = "0.2.1" description = "Accelerated property cache" optional = true -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "propcache-0.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c5869b8fd70b81835a6f187c5fdbe67917a04d7e52b6e7cc4e5fe39d55c39d58"}, - {file = "propcache-0.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:952e0d9d07609d9c5be361f33b0d6d650cd2bae393aabb11d9b719364521984b"}, - {file = "propcache-0.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:33ac8f098df0585c0b53009f039dfd913b38c1d2edafed0cedcc0c32a05aa110"}, - {file = "propcache-0.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97e48e8875e6c13909c800fa344cd54cc4b2b0db1d5f911f840458a500fde2c2"}, - {file = "propcache-0.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:388f3217649d6d59292b722d940d4d2e1e6a7003259eb835724092a1cca0203a"}, - {file = "propcache-0.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f571aea50ba5623c308aa146eb650eebf7dbe0fd8c5d946e28343cb3b5aad577"}, - {file = "propcache-0.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3dfafb44f7bb35c0c06eda6b2ab4bfd58f02729e7c4045e179f9a861b07c9850"}, - {file = "propcache-0.2.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a3ebe9a75be7ab0b7da2464a77bb27febcb4fab46a34f9288f39d74833db7f61"}, - {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d2f0d0f976985f85dfb5f3d685697ef769faa6b71993b46b295cdbbd6be8cc37"}, - {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:a3dc1a4b165283bd865e8f8cb5f0c64c05001e0718ed06250d8cac9bec115b48"}, - {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:9e0f07b42d2a50c7dd2d8675d50f7343d998c64008f1da5fef888396b7f84630"}, - {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e63e3e1e0271f374ed489ff5ee73d4b6e7c60710e1f76af5f0e1a6117cd26394"}, - {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:56bb5c98f058a41bb58eead194b4db8c05b088c93d94d5161728515bd52b052b"}, - {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7665f04d0c7f26ff8bb534e1c65068409bf4687aa2534faf7104d7182debb336"}, - {file = "propcache-0.2.0-cp310-cp310-win32.whl", hash = "sha256:7cf18abf9764746b9c8704774d8b06714bcb0a63641518a3a89c7f85cc02c2ad"}, - {file = "propcache-0.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:cfac69017ef97db2438efb854edf24f5a29fd09a536ff3a992b75990720cdc99"}, - {file = "propcache-0.2.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:63f13bf09cc3336eb04a837490b8f332e0db41da66995c9fd1ba04552e516354"}, - {file = "propcache-0.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:608cce1da6f2672a56b24a015b42db4ac612ee709f3d29f27a00c943d9e851de"}, - {file = "propcache-0.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:466c219deee4536fbc83c08d09115249db301550625c7fef1c5563a584c9bc87"}, - {file = "propcache-0.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc2db02409338bf36590aa985a461b2c96fce91f8e7e0f14c50c5fcc4f229016"}, - {file = "propcache-0.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a6ed8db0a556343d566a5c124ee483ae113acc9a557a807d439bcecc44e7dfbb"}, - {file = "propcache-0.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:91997d9cb4a325b60d4e3f20967f8eb08dfcb32b22554d5ef78e6fd1dda743a2"}, - {file = "propcache-0.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c7dde9e533c0a49d802b4f3f218fa9ad0a1ce21f2c2eb80d5216565202acab4"}, - {file = "propcache-0.2.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffcad6c564fe6b9b8916c1aefbb37a362deebf9394bd2974e9d84232e3e08504"}, - {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:97a58a28bcf63284e8b4d7b460cbee1edaab24634e82059c7b8c09e65284f178"}, - {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:945db8ee295d3af9dbdbb698cce9bbc5c59b5c3fe328bbc4387f59a8a35f998d"}, - {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:39e104da444a34830751715f45ef9fc537475ba21b7f1f5b0f4d71a3b60d7fe2"}, - {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c5ecca8f9bab618340c8e848d340baf68bcd8ad90a8ecd7a4524a81c1764b3db"}, - {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:c436130cc779806bdf5d5fae0d848713105472b8566b75ff70048c47d3961c5b"}, - {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:191db28dc6dcd29d1a3e063c3be0b40688ed76434622c53a284e5427565bbd9b"}, - {file = "propcache-0.2.0-cp311-cp311-win32.whl", hash = "sha256:5f2564ec89058ee7c7989a7b719115bdfe2a2fb8e7a4543b8d1c0cc4cf6478c1"}, - {file = "propcache-0.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:6e2e54267980349b723cff366d1e29b138b9a60fa376664a157a342689553f71"}, - {file = "propcache-0.2.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:2ee7606193fb267be4b2e3b32714f2d58cad27217638db98a60f9efb5efeccc2"}, - {file = "propcache-0.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:91ee8fc02ca52e24bcb77b234f22afc03288e1dafbb1f88fe24db308910c4ac7"}, - {file = "propcache-0.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2e900bad2a8456d00a113cad8c13343f3b1f327534e3589acc2219729237a2e8"}, - {file = "propcache-0.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f52a68c21363c45297aca15561812d542f8fc683c85201df0bebe209e349f793"}, - {file = "propcache-0.2.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1e41d67757ff4fbc8ef2af99b338bfb955010444b92929e9e55a6d4dcc3c4f09"}, - {file = "propcache-0.2.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a64e32f8bd94c105cc27f42d3b658902b5bcc947ece3c8fe7bc1b05982f60e89"}, - {file = "propcache-0.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:55346705687dbd7ef0d77883ab4f6fabc48232f587925bdaf95219bae072491e"}, - {file = "propcache-0.2.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00181262b17e517df2cd85656fcd6b4e70946fe62cd625b9d74ac9977b64d8d9"}, - {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6994984550eaf25dd7fc7bd1b700ff45c894149341725bb4edc67f0ffa94efa4"}, - {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:56295eb1e5f3aecd516d91b00cfd8bf3a13991de5a479df9e27dd569ea23959c"}, - {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:439e76255daa0f8151d3cb325f6dd4a3e93043e6403e6491813bcaaaa8733887"}, - {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f6475a1b2ecb310c98c28d271a30df74f9dd436ee46d09236a6b750a7599ce57"}, - {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3444cdba6628accf384e349014084b1cacd866fbb88433cd9d279d90a54e0b23"}, - {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4a9d9b4d0a9b38d1c391bb4ad24aa65f306c6f01b512e10a8a34a2dc5675d348"}, - {file = "propcache-0.2.0-cp312-cp312-win32.whl", hash = "sha256:69d3a98eebae99a420d4b28756c8ce6ea5a29291baf2dc9ff9414b42676f61d5"}, - {file = "propcache-0.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:ad9c9b99b05f163109466638bd30ada1722abb01bbb85c739c50b6dc11f92dc3"}, - {file = "propcache-0.2.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ecddc221a077a8132cf7c747d5352a15ed763b674c0448d811f408bf803d9ad7"}, - {file = "propcache-0.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0e53cb83fdd61cbd67202735e6a6687a7b491c8742dfc39c9e01e80354956763"}, - {file = "propcache-0.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92fe151145a990c22cbccf9ae15cae8ae9eddabfc949a219c9f667877e40853d"}, - {file = "propcache-0.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6a21ef516d36909931a2967621eecb256018aeb11fc48656e3257e73e2e247a"}, - {file = "propcache-0.2.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f88a4095e913f98988f5b338c1d4d5d07dbb0b6bad19892fd447484e483ba6b"}, - {file = "propcache-0.2.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a5b3bb545ead161be780ee85a2b54fdf7092815995661947812dde94a40f6fb"}, - {file = "propcache-0.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67aeb72e0f482709991aa91345a831d0b707d16b0257e8ef88a2ad246a7280bf"}, - {file = "propcache-0.2.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c997f8c44ec9b9b0bcbf2d422cc00a1d9b9c681f56efa6ca149a941e5560da2"}, - {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2a66df3d4992bc1d725b9aa803e8c5a66c010c65c741ad901e260ece77f58d2f"}, - {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:3ebbcf2a07621f29638799828b8d8668c421bfb94c6cb04269130d8de4fb7136"}, - {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1235c01ddaa80da8235741e80815ce381c5267f96cc49b1477fdcf8c047ef325"}, - {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3947483a381259c06921612550867b37d22e1df6d6d7e8361264b6d037595f44"}, - {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:d5bed7f9805cc29c780f3aee05de3262ee7ce1f47083cfe9f77471e9d6777e83"}, - {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e4a91d44379f45f5e540971d41e4626dacd7f01004826a18cb048e7da7e96544"}, - {file = "propcache-0.2.0-cp313-cp313-win32.whl", hash = "sha256:f902804113e032e2cdf8c71015651c97af6418363bea8d78dc0911d56c335032"}, - {file = "propcache-0.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:8f188cfcc64fb1266f4684206c9de0e80f54622c3f22a910cbd200478aeae61e"}, - {file = "propcache-0.2.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:53d1bd3f979ed529f0805dd35ddaca330f80a9a6d90bc0121d2ff398f8ed8861"}, - {file = "propcache-0.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:83928404adf8fb3d26793665633ea79b7361efa0287dfbd372a7e74311d51ee6"}, - {file = "propcache-0.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:77a86c261679ea5f3896ec060be9dc8e365788248cc1e049632a1be682442063"}, - {file = "propcache-0.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:218db2a3c297a3768c11a34812e63b3ac1c3234c3a086def9c0fee50d35add1f"}, - {file = "propcache-0.2.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7735e82e3498c27bcb2d17cb65d62c14f1100b71723b68362872bca7d0913d90"}, - {file = "propcache-0.2.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:20a617c776f520c3875cf4511e0d1db847a076d720714ae35ffe0df3e440be68"}, - {file = "propcache-0.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67b69535c870670c9f9b14a75d28baa32221d06f6b6fa6f77a0a13c5a7b0a5b9"}, - {file = "propcache-0.2.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4569158070180c3855e9c0791c56be3ceeb192defa2cdf6a3f39e54319e56b89"}, - {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:db47514ffdbd91ccdc7e6f8407aac4ee94cc871b15b577c1c324236b013ddd04"}, - {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:2a60ad3e2553a74168d275a0ef35e8c0a965448ffbc3b300ab3a5bb9956c2162"}, - {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:662dd62358bdeaca0aee5761de8727cfd6861432e3bb828dc2a693aa0471a563"}, - {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:25a1f88b471b3bc911d18b935ecb7115dff3a192b6fef46f0bfaf71ff4f12418"}, - {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:f60f0ac7005b9f5a6091009b09a419ace1610e163fa5deaba5ce3484341840e7"}, - {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:74acd6e291f885678631b7ebc85d2d4aec458dd849b8c841b57ef04047833bed"}, - {file = "propcache-0.2.0-cp38-cp38-win32.whl", hash = "sha256:d9b6ddac6408194e934002a69bcaadbc88c10b5f38fb9307779d1c629181815d"}, - {file = "propcache-0.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:676135dcf3262c9c5081cc8f19ad55c8a64e3f7282a21266d05544450bffc3a5"}, - {file = "propcache-0.2.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:25c8d773a62ce0451b020c7b29a35cfbc05de8b291163a7a0f3b7904f27253e6"}, - {file = "propcache-0.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:375a12d7556d462dc64d70475a9ee5982465fbb3d2b364f16b86ba9135793638"}, - {file = "propcache-0.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1ec43d76b9677637a89d6ab86e1fef70d739217fefa208c65352ecf0282be957"}, - {file = "propcache-0.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f45eec587dafd4b2d41ac189c2156461ebd0c1082d2fe7013571598abb8505d1"}, - {file = "propcache-0.2.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bc092ba439d91df90aea38168e11f75c655880c12782facf5cf9c00f3d42b562"}, - {file = "propcache-0.2.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fa1076244f54bb76e65e22cb6910365779d5c3d71d1f18b275f1dfc7b0d71b4d"}, - {file = "propcache-0.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:682a7c79a2fbf40f5dbb1eb6bfe2cd865376deeac65acf9beb607505dced9e12"}, - {file = "propcache-0.2.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8e40876731f99b6f3c897b66b803c9e1c07a989b366c6b5b475fafd1f7ba3fb8"}, - {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:363ea8cd3c5cb6679f1c2f5f1f9669587361c062e4899fce56758efa928728f8"}, - {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:140fbf08ab3588b3468932974a9331aff43c0ab8a2ec2c608b6d7d1756dbb6cb"}, - {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e70fac33e8b4ac63dfc4c956fd7d85a0b1139adcfc0d964ce288b7c527537fea"}, - {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:b33d7a286c0dc1a15f5fc864cc48ae92a846df287ceac2dd499926c3801054a6"}, - {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:f6d5749fdd33d90e34c2efb174c7e236829147a2713334d708746e94c4bde40d"}, - {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:22aa8f2272d81d9317ff5756bb108021a056805ce63dd3630e27d042c8092798"}, - {file = "propcache-0.2.0-cp39-cp39-win32.whl", hash = "sha256:73e4b40ea0eda421b115248d7e79b59214411109a5bc47d0d48e4c73e3b8fcf9"}, - {file = "propcache-0.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:9517d5e9e0731957468c29dbfd0f976736a0e55afaea843726e887f36fe017df"}, - {file = "propcache-0.2.0-py3-none-any.whl", hash = "sha256:2ccc28197af5313706511fab3a8b66dcd6da067a1331372c82ea1cb74285e036"}, - {file = "propcache-0.2.0.tar.gz", hash = "sha256:df81779732feb9d01e5d513fad0122efb3d53bbc75f61b2a4f29a020bc985e70"}, + {file = "propcache-0.2.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6b3f39a85d671436ee3d12c017f8fdea38509e4f25b28eb25877293c98c243f6"}, + {file = "propcache-0.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d51fbe4285d5db5d92a929e3e21536ea3dd43732c5b177c7ef03f918dff9f2"}, + {file = "propcache-0.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6445804cf4ec763dc70de65a3b0d9954e868609e83850a47ca4f0cb64bd79fea"}, + {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9479aa06a793c5aeba49ce5c5692ffb51fcd9a7016e017d555d5e2b0045d212"}, + {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9631c5e8b5b3a0fda99cb0d29c18133bca1e18aea9effe55adb3da1adef80d3"}, + {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3156628250f46a0895f1f36e1d4fbe062a1af8718ec3ebeb746f1d23f0c5dc4d"}, + {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b6fb63ae352e13748289f04f37868099e69dba4c2b3e271c46061e82c745634"}, + {file = "propcache-0.2.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:887d9b0a65404929641a9fabb6452b07fe4572b269d901d622d8a34a4e9043b2"}, + {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a96dc1fa45bd8c407a0af03b2d5218392729e1822b0c32e62c5bf7eeb5fb3958"}, + {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:a7e65eb5c003a303b94aa2c3852ef130230ec79e349632d030e9571b87c4698c"}, + {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:999779addc413181912e984b942fbcc951be1f5b3663cd80b2687758f434c583"}, + {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:19a0f89a7bb9d8048d9c4370c9c543c396e894c76be5525f5e1ad287f1750ddf"}, + {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:1ac2f5fe02fa75f56e1ad473f1175e11f475606ec9bd0be2e78e4734ad575034"}, + {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:574faa3b79e8ebac7cb1d7930f51184ba1ccf69adfdec53a12f319a06030a68b"}, + {file = "propcache-0.2.1-cp310-cp310-win32.whl", hash = "sha256:03ff9d3f665769b2a85e6157ac8b439644f2d7fd17615a82fa55739bc97863f4"}, + {file = "propcache-0.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:2d3af2e79991102678f53e0dbf4c35de99b6b8b58f29a27ca0325816364caaba"}, + {file = "propcache-0.2.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1ffc3cca89bb438fb9c95c13fc874012f7b9466b89328c3c8b1aa93cdcfadd16"}, + {file = "propcache-0.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f174bbd484294ed9fdf09437f889f95807e5f229d5d93588d34e92106fbf6717"}, + {file = "propcache-0.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:70693319e0b8fd35dd863e3e29513875eb15c51945bf32519ef52927ca883bc3"}, + {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b480c6a4e1138e1aa137c0079b9b6305ec6dcc1098a8ca5196283e8a49df95a9"}, + {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d27b84d5880f6d8aa9ae3edb253c59d9f6642ffbb2c889b78b60361eed449787"}, + {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:857112b22acd417c40fa4595db2fe28ab900c8c5fe4670c7989b1c0230955465"}, + {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf6c4150f8c0e32d241436526f3c3f9cbd34429492abddbada2ffcff506c51af"}, + {file = "propcache-0.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66d4cfda1d8ed687daa4bc0274fcfd5267873db9a5bc0418c2da19273040eeb7"}, + {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c2f992c07c0fca81655066705beae35fc95a2fa7366467366db627d9f2ee097f"}, + {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:4a571d97dbe66ef38e472703067021b1467025ec85707d57e78711c085984e54"}, + {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:bb6178c241278d5fe853b3de743087be7f5f4c6f7d6d22a3b524d323eecec505"}, + {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ad1af54a62ffe39cf34db1aa6ed1a1873bd548f6401db39d8e7cd060b9211f82"}, + {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e7048abd75fe40712005bcfc06bb44b9dfcd8e101dda2ecf2f5aa46115ad07ca"}, + {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:160291c60081f23ee43d44b08a7e5fb76681221a8e10b3139618c5a9a291b84e"}, + {file = "propcache-0.2.1-cp311-cp311-win32.whl", hash = "sha256:819ce3b883b7576ca28da3861c7e1a88afd08cc8c96908e08a3f4dd64a228034"}, + {file = "propcache-0.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:edc9fc7051e3350643ad929df55c451899bb9ae6d24998a949d2e4c87fb596d3"}, + {file = "propcache-0.2.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:081a430aa8d5e8876c6909b67bd2d937bfd531b0382d3fdedb82612c618bc41a"}, + {file = "propcache-0.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d2ccec9ac47cf4e04897619c0e0c1a48c54a71bdf045117d3a26f80d38ab1fb0"}, + {file = "propcache-0.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:14d86fe14b7e04fa306e0c43cdbeebe6b2c2156a0c9ce56b815faacc193e320d"}, + {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:049324ee97bb67285b49632132db351b41e77833678432be52bdd0289c0e05e4"}, + {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1cd9a1d071158de1cc1c71a26014dcdfa7dd3d5f4f88c298c7f90ad6f27bb46d"}, + {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98110aa363f1bb4c073e8dcfaefd3a5cea0f0834c2aab23dda657e4dab2f53b5"}, + {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:647894f5ae99c4cf6bb82a1bb3a796f6e06af3caa3d32e26d2350d0e3e3faf24"}, + {file = "propcache-0.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfd3223c15bebe26518d58ccf9a39b93948d3dcb3e57a20480dfdd315356baff"}, + {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d71264a80f3fcf512eb4f18f59423fe82d6e346ee97b90625f283df56aee103f"}, + {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:e73091191e4280403bde6c9a52a6999d69cdfde498f1fdf629105247599b57ec"}, + {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3935bfa5fede35fb202c4b569bb9c042f337ca4ff7bd540a0aa5e37131659348"}, + {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f508b0491767bb1f2b87fdfacaba5f7eddc2f867740ec69ece6d1946d29029a6"}, + {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:1672137af7c46662a1c2be1e8dc78cb6d224319aaa40271c9257d886be4363a6"}, + {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b74c261802d3d2b85c9df2dfb2fa81b6f90deeef63c2db9f0e029a3cac50b518"}, + {file = "propcache-0.2.1-cp312-cp312-win32.whl", hash = "sha256:d09c333d36c1409d56a9d29b3a1b800a42c76a57a5a8907eacdbce3f18768246"}, + {file = "propcache-0.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:c214999039d4f2a5b2073ac506bba279945233da8c786e490d411dfc30f855c1"}, + {file = "propcache-0.2.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aca405706e0b0a44cc6bfd41fbe89919a6a56999157f6de7e182a990c36e37bc"}, + {file = "propcache-0.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:12d1083f001ace206fe34b6bdc2cb94be66d57a850866f0b908972f90996b3e9"}, + {file = "propcache-0.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d93f3307ad32a27bda2e88ec81134b823c240aa3abb55821a8da553eed8d9439"}, + {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba278acf14471d36316159c94a802933d10b6a1e117b8554fe0d0d9b75c9d536"}, + {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4e6281aedfca15301c41f74d7005e6e3f4ca143584ba696ac69df4f02f40d629"}, + {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5b750a8e5a1262434fb1517ddf64b5de58327f1adc3524a5e44c2ca43305eb0b"}, + {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf72af5e0fb40e9babf594308911436c8efde3cb5e75b6f206c34ad18be5c052"}, + {file = "propcache-0.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b2d0a12018b04f4cb820781ec0dffb5f7c7c1d2a5cd22bff7fb055a2cb19ebce"}, + {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e800776a79a5aabdb17dcc2346a7d66d0777e942e4cd251defeb084762ecd17d"}, + {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:4160d9283bd382fa6c0c2b5e017acc95bc183570cd70968b9202ad6d8fc48dce"}, + {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:30b43e74f1359353341a7adb783c8f1b1c676367b011709f466f42fda2045e95"}, + {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:58791550b27d5488b1bb52bc96328456095d96206a250d28d874fafe11b3dfaf"}, + {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:0f022d381747f0dfe27e99d928e31bc51a18b65bb9e481ae0af1380a6725dd1f"}, + {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:297878dc9d0a334358f9b608b56d02e72899f3b8499fc6044133f0d319e2ec30"}, + {file = "propcache-0.2.1-cp313-cp313-win32.whl", hash = "sha256:ddfab44e4489bd79bda09d84c430677fc7f0a4939a73d2bba3073036f487a0a6"}, + {file = "propcache-0.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:556fc6c10989f19a179e4321e5d678db8eb2924131e64652a51fe83e4c3db0e1"}, + {file = "propcache-0.2.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:6a9a8c34fb7bb609419a211e59da8887eeca40d300b5ea8e56af98f6fbbb1541"}, + {file = "propcache-0.2.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ae1aa1cd222c6d205853b3013c69cd04515f9d6ab6de4b0603e2e1c33221303e"}, + {file = "propcache-0.2.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:accb6150ce61c9c4b7738d45550806aa2b71c7668c6942f17b0ac182b6142fd4"}, + {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5eee736daafa7af6d0a2dc15cc75e05c64f37fc37bafef2e00d77c14171c2097"}, + {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7a31fc1e1bd362874863fdeed71aed92d348f5336fd84f2197ba40c59f061bd"}, + {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba4cfa1052819d16699e1d55d18c92b6e094d4517c41dd231a8b9f87b6fa681"}, + {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f089118d584e859c62b3da0892b88a83d611c2033ac410e929cb6754eec0ed16"}, + {file = "propcache-0.2.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:781e65134efaf88feb447e8c97a51772aa75e48b794352f94cb7ea717dedda0d"}, + {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:31f5af773530fd3c658b32b6bdc2d0838543de70eb9a2156c03e410f7b0d3aae"}, + {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:a7a078f5d37bee6690959c813977da5291b24286e7b962e62a94cec31aa5188b"}, + {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:cea7daf9fc7ae6687cf1e2c049752f19f146fdc37c2cc376e7d0032cf4f25347"}, + {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:8b3489ff1ed1e8315674d0775dc7d2195fb13ca17b3808721b54dbe9fd020faf"}, + {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9403db39be1393618dd80c746cb22ccda168efce239c73af13c3763ef56ffc04"}, + {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5d97151bc92d2b2578ff7ce779cdb9174337390a535953cbb9452fb65164c587"}, + {file = "propcache-0.2.1-cp39-cp39-win32.whl", hash = "sha256:9caac6b54914bdf41bcc91e7eb9147d331d29235a7c967c150ef5df6464fd1bb"}, + {file = "propcache-0.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:92fc4500fcb33899b05ba73276dfb684a20d31caa567b7cb5252d48f896a91b1"}, + {file = "propcache-0.2.1-py3-none-any.whl", hash = "sha256:52277518d6aae65536e9cea52d4e7fd2f7a66f4aa2d30ed3f2fcea620ace3c54"}, + {file = "propcache-0.2.1.tar.gz", hash = "sha256:3f77ce728b19cb537714499928fe800c3dda29e8d9428778fc7c186da4c09a64"}, ] [[package]] @@ -3321,70 +3531,22 @@ files = [ ] [[package]] -name = "pyairports" -version = "2.1.1" -description = "Airport and other locations database" +name = "py-spy" +version = "0.4.0" +description = "Sampling profiler for Python programs" optional = true python-versions = "*" files = [ - {file = "pyairports-2.1.1-py3-none-any.whl", hash = "sha256:3dfa0cc3e47696692ade92feccdc6b046968f2a75f5e30f65735d6db7251cb26"}, - {file = "pyairports-2.1.1.tar.gz", hash = "sha256:3d60a727fce4da81b9c6393ea8ae0b33d67b37ece344dffc863f749e3ad62bcd"}, + {file = "py_spy-0.4.0-py2.py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:f2cf3f7130e7d780471faa5957441d3b4e0ec39a79b2c00f4c33d494f7728428"}, + {file = "py_spy-0.4.0-py2.py3-none-macosx_11_0_arm64.whl", hash = "sha256:47cdda4c34d9b6cb01f3aaeceb2e88faf57da880207fe72ff6ff97e9bb6cc8a9"}, + {file = "py_spy-0.4.0-py2.py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eee3d0bde85ca5cf4f01f012d461180ca76c24835a96f7b5c4ded64eb6a008ab"}, + {file = "py_spy-0.4.0-py2.py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c5f06ffce4c9c98b7fc9f5e67e5e7db591173f1351837633f3f23d9378b1d18a"}, + {file = "py_spy-0.4.0-py2.py3-none-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:87573e64dbfdfc89ba2e0f5e2f525aa84e0299c7eb6454b47ea335fde583a7a0"}, + {file = "py_spy-0.4.0-py2.py3-none-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:8bf2f3702cef367a489faa45177b41a6c31b2a3e5bd78c978d44e29340152f5a"}, + {file = "py_spy-0.4.0-py2.py3-none-win_amd64.whl", hash = "sha256:77d8f637ade38367d944874776f45b703b7ac5938b1f7be8891f3a5876ddbb96"}, + {file = "py_spy-0.4.0.tar.gz", hash = "sha256:806602ce7972782cc9c1e383f339bfc27bfb822d42485e6a3e0530ae5040e1f0"}, ] -[[package]] -name = "pyarrow" -version = "18.0.0" -description = "Python library for Apache Arrow" -optional = true -python-versions = ">=3.9" -files = [ - {file = "pyarrow-18.0.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:2333f93260674e185cfbf208d2da3007132572e56871f451ba1a556b45dae6e2"}, - {file = "pyarrow-18.0.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:4c381857754da44326f3a49b8b199f7f87a51c2faacd5114352fc78de30d3aba"}, - {file = "pyarrow-18.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:603cd8ad4976568954598ef0a6d4ed3dfb78aff3d57fa8d6271f470f0ce7d34f"}, - {file = "pyarrow-18.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58a62549a3e0bc9e03df32f350e10e1efb94ec6cf63e3920c3385b26663948ce"}, - {file = "pyarrow-18.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:bc97316840a349485fbb137eb8d0f4d7057e1b2c1272b1a20eebbbe1848f5122"}, - {file = "pyarrow-18.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:2e549a748fa8b8715e734919923f69318c953e077e9c02140ada13e59d043310"}, - {file = "pyarrow-18.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:606e9a3dcb0f52307c5040698ea962685fb1c852d72379ee9412be7de9c5f9e2"}, - {file = "pyarrow-18.0.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:d5795e37c0a33baa618c5e054cd61f586cf76850a251e2b21355e4085def6280"}, - {file = "pyarrow-18.0.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:5f0510608ccd6e7f02ca8596962afb8c6cc84c453e7be0da4d85f5f4f7b0328a"}, - {file = "pyarrow-18.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:616ea2826c03c16e87f517c46296621a7c51e30400f6d0a61be645f203aa2b93"}, - {file = "pyarrow-18.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1824f5b029ddd289919f354bc285992cb4e32da518758c136271cf66046ef22"}, - {file = "pyarrow-18.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:6dd1b52d0d58dd8f685ced9971eb49f697d753aa7912f0a8f50833c7a7426319"}, - {file = "pyarrow-18.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:320ae9bd45ad7ecc12ec858b3e8e462578de060832b98fc4d671dee9f10d9954"}, - {file = "pyarrow-18.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:2c992716cffb1088414f2b478f7af0175fd0a76fea80841b1706baa8fb0ebaad"}, - {file = "pyarrow-18.0.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:e7ab04f272f98ebffd2a0661e4e126036f6936391ba2889ed2d44c5006237802"}, - {file = "pyarrow-18.0.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:03f40b65a43be159d2f97fd64dc998f769d0995a50c00f07aab58b0b3da87e1f"}, - {file = "pyarrow-18.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be08af84808dff63a76860847c48ec0416928a7b3a17c2f49a072cac7c45efbd"}, - {file = "pyarrow-18.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c70c1965cde991b711a98448ccda3486f2a336457cf4ec4dca257a926e149c9"}, - {file = "pyarrow-18.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:00178509f379415a3fcf855af020e3340254f990a8534294ec3cf674d6e255fd"}, - {file = "pyarrow-18.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:a71ab0589a63a3e987beb2bc172e05f000a5c5be2636b4b263c44034e215b5d7"}, - {file = "pyarrow-18.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:fe92efcdbfa0bcf2fa602e466d7f2905500f33f09eb90bf0bcf2e6ca41b574c8"}, - {file = "pyarrow-18.0.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:907ee0aa8ca576f5e0cdc20b5aeb2ad4d3953a3b4769fc4b499e00ef0266f02f"}, - {file = "pyarrow-18.0.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:66dcc216ebae2eb4c37b223feaf82f15b69d502821dde2da138ec5a3716e7463"}, - {file = "pyarrow-18.0.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc1daf7c425f58527900876354390ee41b0ae962a73ad0959b9d829def583bb1"}, - {file = "pyarrow-18.0.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:871b292d4b696b09120ed5bde894f79ee2a5f109cb84470546471df264cae136"}, - {file = "pyarrow-18.0.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:082ba62bdcb939824ba1ce10b8acef5ab621da1f4c4805e07bfd153617ac19d4"}, - {file = "pyarrow-18.0.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:2c664ab88b9766413197733c1720d3dcd4190e8fa3bbdc3710384630a0a7207b"}, - {file = "pyarrow-18.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:dc892be34dbd058e8d189b47db1e33a227d965ea8805a235c8a7286f7fd17d3a"}, - {file = "pyarrow-18.0.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:28f9c39a56d2c78bf6b87dcc699d520ab850919d4a8c7418cd20eda49874a2ea"}, - {file = "pyarrow-18.0.0-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:f1a198a50c409ab2d009fbf20956ace84567d67f2c5701511d4dd561fae6f32e"}, - {file = "pyarrow-18.0.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5bd7fd32e3ace012d43925ea4fc8bd1b02cc6cc1e9813b518302950e89b5a22"}, - {file = "pyarrow-18.0.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:336addb8b6f5208be1b2398442c703a710b6b937b1a046065ee4db65e782ff5a"}, - {file = "pyarrow-18.0.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:45476490dd4adec5472c92b4d253e245258745d0ccaabe706f8d03288ed60a79"}, - {file = "pyarrow-18.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:b46591222c864e7da7faa3b19455196416cd8355ff6c2cc2e65726a760a3c420"}, - {file = "pyarrow-18.0.0-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:eb7e3abcda7e1e6b83c2dc2909c8d045881017270a119cc6ee7fdcfe71d02df8"}, - {file = "pyarrow-18.0.0-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:09f30690b99ce34e0da64d20dab372ee54431745e4efb78ac938234a282d15f9"}, - {file = "pyarrow-18.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d5ca5d707e158540312e09fd907f9f49bacbe779ab5236d9699ced14d2293b8"}, - {file = "pyarrow-18.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d6331f280c6e4521c69b201a42dd978f60f7e129511a55da9e0bfe426b4ebb8d"}, - {file = "pyarrow-18.0.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:3ac24b2be732e78a5a3ac0b3aa870d73766dd00beba6e015ea2ea7394f8b4e55"}, - {file = "pyarrow-18.0.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:b30a927c6dff89ee702686596f27c25160dd6c99be5bcc1513a763ae5b1bfc03"}, - {file = "pyarrow-18.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:8f40ec677e942374e3d7f2fad6a67a4c2811a8b975e8703c6fd26d3b168a90e2"}, - {file = "pyarrow-18.0.0.tar.gz", hash = "sha256:a6aa027b1a9d2970cf328ccd6dbe4a996bc13c39fd427f502782f5bdb9ca20f5"}, -] - -[package.extras] -test = ["cffi", "hypothesis", "pandas", "pytest", "pytz"] - [[package]] name = "pyasn1" version = "0.6.1" @@ -3410,6 +3572,20 @@ files = [ [package.dependencies] pyasn1 = ">=0.4.6,<0.7.0" +[[package]] +name = "pybind11" +version = "2.13.6" +description = "Seamless operability between C++11 and Python" +optional = true +python-versions = ">=3.7" +files = [ + {file = "pybind11-2.13.6-py3-none-any.whl", hash = "sha256:237c41e29157b962835d356b370ededd57594a26d5894a795960f0047cb5caf5"}, + {file = "pybind11-2.13.6.tar.gz", hash = "sha256:ba6af10348c12b24e92fa086b39cfba0eff619b61ac77c406167d813b096d39a"}, +] + +[package.extras] +global = ["pybind11-global (==2.13.6)"] + [[package]] name = "pycountry" version = "24.6.1" @@ -3434,19 +3610,19 @@ files = [ [[package]] name = "pydantic" -version = "2.9.2" +version = "2.10.4" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic-2.9.2-py3-none-any.whl", hash = "sha256:f048cec7b26778210e28a0459867920654d48e5e62db0958433636cde4254f12"}, - {file = "pydantic-2.9.2.tar.gz", hash = "sha256:d155cef71265d1e9807ed1c32b4c8deec042a44a50a4188b25ac67ecd81a9c0f"}, + {file = "pydantic-2.10.4-py3-none-any.whl", hash = "sha256:597e135ea68be3a37552fb524bc7d0d66dcf93d395acd93a00682f1efcb8ee3d"}, + {file = "pydantic-2.10.4.tar.gz", hash = "sha256:82f12e9723da6de4fe2ba888b5971157b3be7ad914267dea8f05f82b28254f06"}, ] [package.dependencies] annotated-types = ">=0.6.0" -pydantic-core = "2.23.4" -typing-extensions = {version = ">=4.6.1", markers = "python_version < \"3.13\""} +pydantic-core = "2.27.2" +typing-extensions = ">=4.12.2" [package.extras] email = ["email-validator (>=2.0.0)"] @@ -3454,114 +3630,139 @@ timezone = ["tzdata"] [[package]] name = "pydantic-core" -version = "2.23.4" +version = "2.27.2" description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic_core-2.23.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:b10bd51f823d891193d4717448fab065733958bdb6a6b351967bd349d48d5c9b"}, - {file = "pydantic_core-2.23.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4fc714bdbfb534f94034efaa6eadd74e5b93c8fa6315565a222f7b6f42ca1166"}, - {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63e46b3169866bd62849936de036f901a9356e36376079b05efa83caeaa02ceb"}, - {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed1a53de42fbe34853ba90513cea21673481cd81ed1be739f7f2efb931b24916"}, - {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cfdd16ab5e59fc31b5e906d1a3f666571abc367598e3e02c83403acabc092e07"}, - {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255a8ef062cbf6674450e668482456abac99a5583bbafb73f9ad469540a3a232"}, - {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a7cd62e831afe623fbb7aabbb4fe583212115b3ef38a9f6b71869ba644624a2"}, - {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f09e2ff1f17c2b51f2bc76d1cc33da96298f0a036a137f5440ab3ec5360b624f"}, - {file = "pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e38e63e6f3d1cec5a27e0afe90a085af8b6806ee208b33030e65b6516353f1a3"}, - {file = "pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0dbd8dbed2085ed23b5c04afa29d8fd2771674223135dc9bc937f3c09284d071"}, - {file = "pydantic_core-2.23.4-cp310-none-win32.whl", hash = "sha256:6531b7ca5f951d663c339002e91aaebda765ec7d61b7d1e3991051906ddde119"}, - {file = "pydantic_core-2.23.4-cp310-none-win_amd64.whl", hash = "sha256:7c9129eb40958b3d4500fa2467e6a83356b3b61bfff1b414c7361d9220f9ae8f"}, - {file = "pydantic_core-2.23.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:77733e3892bb0a7fa797826361ce8a9184d25c8dffaec60b7ffe928153680ba8"}, - {file = "pydantic_core-2.23.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b84d168f6c48fabd1f2027a3d1bdfe62f92cade1fb273a5d68e621da0e44e6d"}, - {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df49e7a0861a8c36d089c1ed57d308623d60416dab2647a4a17fe050ba85de0e"}, - {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ff02b6d461a6de369f07ec15e465a88895f3223eb75073ffea56b84d9331f607"}, - {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:996a38a83508c54c78a5f41456b0103c30508fed9abcad0a59b876d7398f25fd"}, - {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d97683ddee4723ae8c95d1eddac7c192e8c552da0c73a925a89fa8649bf13eea"}, - {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:216f9b2d7713eb98cb83c80b9c794de1f6b7e3145eef40400c62e86cee5f4e1e"}, - {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6f783e0ec4803c787bcea93e13e9932edab72068f68ecffdf86a99fd5918878b"}, - {file = "pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d0776dea117cf5272382634bd2a5c1b6eb16767c223c6a5317cd3e2a757c61a0"}, - {file = "pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d5f7a395a8cf1621939692dba2a6b6a830efa6b3cee787d82c7de1ad2930de64"}, - {file = "pydantic_core-2.23.4-cp311-none-win32.whl", hash = "sha256:74b9127ffea03643e998e0c5ad9bd3811d3dac8c676e47db17b0ee7c3c3bf35f"}, - {file = "pydantic_core-2.23.4-cp311-none-win_amd64.whl", hash = "sha256:98d134c954828488b153d88ba1f34e14259284f256180ce659e8d83e9c05eaa3"}, - {file = "pydantic_core-2.23.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f3e0da4ebaef65158d4dfd7d3678aad692f7666877df0002b8a522cdf088f231"}, - {file = "pydantic_core-2.23.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f69a8e0b033b747bb3e36a44e7732f0c99f7edd5cea723d45bc0d6e95377ffee"}, - {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:723314c1d51722ab28bfcd5240d858512ffd3116449c557a1336cbe3919beb87"}, - {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb2802e667b7051a1bebbfe93684841cc9351004e2badbd6411bf357ab8d5ac8"}, - {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d18ca8148bebe1b0a382a27a8ee60350091a6ddaf475fa05ef50dc35b5df6327"}, - {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33e3d65a85a2a4a0dc3b092b938a4062b1a05f3a9abde65ea93b233bca0e03f2"}, - {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:128585782e5bfa515c590ccee4b727fb76925dd04a98864182b22e89a4e6ed36"}, - {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:68665f4c17edcceecc112dfed5dbe6f92261fb9d6054b47d01bf6371a6196126"}, - {file = "pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:20152074317d9bed6b7a95ade3b7d6054845d70584216160860425f4fbd5ee9e"}, - {file = "pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9261d3ce84fa1d38ed649c3638feefeae23d32ba9182963e465d58d62203bd24"}, - {file = "pydantic_core-2.23.4-cp312-none-win32.whl", hash = "sha256:4ba762ed58e8d68657fc1281e9bb72e1c3e79cc5d464be146e260c541ec12d84"}, - {file = "pydantic_core-2.23.4-cp312-none-win_amd64.whl", hash = "sha256:97df63000f4fea395b2824da80e169731088656d1818a11b95f3b173747b6cd9"}, - {file = "pydantic_core-2.23.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7530e201d10d7d14abce4fb54cfe5b94a0aefc87da539d0346a484ead376c3cc"}, - {file = "pydantic_core-2.23.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:df933278128ea1cd77772673c73954e53a1c95a4fdf41eef97c2b779271bd0bd"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cb3da3fd1b6a5d0279a01877713dbda118a2a4fc6f0d821a57da2e464793f05"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c6dcb030aefb668a2b7009c85b27f90e51e6a3b4d5c9bc4c57631292015b0d"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:696dd8d674d6ce621ab9d45b205df149399e4bb9aa34102c970b721554828510"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2971bb5ffe72cc0f555c13e19b23c85b654dd2a8f7ab493c262071377bfce9f6"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8394d940e5d400d04cad4f75c0598665cbb81aecefaca82ca85bd28264af7f9b"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0dff76e0602ca7d4cdaacc1ac4c005e0ce0dcfe095d5b5259163a80d3a10d327"}, - {file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7d32706badfe136888bdea71c0def994644e09fff0bfe47441deaed8e96fdbc6"}, - {file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ed541d70698978a20eb63d8c5d72f2cc6d7079d9d90f6b50bad07826f1320f5f"}, - {file = "pydantic_core-2.23.4-cp313-none-win32.whl", hash = "sha256:3d5639516376dce1940ea36edf408c554475369f5da2abd45d44621cb616f769"}, - {file = "pydantic_core-2.23.4-cp313-none-win_amd64.whl", hash = "sha256:5a1504ad17ba4210df3a045132a7baeeba5a200e930f57512ee02909fc5c4cb5"}, - {file = "pydantic_core-2.23.4-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d4488a93b071c04dc20f5cecc3631fc78b9789dd72483ba15d423b5b3689b555"}, - {file = "pydantic_core-2.23.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:81965a16b675b35e1d09dd14df53f190f9129c0202356ed44ab2728b1c905658"}, - {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ffa2ebd4c8530079140dd2d7f794a9d9a73cbb8e9d59ffe24c63436efa8f271"}, - {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:61817945f2fe7d166e75fbfb28004034b48e44878177fc54d81688e7b85a3665"}, - {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:29d2c342c4bc01b88402d60189f3df065fb0dda3654744d5a165a5288a657368"}, - {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5e11661ce0fd30a6790e8bcdf263b9ec5988e95e63cf901972107efc49218b13"}, - {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d18368b137c6295db49ce7218b1a9ba15c5bc254c96d7c9f9e924a9bc7825ad"}, - {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ec4e55f79b1c4ffb2eecd8a0cfba9955a2588497d96851f4c8f99aa4a1d39b12"}, - {file = "pydantic_core-2.23.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:374a5e5049eda9e0a44c696c7ade3ff355f06b1fe0bb945ea3cac2bc336478a2"}, - {file = "pydantic_core-2.23.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5c364564d17da23db1106787675fc7af45f2f7b58b4173bfdd105564e132e6fb"}, - {file = "pydantic_core-2.23.4-cp38-none-win32.whl", hash = "sha256:d7a80d21d613eec45e3d41eb22f8f94ddc758a6c4720842dc74c0581f54993d6"}, - {file = "pydantic_core-2.23.4-cp38-none-win_amd64.whl", hash = "sha256:5f5ff8d839f4566a474a969508fe1c5e59c31c80d9e140566f9a37bba7b8d556"}, - {file = "pydantic_core-2.23.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a4fa4fc04dff799089689f4fd502ce7d59de529fc2f40a2c8836886c03e0175a"}, - {file = "pydantic_core-2.23.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0a7df63886be5e270da67e0966cf4afbae86069501d35c8c1b3b6c168f42cb36"}, - {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcedcd19a557e182628afa1d553c3895a9f825b936415d0dbd3cd0bbcfd29b4b"}, - {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f54b118ce5de9ac21c363d9b3caa6c800341e8c47a508787e5868c6b79c9323"}, - {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86d2f57d3e1379a9525c5ab067b27dbb8a0642fb5d454e17a9ac434f9ce523e3"}, - {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:de6d1d1b9e5101508cb37ab0d972357cac5235f5c6533d1071964c47139257df"}, - {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1278e0d324f6908e872730c9102b0112477a7f7cf88b308e4fc36ce1bdb6d58c"}, - {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9a6b5099eeec78827553827f4c6b8615978bb4b6a88e5d9b93eddf8bb6790f55"}, - {file = "pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e55541f756f9b3ee346b840103f32779c695a19826a4c442b7954550a0972040"}, - {file = "pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a5c7ba8ffb6d6f8f2ab08743be203654bb1aaa8c9dcb09f82ddd34eadb695605"}, - {file = "pydantic_core-2.23.4-cp39-none-win32.whl", hash = "sha256:37b0fe330e4a58d3c58b24d91d1eb102aeec675a3db4c292ec3928ecd892a9a6"}, - {file = "pydantic_core-2.23.4-cp39-none-win_amd64.whl", hash = "sha256:1498bec4c05c9c787bde9125cfdcc63a41004ff167f495063191b863399b1a29"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f455ee30a9d61d3e1a15abd5068827773d6e4dc513e795f380cdd59932c782d5"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1e90d2e3bd2c3863d48525d297cd143fe541be8bbf6f579504b9712cb6b643ec"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e203fdf807ac7e12ab59ca2bfcabb38c7cf0b33c41efeb00f8e5da1d86af480"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e08277a400de01bc72436a0ccd02bdf596631411f592ad985dcee21445bd0068"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f220b0eea5965dec25480b6333c788fb72ce5f9129e8759ef876a1d805d00801"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d06b0c8da4f16d1d1e352134427cb194a0a6e19ad5db9161bf32b2113409e728"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ba1a0996f6c2773bd83e63f18914c1de3c9dd26d55f4ac302a7efe93fb8e7433"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:9a5bce9d23aac8f0cf0836ecfc033896aa8443b501c58d0602dbfd5bd5b37753"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:78ddaaa81421a29574a682b3179d4cf9e6d405a09b99d93ddcf7e5239c742e21"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:883a91b5dd7d26492ff2f04f40fbb652de40fcc0afe07e8129e8ae779c2110eb"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88ad334a15b32a791ea935af224b9de1bf99bcd62fabf745d5f3442199d86d59"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:233710f069d251feb12a56da21e14cca67994eab08362207785cf8c598e74577"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:19442362866a753485ba5e4be408964644dd6a09123d9416c54cd49171f50744"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:624e278a7d29b6445e4e813af92af37820fafb6dcc55c012c834f9e26f9aaaef"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f5ef8f42bec47f21d07668a043f077d507e5bf4e668d5c6dfe6aaba89de1a5b8"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:aea443fffa9fbe3af1a9ba721a87f926fe548d32cab71d188a6ede77d0ff244e"}, - {file = "pydantic_core-2.23.4.tar.gz", hash = "sha256:2584f7cf844ac4d970fba483a717dbe10c1c1c96a969bf65d61ffe94df1b2863"}, + {file = "pydantic_core-2.27.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2d367ca20b2f14095a8f4fa1210f5a7b78b8a20009ecced6b12818f455b1e9fa"}, + {file = "pydantic_core-2.27.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:491a2b73db93fab69731eaee494f320faa4e093dbed776be1a829c2eb222c34c"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7969e133a6f183be60e9f6f56bfae753585680f3b7307a8e555a948d443cc05a"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3de9961f2a346257caf0aa508a4da705467f53778e9ef6fe744c038119737ef5"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e2bb4d3e5873c37bb3dd58714d4cd0b0e6238cebc4177ac8fe878f8b3aa8e74c"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:280d219beebb0752699480fe8f1dc61ab6615c2046d76b7ab7ee38858de0a4e7"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47956ae78b6422cbd46f772f1746799cbb862de838fd8d1fbd34a82e05b0983a"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:14d4a5c49d2f009d62a2a7140d3064f686d17a5d1a268bc641954ba181880236"}, + {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:337b443af21d488716f8d0b6164de833e788aa6bd7e3a39c005febc1284f4962"}, + {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:03d0f86ea3184a12f41a2d23f7ccb79cdb5a18e06993f8a45baa8dfec746f0e9"}, + {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7041c36f5680c6e0f08d922aed302e98b3745d97fe1589db0a3eebf6624523af"}, + {file = "pydantic_core-2.27.2-cp310-cp310-win32.whl", hash = "sha256:50a68f3e3819077be2c98110c1f9dcb3817e93f267ba80a2c05bb4f8799e2ff4"}, + {file = "pydantic_core-2.27.2-cp310-cp310-win_amd64.whl", hash = "sha256:e0fd26b16394ead34a424eecf8a31a1f5137094cabe84a1bcb10fa6ba39d3d31"}, + {file = "pydantic_core-2.27.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:8e10c99ef58cfdf2a66fc15d66b16c4a04f62bca39db589ae8cba08bc55331bc"}, + {file = "pydantic_core-2.27.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:26f32e0adf166a84d0cb63be85c562ca8a6fa8de28e5f0d92250c6b7e9e2aff7"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c19d1ea0673cd13cc2f872f6c9ab42acc4e4f492a7ca9d3795ce2b112dd7e15"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e68c4446fe0810e959cdff46ab0a41ce2f2c86d227d96dc3847af0ba7def306"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9640b0059ff4f14d1f37321b94061c6db164fbe49b334b31643e0528d100d99"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40d02e7d45c9f8af700f3452f329ead92da4c5f4317ca9b896de7ce7199ea459"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c1fd185014191700554795c99b347d64f2bb637966c4cfc16998a0ca700d048"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d81d2068e1c1228a565af076598f9e7451712700b673de8f502f0334f281387d"}, + {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1a4207639fb02ec2dbb76227d7c751a20b1a6b4bc52850568e52260cae64ca3b"}, + {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:3de3ce3c9ddc8bbd88f6e0e304dea0e66d843ec9de1b0042b0911c1663ffd474"}, + {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:30c5f68ded0c36466acede341551106821043e9afaad516adfb6e8fa80a4e6a6"}, + {file = "pydantic_core-2.27.2-cp311-cp311-win32.whl", hash = "sha256:c70c26d2c99f78b125a3459f8afe1aed4d9687c24fd677c6a4436bc042e50d6c"}, + {file = "pydantic_core-2.27.2-cp311-cp311-win_amd64.whl", hash = "sha256:08e125dbdc505fa69ca7d9c499639ab6407cfa909214d500897d02afb816e7cc"}, + {file = "pydantic_core-2.27.2-cp311-cp311-win_arm64.whl", hash = "sha256:26f0d68d4b235a2bae0c3fc585c585b4ecc51382db0e3ba402a22cbc440915e4"}, + {file = "pydantic_core-2.27.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9e0c8cfefa0ef83b4da9588448b6d8d2a2bf1a53c3f1ae5fca39eb3061e2f0b0"}, + {file = "pydantic_core-2.27.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83097677b8e3bd7eaa6775720ec8e0405f1575015a463285a92bfdfe254529ef"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:172fce187655fece0c90d90a678424b013f8fbb0ca8b036ac266749c09438cb7"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:519f29f5213271eeeeb3093f662ba2fd512b91c5f188f3bb7b27bc5973816934"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05e3a55d124407fffba0dd6b0c0cd056d10e983ceb4e5dbd10dda135c31071d6"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c3ed807c7b91de05e63930188f19e921d1fe90de6b4f5cd43ee7fcc3525cb8c"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fb4aadc0b9a0c063206846d603b92030eb6f03069151a625667f982887153e2"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28ccb213807e037460326424ceb8b5245acb88f32f3d2777427476e1b32c48c4"}, + {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:de3cd1899e2c279b140adde9357c4495ed9d47131b4a4eaff9052f23398076b3"}, + {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:220f892729375e2d736b97d0e51466252ad84c51857d4d15f5e9692f9ef12be4"}, + {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a0fcd29cd6b4e74fe8ddd2c90330fd8edf2e30cb52acda47f06dd615ae72da57"}, + {file = "pydantic_core-2.27.2-cp312-cp312-win32.whl", hash = "sha256:1e2cb691ed9834cd6a8be61228471d0a503731abfb42f82458ff27be7b2186fc"}, + {file = "pydantic_core-2.27.2-cp312-cp312-win_amd64.whl", hash = "sha256:cc3f1a99a4f4f9dd1de4fe0312c114e740b5ddead65bb4102884b384c15d8bc9"}, + {file = "pydantic_core-2.27.2-cp312-cp312-win_arm64.whl", hash = "sha256:3911ac9284cd8a1792d3cb26a2da18f3ca26c6908cc434a18f730dc0db7bfa3b"}, + {file = "pydantic_core-2.27.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7d14bd329640e63852364c306f4d23eb744e0f8193148d4044dd3dacdaacbd8b"}, + {file = "pydantic_core-2.27.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82f91663004eb8ed30ff478d77c4d1179b3563df6cdb15c0817cd1cdaf34d154"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71b24c7d61131bb83df10cc7e687433609963a944ccf45190cfc21e0887b08c9"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa8e459d4954f608fa26116118bb67f56b93b209c39b008277ace29937453dc9"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce8918cbebc8da707ba805b7fd0b382816858728ae7fe19a942080c24e5b7cd1"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eda3f5c2a021bbc5d976107bb302e0131351c2ba54343f8a496dc8783d3d3a6a"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8086fa684c4775c27f03f062cbb9eaa6e17f064307e86b21b9e0abc9c0f02e"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8d9b3388db186ba0c099a6d20f0604a44eabdeef1777ddd94786cdae158729e4"}, + {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7a66efda2387de898c8f38c0cf7f14fca0b51a8ef0b24bfea5849f1b3c95af27"}, + {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:18a101c168e4e092ab40dbc2503bdc0f62010e95d292b27827871dc85450d7ee"}, + {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ba5dd002f88b78a4215ed2f8ddbdf85e8513382820ba15ad5ad8955ce0ca19a1"}, + {file = "pydantic_core-2.27.2-cp313-cp313-win32.whl", hash = "sha256:1ebaf1d0481914d004a573394f4be3a7616334be70261007e47c2a6fe7e50130"}, + {file = "pydantic_core-2.27.2-cp313-cp313-win_amd64.whl", hash = "sha256:953101387ecf2f5652883208769a79e48db18c6df442568a0b5ccd8c2723abee"}, + {file = "pydantic_core-2.27.2-cp313-cp313-win_arm64.whl", hash = "sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b"}, + {file = "pydantic_core-2.27.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d3e8d504bdd3f10835468f29008d72fc8359d95c9c415ce6e767203db6127506"}, + {file = "pydantic_core-2.27.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:521eb9b7f036c9b6187f0b47318ab0d7ca14bd87f776240b90b21c1f4f149320"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85210c4d99a0114f5a9481b44560d7d1e35e32cc5634c656bc48e590b669b145"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d716e2e30c6f140d7560ef1538953a5cd1a87264c737643d481f2779fc247fe1"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f66d89ba397d92f840f8654756196d93804278457b5fbede59598a1f9f90b228"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:669e193c1c576a58f132e3158f9dfa9662969edb1a250c54d8fa52590045f046"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdbe7629b996647b99c01b37f11170a57ae675375b14b8c13b8518b8320ced5"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d262606bf386a5ba0b0af3b97f37c83d7011439e3dc1a9298f21efb292e42f1a"}, + {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:cabb9bcb7e0d97f74df8646f34fc76fbf793b7f6dc2438517d7a9e50eee4f14d"}, + {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_armv7l.whl", hash = "sha256:d2d63f1215638d28221f664596b1ccb3944f6e25dd18cd3b86b0a4c408d5ebb9"}, + {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bca101c00bff0adb45a833f8451b9105d9df18accb8743b08107d7ada14bd7da"}, + {file = "pydantic_core-2.27.2-cp38-cp38-win32.whl", hash = "sha256:f6f8e111843bbb0dee4cb6594cdc73e79b3329b526037ec242a3e49012495b3b"}, + {file = "pydantic_core-2.27.2-cp38-cp38-win_amd64.whl", hash = "sha256:fd1aea04935a508f62e0d0ef1f5ae968774a32afc306fb8545e06f5ff5cdf3ad"}, + {file = "pydantic_core-2.27.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:c10eb4f1659290b523af58fa7cffb452a61ad6ae5613404519aee4bfbf1df993"}, + {file = "pydantic_core-2.27.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ef592d4bad47296fb11f96cd7dc898b92e795032b4894dfb4076cfccd43a9308"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c61709a844acc6bf0b7dce7daae75195a10aac96a596ea1b776996414791ede4"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c5f762659e47fdb7b16956c71598292f60a03aa92f8b6351504359dbdba6cf"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c9775e339e42e79ec99c441d9730fccf07414af63eac2f0e48e08fd38a64d76"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57762139821c31847cfb2df63c12f725788bd9f04bc2fb392790959b8f70f118"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d1e85068e818c73e048fe28cfc769040bb1f475524f4745a5dc621f75ac7630"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:097830ed52fd9e427942ff3b9bc17fab52913b2f50f2880dc4a5611446606a54"}, + {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:044a50963a614ecfae59bb1eaf7ea7efc4bc62f49ed594e18fa1e5d953c40e9f"}, + {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:4e0b4220ba5b40d727c7f879eac379b822eee5d8fff418e9d3381ee45b3b0362"}, + {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5e4f4bb20d75e9325cc9696c6802657b58bc1dbbe3022f32cc2b2b632c3fbb96"}, + {file = "pydantic_core-2.27.2-cp39-cp39-win32.whl", hash = "sha256:cca63613e90d001b9f2f9a9ceb276c308bfa2a43fafb75c8031c4f66039e8c6e"}, + {file = "pydantic_core-2.27.2-cp39-cp39-win_amd64.whl", hash = "sha256:77d1bca19b0f7021b3a982e6f903dcd5b2b06076def36a652e3907f596e29f67"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2bf14caea37e91198329b828eae1618c068dfb8ef17bb33287a7ad4b61ac314e"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b0cb791f5b45307caae8810c2023a184c74605ec3bcbb67d13846c28ff731ff8"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:688d3fd9fcb71f41c4c015c023d12a79d1c4c0732ec9eb35d96e3388a120dcf3"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d591580c34f4d731592f0e9fe40f9cc1b430d297eecc70b962e93c5c668f15f"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:82f986faf4e644ffc189a7f1aafc86e46ef70372bb153e7001e8afccc6e54133"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:bec317a27290e2537f922639cafd54990551725fc844249e64c523301d0822fc"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:0296abcb83a797db256b773f45773da397da75a08f5fcaef41f2044adec05f50"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:0d75070718e369e452075a6017fbf187f788e17ed67a3abd47fa934d001863d9"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7e17b560be3c98a8e3aa66ce828bdebb9e9ac6ad5466fba92eb74c4c95cb1151"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c33939a82924da9ed65dab5a65d427205a73181d8098e79b6b426bdf8ad4e656"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:00bad2484fa6bda1e216e7345a798bd37c68fb2d97558edd584942aa41b7d278"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c817e2b40aba42bac6f457498dacabc568c3b7a986fc9ba7c8d9d260b71485fb"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:251136cdad0cb722e93732cb45ca5299fb56e1344a833640bf93b2803f8d1bfd"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d2088237af596f0a524d3afc39ab3b036e8adb054ee57cbb1dcf8e09da5b29cc"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d4041c0b966a84b4ae7a09832eb691a35aec90910cd2dbe7a208de59be77965b"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:8083d4e875ebe0b864ffef72a4304827015cff328a1be6e22cc850753bfb122b"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f141ee28a0ad2123b6611b6ceff018039df17f32ada8b534e6aa039545a3efb2"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7d0c8399fcc1848491f00e0314bd59fb34a9c008761bcb422a057670c3f65e35"}, + {file = "pydantic_core-2.27.2.tar.gz", hash = "sha256:eb026e5a4c1fee05726072337ff51d1efb6f59090b7da90d30ea58625b1ffb39"}, ] [package.dependencies] typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" +[[package]] +name = "pygments" +version = "2.19.1" +description = "Pygments is a syntax highlighting package written in Python." +optional = true +python-versions = ">=3.8" +files = [ + {file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"}, + {file = "pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f"}, +] + +[package.extras] +windows-terminal = ["colorama (>=0.4.6)"] + [[package]] name = "pyjwt" -version = "2.9.0" +version = "2.10.1" description = "JSON Web Token implementation in Python" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "PyJWT-2.9.0-py3-none-any.whl", hash = "sha256:3b02fb0f44517787776cf48f2ae25d8e14f300e6d7545a4315cee571a415e850"}, - {file = "pyjwt-2.9.0.tar.gz", hash = "sha256:7e1e5b56cc735432a7369cbfa0efe50fa113ebecdc04ae6922deba8b84582d0c"}, + {file = "PyJWT-2.10.1-py3-none-any.whl", hash = "sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb"}, + {file = "pyjwt-2.10.1.tar.gz", hash = "sha256:3cc5772eb20009233caf06e9d8a0577824723b44e6648ee0a2aedb6cf9381953"}, ] [package.dependencies] @@ -3900,60 +4101,74 @@ cffi = {version = "*", markers = "implementation_name == \"pypy\""} [[package]] name = "ray" -version = "2.38.0" +version = "2.40.0" description = "Ray provides a simple, universal API for building distributed applications." optional = true python-versions = ">=3.9" files = [ - {file = "ray-2.38.0-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:fe01fce188ddea96ca5c7dfa4a783d2e5d80662318a640fae58d89e6eaf2cd7f"}, - {file = "ray-2.38.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1fa0833cc54ca0c48aebc98b813fa1e990a20c8ee1da857073e11eb72696d316"}, - {file = "ray-2.38.0-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:3cdd71617f935a0d741864e94061093d14fad659e67271c9a779108878294ac3"}, - {file = "ray-2.38.0-cp310-cp310-manylinux2014_x86_64.whl", hash = "sha256:3a7d6f7159bce4117bfe8f9c3d0b65ff27257fe2dd8d737dad0f3869666440da"}, - {file = "ray-2.38.0-cp310-cp310-win_amd64.whl", hash = "sha256:b56c78ebdd7535ab6e8566e66c1f1c65a694432875dd683b1310e3d7b9af79f3"}, - {file = "ray-2.38.0-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:cce1a39fa91fe08b15d2d62d084052968a155c8528415f248346567aa589580c"}, - {file = "ray-2.38.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:454f576b3dbef2231693e3081ba5bf093add610c72ebf3c17788943f6653fe68"}, - {file = "ray-2.38.0-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:282a326d2848d411c3ce305e57e2de8357e24cb9becbec7e507e8800572c487e"}, - {file = "ray-2.38.0-cp311-cp311-manylinux2014_x86_64.whl", hash = "sha256:ece802cf3a1c102b53f63b8bc90d947971c4b387deaf233c224ed8ef34a1f3cb"}, - {file = "ray-2.38.0-cp311-cp311-win_amd64.whl", hash = "sha256:64f7cd908177dd50089469cf331afbeb22e61e26d0a4be210ad20dccddbf6efb"}, - {file = "ray-2.38.0-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:10174ac63406b95a0a795a89396aeb8966286f15558087127719b13c367b40e3"}, - {file = "ray-2.38.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ea4148e929c17543378ba8909398fc81ce09d8e2257fc21afa62fc88ba4babc2"}, - {file = "ray-2.38.0-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:d4efaf1cfc727d60d78cc7112ff8eaa67634a5327e2a84f8dcaab5d167fe7fec"}, - {file = "ray-2.38.0-cp312-cp312-manylinux2014_x86_64.whl", hash = "sha256:07507d2f9961e8d5390c0eb606df249216ef5afb1ff8185581f3e92041d66293"}, - {file = "ray-2.38.0-cp312-cp312-win_amd64.whl", hash = "sha256:6fdef893cbe617ac9d079e65702e9f1b3f455835f05b6f8b46467cf2184a52dc"}, - {file = "ray-2.38.0-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:0910eb721943f9825d10ae16d9cd3c7de70f4dde985207e18fddf59c0126770f"}, - {file = "ray-2.38.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3d0bd0d7a116ab79864ca8bf3222758ad85cc9f9421a51136ca33429e8e87ed9"}, - {file = "ray-2.38.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:cdfd910da985bc3c985945b7bbbef5f891473eddd06af9208b8af0d020e3a9a7"}, - {file = "ray-2.38.0-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:e18ac9e23da17393b4447ef2924e11ef95bb8a5d5b561ca8c74c05f2a594a6fe"}, - {file = "ray-2.38.0-cp39-cp39-win_amd64.whl", hash = "sha256:1f0d014f215b25f92041d4a2acfbc4e44abb2a92f43971228f493ba7874ede00"}, + {file = "ray-2.40.0-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:064af8bc52cc988c82470b8e76e5df417737fa7c1d87f597a892c69eb4ec3caa"}, + {file = "ray-2.40.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:45beb4019cd20b6cb10572d8012c771bccd623f544a669da6797ccf993c4bb33"}, + {file = "ray-2.40.0-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:6cede5fbf7de4fae22cebe2c6977aaf3c85fde6f7de2aa10c46992cf24ea8bda"}, + {file = "ray-2.40.0-cp310-cp310-manylinux2014_x86_64.whl", hash = "sha256:f6eab11dc8490f88e78e06aa645905b259cde1fa03b15e8426155c4782ba0bbe"}, + {file = "ray-2.40.0-cp310-cp310-win_amd64.whl", hash = "sha256:f83cda1ecceb7abe021cd377f0c503596f26d2d66cdff13c1089a06c8b780c23"}, + {file = "ray-2.40.0-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:dac89bb2cb889c19549a4ac0383492e7550f3e63b78b629a3118e8b91e4e82f3"}, + {file = "ray-2.40.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3e4efdf8aebff6e71391c2d5dd66bb45835f2d6d629ac03a3e21e2d4283e2311"}, + {file = "ray-2.40.0-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:c776f131e5d0a169a98ab8021c5796f52bf48fcfc6c44ffbd2a9d090fe10748a"}, + {file = "ray-2.40.0-cp311-cp311-manylinux2014_x86_64.whl", hash = "sha256:71711cbf2c156213fd49b0f9cc93180a7ba424110070a34bdea3dc09527f31df"}, + {file = "ray-2.40.0-cp311-cp311-win_amd64.whl", hash = "sha256:532321132618983366e39aeb4cc7867cf7241b0b1e49ee44b01d2aee9923e422"}, + {file = "ray-2.40.0-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:6992922fe91a90b5cc97d9f05ca51b64d72cd644db7ad55caa936be9a6098cce"}, + {file = "ray-2.40.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:28329e7a7471610a475d3bb09a4c1b31abcf3596cee25c4254f8d01ad161ba84"}, + {file = "ray-2.40.0-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:8ea05221fa48e32c652c29498d320e90134b3a012421006af98965097dd1cc3b"}, + {file = "ray-2.40.0-cp312-cp312-manylinux2014_x86_64.whl", hash = "sha256:674755814f5692306c554cadbc24015af823dc0516e34bdef24ccac9d7a656e3"}, + {file = "ray-2.40.0-cp312-cp312-win_amd64.whl", hash = "sha256:bbc01d773cbc43e3efa462ec28ee4c0cacc50f098078332fb45b1ab38eaf9b5d"}, + {file = "ray-2.40.0-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:27292bf8921dd69757e7581644afcd3ccae13d6f10f3841f5523ae82b6612f4b"}, + {file = "ray-2.40.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2b74ca43d0c4ccdcaefbf1e7d26aabb1c0d20f825688a9fd7134ba918bda8442"}, + {file = "ray-2.40.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:5eb7a203f58defedff0dc53f78a4e1431d040b2b8458548704979c0113f3b892"}, + {file = "ray-2.40.0-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:a36a20a3b936b36d14fab031222f92e3c5e731d7db6bb183ca4fba6d0ce3f52a"}, + {file = "ray-2.40.0-cp39-cp39-win_amd64.whl", hash = "sha256:fbe9cd3e076dea676afd57caf19b2897a67ecdf14a542c03864800966cf2aec9"}, ] [package.dependencies] +aiohttp = {version = ">=3.7", optional = true, markers = "extra == \"default\""} +aiohttp-cors = {version = "*", optional = true, markers = "extra == \"default\""} aiosignal = "*" click = ">=7.0" +colorful = {version = "*", optional = true, markers = "extra == \"default\""} filelock = "*" frozenlist = "*" +grpcio = [ + {version = ">=1.42.0", optional = true, markers = "python_version >= \"3.10\" and extra == \"default\""}, + {version = ">=1.32.0", optional = true, markers = "python_version < \"3.10\" and extra == \"default\""}, +] jsonschema = "*" +memray = {version = "*", optional = true, markers = "sys_platform != \"win32\" and extra == \"default\""} msgpack = ">=1.0.0,<2.0.0" +opencensus = {version = "*", optional = true, markers = "extra == \"default\""} packaging = "*" +prometheus-client = {version = ">=0.7.1", optional = true, markers = "extra == \"default\""} protobuf = ">=3.15.3,<3.19.5 || >3.19.5" +py-spy = {version = ">=0.2.0", optional = true, markers = "extra == \"default\""} +pydantic = {version = "<2.0.dev0 || >=2.5.dev0,<3", optional = true, markers = "extra == \"default\""} pyyaml = "*" requests = "*" +smart-open = {version = "*", optional = true, markers = "extra == \"default\""} +virtualenv = {version = ">=20.0.24,<20.21.1 || >20.21.1", optional = true, markers = "extra == \"default\""} [package.extras] adag = ["cupy-cuda12x"] -air = ["aiohttp (>=3.7)", "aiohttp-cors", "colorful", "fastapi", "fsspec", "grpcio (>=1.32.0)", "grpcio (>=1.42.0)", "memray", "numpy (>=1.20)", "opencensus", "pandas", "pandas (>=1.3)", "prometheus-client (>=0.7.1)", "py-spy (>=0.2.0)", "pyarrow (>=6.0.1)", "pydantic (<2.0.dev0 || >=2.5.dev0,<3)", "requests", "smart-open", "starlette", "tensorboardX (>=1.9)", "uvicorn[standard]", "virtualenv (>=20.0.24,!=20.21.1)", "watchfiles"] -all = ["aiohttp (>=3.7)", "aiohttp-cors", "colorful", "cupy-cuda12x", "dm-tree", "fastapi", "fsspec", "grpcio (!=1.56.0)", "grpcio (>=1.32.0)", "grpcio (>=1.42.0)", "gymnasium (==0.28.1)", "lz4", "memray", "numpy (>=1.20)", "opencensus", "opentelemetry-api", "opentelemetry-exporter-otlp", "opentelemetry-sdk", "pandas", "pandas (>=1.3)", "prometheus-client (>=0.7.1)", "py-spy (>=0.2.0)", "pyOpenSSL", "pyarrow (>=6.0.1)", "pydantic (<2.0.dev0 || >=2.5.dev0,<3)", "pyyaml", "requests", "rich", "scikit-image", "scipy", "smart-open", "starlette", "tensorboardX (>=1.9)", "typer", "uvicorn[standard]", "virtualenv (>=20.0.24,!=20.21.1)", "watchfiles"] -all-cpp = ["aiohttp (>=3.7)", "aiohttp-cors", "colorful", "cupy-cuda12x", "dm-tree", "fastapi", "fsspec", "grpcio (!=1.56.0)", "grpcio (>=1.32.0)", "grpcio (>=1.42.0)", "gymnasium (==0.28.1)", "lz4", "memray", "numpy (>=1.20)", "opencensus", "opentelemetry-api", "opentelemetry-exporter-otlp", "opentelemetry-sdk", "pandas", "pandas (>=1.3)", "prometheus-client (>=0.7.1)", "py-spy (>=0.2.0)", "pyOpenSSL", "pyarrow (>=6.0.1)", "pydantic (<2.0.dev0 || >=2.5.dev0,<3)", "pyyaml", "ray-cpp (==2.38.0)", "requests", "rich", "scikit-image", "scipy", "smart-open", "starlette", "tensorboardX (>=1.9)", "typer", "uvicorn[standard]", "virtualenv (>=20.0.24,!=20.21.1)", "watchfiles"] +air = ["aiohttp (>=3.7)", "aiohttp-cors", "colorful", "fastapi", "fsspec", "grpcio (>=1.32.0)", "grpcio (>=1.42.0)", "memray", "numpy (>=1.20)", "opencensus", "pandas", "pandas (>=1.3)", "prometheus-client (>=0.7.1)", "py-spy (>=0.2.0)", "pyarrow (<18)", "pyarrow (>=9.0.0)", "pydantic (<2.0.dev0 || >=2.5.dev0,<3)", "requests", "smart-open", "starlette", "tensorboardX (>=1.9)", "uvicorn[standard]", "virtualenv (>=20.0.24,!=20.21.1)", "watchfiles"] +all = ["aiohttp (>=3.7)", "aiohttp-cors", "colorful", "cupy-cuda12x", "dm-tree", "fastapi", "fsspec", "grpcio (!=1.56.0)", "grpcio (>=1.32.0)", "grpcio (>=1.42.0)", "gymnasium (==1.0.0)", "lz4", "memray", "numpy (>=1.20)", "opencensus", "opentelemetry-api", "opentelemetry-exporter-otlp", "opentelemetry-sdk", "pandas", "pandas (>=1.3)", "prometheus-client (>=0.7.1)", "py-spy (>=0.2.0)", "pyOpenSSL", "pyarrow (<18)", "pyarrow (>=9.0.0)", "pydantic (<2.0.dev0 || >=2.5.dev0,<3)", "pyyaml", "requests", "rich", "scikit-image", "scipy", "smart-open", "starlette", "tensorboardX (>=1.9)", "typer", "uvicorn[standard]", "virtualenv (>=20.0.24,!=20.21.1)", "watchfiles"] +all-cpp = ["aiohttp (>=3.7)", "aiohttp-cors", "colorful", "cupy-cuda12x", "dm-tree", "fastapi", "fsspec", "grpcio (!=1.56.0)", "grpcio (>=1.32.0)", "grpcio (>=1.42.0)", "gymnasium (==1.0.0)", "lz4", "memray", "numpy (>=1.20)", "opencensus", "opentelemetry-api", "opentelemetry-exporter-otlp", "opentelemetry-sdk", "pandas", "pandas (>=1.3)", "prometheus-client (>=0.7.1)", "py-spy (>=0.2.0)", "pyOpenSSL", "pyarrow (<18)", "pyarrow (>=9.0.0)", "pydantic (<2.0.dev0 || >=2.5.dev0,<3)", "pyyaml", "ray-cpp (==2.40.0)", "requests", "rich", "scikit-image", "scipy", "smart-open", "starlette", "tensorboardX (>=1.9)", "typer", "uvicorn[standard]", "virtualenv (>=20.0.24,!=20.21.1)", "watchfiles"] client = ["grpcio (!=1.56.0)"] -cpp = ["ray-cpp (==2.38.0)"] -data = ["fsspec", "numpy (>=1.20)", "pandas (>=1.3)", "pyarrow (>=6.0.1)"] +cpp = ["ray-cpp (==2.40.0)"] +data = ["fsspec", "numpy (>=1.20)", "pandas (>=1.3)", "pyarrow (<18)", "pyarrow (>=9.0.0)"] default = ["aiohttp (>=3.7)", "aiohttp-cors", "colorful", "grpcio (>=1.32.0)", "grpcio (>=1.42.0)", "memray", "opencensus", "prometheus-client (>=0.7.1)", "py-spy (>=0.2.0)", "pydantic (<2.0.dev0 || >=2.5.dev0,<3)", "requests", "smart-open", "virtualenv (>=20.0.24,!=20.21.1)"] observability = ["opentelemetry-api", "opentelemetry-exporter-otlp", "opentelemetry-sdk"] -rllib = ["dm-tree", "fsspec", "gymnasium (==0.28.1)", "lz4", "pandas", "pyarrow (>=6.0.1)", "pyyaml", "requests", "rich", "scikit-image", "scipy", "tensorboardX (>=1.9)", "typer"] +rllib = ["dm-tree", "fsspec", "gymnasium (==1.0.0)", "lz4", "pandas", "pyarrow (<18)", "pyarrow (>=9.0.0)", "pyyaml", "requests", "rich", "scikit-image", "scipy", "tensorboardX (>=1.9)", "typer"] serve = ["aiohttp (>=3.7)", "aiohttp-cors", "colorful", "fastapi", "grpcio (>=1.32.0)", "grpcio (>=1.42.0)", "memray", "opencensus", "prometheus-client (>=0.7.1)", "py-spy (>=0.2.0)", "pydantic (<2.0.dev0 || >=2.5.dev0,<3)", "requests", "smart-open", "starlette", "uvicorn[standard]", "virtualenv (>=20.0.24,!=20.21.1)", "watchfiles"] serve-grpc = ["aiohttp (>=3.7)", "aiohttp-cors", "colorful", "fastapi", "grpcio (>=1.32.0)", "grpcio (>=1.42.0)", "memray", "opencensus", "prometheus-client (>=0.7.1)", "py-spy (>=0.2.0)", "pyOpenSSL", "pydantic (<2.0.dev0 || >=2.5.dev0,<3)", "requests", "smart-open", "starlette", "uvicorn[standard]", "virtualenv (>=20.0.24,!=20.21.1)", "watchfiles"] -train = ["fsspec", "pandas", "pyarrow (>=6.0.1)", "requests", "tensorboardX (>=1.9)"] -tune = ["fsspec", "pandas", "pyarrow (>=6.0.1)", "requests", "tensorboardX (>=1.9)"] +train = ["fsspec", "pandas", "pyarrow (<18)", "pyarrow (>=9.0.0)", "requests", "tensorboardX (>=1.9)"] +tune = ["fsspec", "pandas", "pyarrow (<18)", "pyarrow (>=9.0.0)", "requests", "tensorboardX (>=1.9)"] [[package]] name = "referencing" @@ -3972,105 +4187,105 @@ rpds-py = ">=0.7.0" [[package]] name = "regex" -version = "2024.9.11" +version = "2024.11.6" description = "Alternative regular expression module, to replace re." optional = false python-versions = ">=3.8" files = [ - {file = "regex-2024.9.11-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1494fa8725c285a81d01dc8c06b55287a1ee5e0e382d8413adc0a9197aac6408"}, - {file = "regex-2024.9.11-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0e12c481ad92d129c78f13a2a3662317e46ee7ef96c94fd332e1c29131875b7d"}, - {file = "regex-2024.9.11-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:16e13a7929791ac1216afde26f712802e3df7bf0360b32e4914dca3ab8baeea5"}, - {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46989629904bad940bbec2106528140a218b4a36bb3042d8406980be1941429c"}, - {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a906ed5e47a0ce5f04b2c981af1c9acf9e8696066900bf03b9d7879a6f679fc8"}, - {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e9a091b0550b3b0207784a7d6d0f1a00d1d1c8a11699c1a4d93db3fbefc3ad35"}, - {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ddcd9a179c0a6fa8add279a4444015acddcd7f232a49071ae57fa6e278f1f71"}, - {file = "regex-2024.9.11-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6b41e1adc61fa347662b09398e31ad446afadff932a24807d3ceb955ed865cc8"}, - {file = "regex-2024.9.11-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ced479f601cd2f8ca1fd7b23925a7e0ad512a56d6e9476f79b8f381d9d37090a"}, - {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:635a1d96665f84b292e401c3d62775851aedc31d4f8784117b3c68c4fcd4118d"}, - {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c0256beda696edcf7d97ef16b2a33a8e5a875affd6fa6567b54f7c577b30a137"}, - {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:3ce4f1185db3fbde8ed8aa223fc9620f276c58de8b0d4f8cc86fd1360829edb6"}, - {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:09d77559e80dcc9d24570da3745ab859a9cf91953062e4ab126ba9d5993688ca"}, - {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7a22ccefd4db3f12b526eccb129390942fe874a3a9fdbdd24cf55773a1faab1a"}, - {file = "regex-2024.9.11-cp310-cp310-win32.whl", hash = "sha256:f745ec09bc1b0bd15cfc73df6fa4f726dcc26bb16c23a03f9e3367d357eeedd0"}, - {file = "regex-2024.9.11-cp310-cp310-win_amd64.whl", hash = "sha256:01c2acb51f8a7d6494c8c5eafe3d8e06d76563d8a8a4643b37e9b2dd8a2ff623"}, - {file = "regex-2024.9.11-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2cce2449e5927a0bf084d346da6cd5eb016b2beca10d0013ab50e3c226ffc0df"}, - {file = "regex-2024.9.11-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3b37fa423beefa44919e009745ccbf353d8c981516e807995b2bd11c2c77d268"}, - {file = "regex-2024.9.11-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:64ce2799bd75039b480cc0360907c4fb2f50022f030bf9e7a8705b636e408fad"}, - {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a4cc92bb6db56ab0c1cbd17294e14f5e9224f0cc6521167ef388332604e92679"}, - {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d05ac6fa06959c4172eccd99a222e1fbf17b5670c4d596cb1e5cde99600674c4"}, - {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:040562757795eeea356394a7fb13076ad4f99d3c62ab0f8bdfb21f99a1f85664"}, - {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6113c008a7780792efc80f9dfe10ba0cd043cbf8dc9a76ef757850f51b4edc50"}, - {file = "regex-2024.9.11-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8e5fb5f77c8745a60105403a774fe2c1759b71d3e7b4ca237a5e67ad066c7199"}, - {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:54d9ff35d4515debf14bc27f1e3b38bfc453eff3220f5bce159642fa762fe5d4"}, - {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:df5cbb1fbc74a8305b6065d4ade43b993be03dbe0f8b30032cced0d7740994bd"}, - {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:7fb89ee5d106e4a7a51bce305ac4efb981536301895f7bdcf93ec92ae0d91c7f"}, - {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:a738b937d512b30bf75995c0159c0ddf9eec0775c9d72ac0202076c72f24aa96"}, - {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e28f9faeb14b6f23ac55bfbbfd3643f5c7c18ede093977f1df249f73fd22c7b1"}, - {file = "regex-2024.9.11-cp311-cp311-win32.whl", hash = "sha256:18e707ce6c92d7282dfce370cd205098384b8ee21544e7cb29b8aab955b66fa9"}, - {file = "regex-2024.9.11-cp311-cp311-win_amd64.whl", hash = "sha256:313ea15e5ff2a8cbbad96ccef6be638393041b0a7863183c2d31e0c6116688cf"}, - {file = "regex-2024.9.11-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b0d0a6c64fcc4ef9c69bd5b3b3626cc3776520a1637d8abaa62b9edc147a58f7"}, - {file = "regex-2024.9.11-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:49b0e06786ea663f933f3710a51e9385ce0cba0ea56b67107fd841a55d56a231"}, - {file = "regex-2024.9.11-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5b513b6997a0b2f10e4fd3a1313568e373926e8c252bd76c960f96fd039cd28d"}, - {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee439691d8c23e76f9802c42a95cfeebf9d47cf4ffd06f18489122dbb0a7ad64"}, - {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a8f877c89719d759e52783f7fe6e1c67121076b87b40542966c02de5503ace42"}, - {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:23b30c62d0f16827f2ae9f2bb87619bc4fba2044911e2e6c2eb1af0161cdb766"}, - {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85ab7824093d8f10d44330fe1e6493f756f252d145323dd17ab6b48733ff6c0a"}, - {file = "regex-2024.9.11-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8dee5b4810a89447151999428fe096977346cf2f29f4d5e29609d2e19e0199c9"}, - {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:98eeee2f2e63edae2181c886d7911ce502e1292794f4c5ee71e60e23e8d26b5d"}, - {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:57fdd2e0b2694ce6fc2e5ccf189789c3e2962916fb38779d3e3521ff8fe7a822"}, - {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:d552c78411f60b1fdaafd117a1fca2f02e562e309223b9d44b7de8be451ec5e0"}, - {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a0b2b80321c2ed3fcf0385ec9e51a12253c50f146fddb2abbb10f033fe3d049a"}, - {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:18406efb2f5a0e57e3a5881cd9354c1512d3bb4f5c45d96d110a66114d84d23a"}, - {file = "regex-2024.9.11-cp312-cp312-win32.whl", hash = "sha256:e464b467f1588e2c42d26814231edecbcfe77f5ac414d92cbf4e7b55b2c2a776"}, - {file = "regex-2024.9.11-cp312-cp312-win_amd64.whl", hash = "sha256:9e8719792ca63c6b8340380352c24dcb8cd7ec49dae36e963742a275dfae6009"}, - {file = "regex-2024.9.11-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c157bb447303070f256e084668b702073db99bbb61d44f85d811025fcf38f784"}, - {file = "regex-2024.9.11-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4db21ece84dfeefc5d8a3863f101995de646c6cb0536952c321a2650aa202c36"}, - {file = "regex-2024.9.11-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:220e92a30b426daf23bb67a7962900ed4613589bab80382be09b48896d211e92"}, - {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb1ae19e64c14c7ec1995f40bd932448713d3c73509e82d8cd7744dc00e29e86"}, - {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f47cd43a5bfa48f86925fe26fbdd0a488ff15b62468abb5d2a1e092a4fb10e85"}, - {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9d4a76b96f398697fe01117093613166e6aa8195d63f1b4ec3f21ab637632963"}, - {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ea51dcc0835eea2ea31d66456210a4e01a076d820e9039b04ae8d17ac11dee6"}, - {file = "regex-2024.9.11-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7aaa315101c6567a9a45d2839322c51c8d6e81f67683d529512f5bcfb99c802"}, - {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c57d08ad67aba97af57a7263c2d9006d5c404d721c5f7542f077f109ec2a4a29"}, - {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f8404bf61298bb6f8224bb9176c1424548ee1181130818fcd2cbffddc768bed8"}, - {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:dd4490a33eb909ef5078ab20f5f000087afa2a4daa27b4c072ccb3cb3050ad84"}, - {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:eee9130eaad130649fd73e5cd92f60e55708952260ede70da64de420cdcad554"}, - {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6a2644a93da36c784e546de579ec1806bfd2763ef47babc1b03d765fe560c9f8"}, - {file = "regex-2024.9.11-cp313-cp313-win32.whl", hash = "sha256:e997fd30430c57138adc06bba4c7c2968fb13d101e57dd5bb9355bf8ce3fa7e8"}, - {file = "regex-2024.9.11-cp313-cp313-win_amd64.whl", hash = "sha256:042c55879cfeb21a8adacc84ea347721d3d83a159da6acdf1116859e2427c43f"}, - {file = "regex-2024.9.11-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:35f4a6f96aa6cb3f2f7247027b07b15a374f0d5b912c0001418d1d55024d5cb4"}, - {file = "regex-2024.9.11-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:55b96e7ce3a69a8449a66984c268062fbaa0d8ae437b285428e12797baefce7e"}, - {file = "regex-2024.9.11-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cb130fccd1a37ed894824b8c046321540263013da72745d755f2d35114b81a60"}, - {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:323c1f04be6b2968944d730e5c2091c8c89767903ecaa135203eec4565ed2b2b"}, - {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be1c8ed48c4c4065ecb19d882a0ce1afe0745dfad8ce48c49586b90a55f02366"}, - {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b5b029322e6e7b94fff16cd120ab35a253236a5f99a79fb04fda7ae71ca20ae8"}, - {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6fff13ef6b5f29221d6904aa816c34701462956aa72a77f1f151a8ec4f56aeb"}, - {file = "regex-2024.9.11-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:587d4af3979376652010e400accc30404e6c16b7df574048ab1f581af82065e4"}, - {file = "regex-2024.9.11-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:079400a8269544b955ffa9e31f186f01d96829110a3bf79dc338e9910f794fca"}, - {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:f9268774428ec173654985ce55fc6caf4c6d11ade0f6f914d48ef4719eb05ebb"}, - {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:23f9985c8784e544d53fc2930fc1ac1a7319f5d5332d228437acc9f418f2f168"}, - {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:ae2941333154baff9838e88aa71c1d84f4438189ecc6021a12c7573728b5838e"}, - {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:e93f1c331ca8e86fe877a48ad64e77882c0c4da0097f2212873a69bbfea95d0c"}, - {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:846bc79ee753acf93aef4184c040d709940c9d001029ceb7b7a52747b80ed2dd"}, - {file = "regex-2024.9.11-cp38-cp38-win32.whl", hash = "sha256:c94bb0a9f1db10a1d16c00880bdebd5f9faf267273b8f5bd1878126e0fbde771"}, - {file = "regex-2024.9.11-cp38-cp38-win_amd64.whl", hash = "sha256:2b08fce89fbd45664d3df6ad93e554b6c16933ffa9d55cb7e01182baaf971508"}, - {file = "regex-2024.9.11-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:07f45f287469039ffc2c53caf6803cd506eb5f5f637f1d4acb37a738f71dd066"}, - {file = "regex-2024.9.11-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4838e24ee015101d9f901988001038f7f0d90dc0c3b115541a1365fb439add62"}, - {file = "regex-2024.9.11-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6edd623bae6a737f10ce853ea076f56f507fd7726bee96a41ee3d68d347e4d16"}, - {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c69ada171c2d0e97a4b5aa78fbb835e0ffbb6b13fc5da968c09811346564f0d3"}, - {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:02087ea0a03b4af1ed6ebab2c54d7118127fee8d71b26398e8e4b05b78963199"}, - {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:69dee6a020693d12a3cf892aba4808fe168d2a4cef368eb9bf74f5398bfd4ee8"}, - {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:297f54910247508e6e5cae669f2bc308985c60540a4edd1c77203ef19bfa63ca"}, - {file = "regex-2024.9.11-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ecea58b43a67b1b79805f1a0255730edaf5191ecef84dbc4cc85eb30bc8b63b9"}, - {file = "regex-2024.9.11-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:eab4bb380f15e189d1313195b062a6aa908f5bd687a0ceccd47c8211e9cf0d4a"}, - {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0cbff728659ce4bbf4c30b2a1be040faafaa9eca6ecde40aaff86f7889f4ab39"}, - {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:54c4a097b8bc5bb0dfc83ae498061d53ad7b5762e00f4adaa23bee22b012e6ba"}, - {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:73d6d2f64f4d894c96626a75578b0bf7d9e56dcda8c3d037a2118fdfe9b1c664"}, - {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:e53b5fbab5d675aec9f0c501274c467c0f9a5d23696cfc94247e1fb56501ed89"}, - {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0ffbcf9221e04502fc35e54d1ce9567541979c3fdfb93d2c554f0ca583a19b35"}, - {file = "regex-2024.9.11-cp39-cp39-win32.whl", hash = "sha256:e4c22e1ac1f1ec1e09f72e6c44d8f2244173db7eb9629cc3a346a8d7ccc31142"}, - {file = "regex-2024.9.11-cp39-cp39-win_amd64.whl", hash = "sha256:faa3c142464efec496967359ca99696c896c591c56c53506bac1ad465f66e919"}, - {file = "regex-2024.9.11.tar.gz", hash = "sha256:6c188c307e8433bcb63dc1915022deb553b4203a70722fc542c363bf120a01fd"}, + {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff590880083d60acc0433f9c3f713c51f7ac6ebb9adf889c79a261ecf541aa91"}, + {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:658f90550f38270639e83ce492f27d2c8d2cd63805c65a13a14d36ca126753f0"}, + {file = "regex-2024.11.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:164d8b7b3b4bcb2068b97428060b2a53be050085ef94eca7f240e7947f1b080e"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3660c82f209655a06b587d55e723f0b813d3a7db2e32e5e7dc64ac2a9e86fde"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d22326fcdef5e08c154280b71163ced384b428343ae16a5ab2b3354aed12436e"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1ac758ef6aebfc8943560194e9fd0fa18bcb34d89fd8bd2af18183afd8da3a2"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:997d6a487ff00807ba810e0f8332c18b4eb8d29463cfb7c820dc4b6e7562d0cf"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:02a02d2bb04fec86ad61f3ea7f49c015a0681bf76abb9857f945d26159d2968c"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f02f93b92358ee3f78660e43b4b0091229260c5d5c408d17d60bf26b6c900e86"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:06eb1be98df10e81ebaded73fcd51989dcf534e3c753466e4b60c4697a003b67"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:040df6fe1a5504eb0f04f048e6d09cd7c7110fef851d7c567a6b6e09942feb7d"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabbfc59f2c6edba2a6622c647b716e34e8e3867e0ab975412c5c2f79b82da2"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8447d2d39b5abe381419319f942de20b7ecd60ce86f16a23b0698f22e1b70008"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:da8f5fc57d1933de22a9e23eec290a0d8a5927a5370d24bda9a6abe50683fe62"}, + {file = "regex-2024.11.6-cp310-cp310-win32.whl", hash = "sha256:b489578720afb782f6ccf2840920f3a32e31ba28a4b162e13900c3e6bd3f930e"}, + {file = "regex-2024.11.6-cp310-cp310-win_amd64.whl", hash = "sha256:5071b2093e793357c9d8b2929dfc13ac5f0a6c650559503bb81189d0a3814519"}, + {file = "regex-2024.11.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5478c6962ad548b54a591778e93cd7c456a7a29f8eca9c49e4f9a806dcc5d638"}, + {file = "regex-2024.11.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2c89a8cc122b25ce6945f0423dc1352cb9593c68abd19223eebbd4e56612c5b7"}, + {file = "regex-2024.11.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:94d87b689cdd831934fa3ce16cc15cd65748e6d689f5d2b8f4f4df2065c9fa20"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1062b39a0a2b75a9c694f7a08e7183a80c63c0d62b301418ffd9c35f55aaa114"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:167ed4852351d8a750da48712c3930b031f6efdaa0f22fa1933716bfcd6bf4a3"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d548dafee61f06ebdb584080621f3e0c23fff312f0de1afc776e2a2ba99a74f"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2a19f302cd1ce5dd01a9099aaa19cae6173306d1302a43b627f62e21cf18ac0"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bec9931dfb61ddd8ef2ebc05646293812cb6b16b60cf7c9511a832b6f1854b55"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9714398225f299aa85267fd222f7142fcb5c769e73d7733344efc46f2ef5cf89"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:202eb32e89f60fc147a41e55cb086db2a3f8cb82f9a9a88440dcfc5d37faae8d"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:4181b814e56078e9b00427ca358ec44333765f5ca1b45597ec7446d3a1ef6e34"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:068376da5a7e4da51968ce4c122a7cd31afaaec4fccc7856c92f63876e57b51d"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ac10f2c4184420d881a3475fb2c6f4d95d53a8d50209a2500723d831036f7c45"}, + {file = "regex-2024.11.6-cp311-cp311-win32.whl", hash = "sha256:c36f9b6f5f8649bb251a5f3f66564438977b7ef8386a52460ae77e6070d309d9"}, + {file = "regex-2024.11.6-cp311-cp311-win_amd64.whl", hash = "sha256:02e28184be537f0e75c1f9b2f8847dc51e08e6e171c6bde130b2687e0c33cf60"}, + {file = "regex-2024.11.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:52fb28f528778f184f870b7cf8f225f5eef0a8f6e3778529bdd40c7b3920796a"}, + {file = "regex-2024.11.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdd6028445d2460f33136c55eeb1f601ab06d74cb3347132e1c24250187500d9"}, + {file = "regex-2024.11.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:805e6b60c54bf766b251e94526ebad60b7de0c70f70a4e6210ee2891acb70bf2"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b85c2530be953a890eaffde05485238f07029600e8f098cdf1848d414a8b45e4"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb26437975da7dc36b7efad18aa9dd4ea569d2357ae6b783bf1118dabd9ea577"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:abfa5080c374a76a251ba60683242bc17eeb2c9818d0d30117b4486be10c59d3"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b7fa6606c2881c1db9479b0eaa11ed5dfa11c8d60a474ff0e095099f39d98e"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c32f75920cf99fe6b6c539c399a4a128452eaf1af27f39bce8909c9a3fd8cbe"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:982e6d21414e78e1f51cf595d7f321dcd14de1f2881c5dc6a6e23bbbbd68435e"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a7c2155f790e2fb448faed6dd241386719802296ec588a8b9051c1f5c481bc29"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:149f5008d286636e48cd0b1dd65018548944e495b0265b45e1bffecce1ef7f39"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e5364a4502efca094731680e80009632ad6624084aff9a23ce8c8c6820de3e51"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0a86e7eeca091c09e021db8eb72d54751e527fa47b8d5787caf96d9831bd02ad"}, + {file = "regex-2024.11.6-cp312-cp312-win32.whl", hash = "sha256:32f9a4c643baad4efa81d549c2aadefaeba12249b2adc5af541759237eee1c54"}, + {file = "regex-2024.11.6-cp312-cp312-win_amd64.whl", hash = "sha256:a93c194e2df18f7d264092dc8539b8ffb86b45b899ab976aa15d48214138e81b"}, + {file = "regex-2024.11.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a6ba92c0bcdf96cbf43a12c717eae4bc98325ca3730f6b130ffa2e3c3c723d84"}, + {file = "regex-2024.11.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:525eab0b789891ac3be914d36893bdf972d483fe66551f79d3e27146191a37d4"}, + {file = "regex-2024.11.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:086a27a0b4ca227941700e0b31425e7a28ef1ae8e5e05a33826e17e47fbfdba0"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bde01f35767c4a7899b7eb6e823b125a64de314a8ee9791367c9a34d56af18d0"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b583904576650166b3d920d2bcce13971f6f9e9a396c673187f49811b2769dc7"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c4de13f06a0d54fa0d5ab1b7138bfa0d883220965a29616e3ea61b35d5f5fc7"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3cde6e9f2580eb1665965ce9bf17ff4952f34f5b126beb509fee8f4e994f143c"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d7f453dca13f40a02b79636a339c5b62b670141e63efd511d3f8f73fba162b3"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:59dfe1ed21aea057a65c6b586afd2a945de04fc7db3de0a6e3ed5397ad491b07"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b97c1e0bd37c5cd7902e65f410779d39eeda155800b65fc4d04cc432efa9bc6e"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f9d1e379028e0fc2ae3654bac3cbbef81bf3fd571272a42d56c24007979bafb6"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:13291b39131e2d002a7940fb176e120bec5145f3aeb7621be6534e46251912c4"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f51f88c126370dcec4908576c5a627220da6c09d0bff31cfa89f2523843316d"}, + {file = "regex-2024.11.6-cp313-cp313-win32.whl", hash = "sha256:63b13cfd72e9601125027202cad74995ab26921d8cd935c25f09c630436348ff"}, + {file = "regex-2024.11.6-cp313-cp313-win_amd64.whl", hash = "sha256:2b3361af3198667e99927da8b84c1b010752fa4b1115ee30beaa332cabc3ef1a"}, + {file = "regex-2024.11.6-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:3a51ccc315653ba012774efca4f23d1d2a8a8f278a6072e29c7147eee7da446b"}, + {file = "regex-2024.11.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ad182d02e40de7459b73155deb8996bbd8e96852267879396fb274e8700190e3"}, + {file = "regex-2024.11.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ba9b72e5643641b7d41fa1f6d5abda2c9a263ae835b917348fc3c928182ad467"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40291b1b89ca6ad8d3f2b82782cc33807f1406cf68c8d440861da6304d8ffbbd"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cdf58d0e516ee426a48f7b2c03a332a4114420716d55769ff7108c37a09951bf"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a36fdf2af13c2b14738f6e973aba563623cb77d753bbbd8d414d18bfaa3105dd"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1cee317bfc014c2419a76bcc87f071405e3966da434e03e13beb45f8aced1a6"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50153825ee016b91549962f970d6a4442fa106832e14c918acd1c8e479916c4f"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ea1bfda2f7162605f6e8178223576856b3d791109f15ea99a9f95c16a7636fb5"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:df951c5f4a1b1910f1a99ff42c473ff60f8225baa1cdd3539fe2819d9543e9df"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:072623554418a9911446278f16ecb398fb3b540147a7828c06e2011fa531e773"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:f654882311409afb1d780b940234208a252322c24a93b442ca714d119e68086c"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:89d75e7293d2b3e674db7d4d9b1bee7f8f3d1609428e293771d1a962617150cc"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:f65557897fc977a44ab205ea871b690adaef6b9da6afda4790a2484b04293a5f"}, + {file = "regex-2024.11.6-cp38-cp38-win32.whl", hash = "sha256:6f44ec28b1f858c98d3036ad5d7d0bfc568bdd7a74f9c24e25f41ef1ebfd81a4"}, + {file = "regex-2024.11.6-cp38-cp38-win_amd64.whl", hash = "sha256:bb8f74f2f10dbf13a0be8de623ba4f9491faf58c24064f32b65679b021ed0001"}, + {file = "regex-2024.11.6-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5704e174f8ccab2026bd2f1ab6c510345ae8eac818b613d7d73e785f1310f839"}, + {file = "regex-2024.11.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:220902c3c5cc6af55d4fe19ead504de80eb91f786dc102fbd74894b1551f095e"}, + {file = "regex-2024.11.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5e7e351589da0850c125f1600a4c4ba3c722efefe16b297de54300f08d734fbf"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5056b185ca113c88e18223183aa1a50e66507769c9640a6ff75859619d73957b"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e34b51b650b23ed3354b5a07aab37034d9f923db2a40519139af34f485f77d0"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5670bce7b200273eee1840ef307bfa07cda90b38ae56e9a6ebcc9f50da9c469b"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08986dce1339bc932923e7d1232ce9881499a0e02925f7402fb7c982515419ef"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:93c0b12d3d3bc25af4ebbf38f9ee780a487e8bf6954c115b9f015822d3bb8e48"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:764e71f22ab3b305e7f4c21f1a97e1526a25ebdd22513e251cf376760213da13"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f056bf21105c2515c32372bbc057f43eb02aae2fda61052e2f7622c801f0b4e2"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:69ab78f848845569401469da20df3e081e6b5a11cb086de3eed1d48f5ed57c95"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:86fddba590aad9208e2fa8b43b4c098bb0ec74f15718bb6a704e3c63e2cef3e9"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:684d7a212682996d21ca12ef3c17353c021fe9de6049e19ac8481ec35574a70f"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a03e02f48cd1abbd9f3b7e3586d97c8f7a9721c436f51a5245b3b9483044480b"}, + {file = "regex-2024.11.6-cp39-cp39-win32.whl", hash = "sha256:41758407fc32d5c3c5de163888068cfee69cb4c2be844e7ac517a52770f9af57"}, + {file = "regex-2024.11.6-cp39-cp39-win_amd64.whl", hash = "sha256:b2837718570f95dd41675328e111345f9b7095d821bac435aac173ac80b19983"}, + {file = "regex-2024.11.6.tar.gz", hash = "sha256:7ab159b063c52a0333c884e4679f8d7a85112ee3078fe3d9004b2dd875585519"}, ] [[package]] @@ -4112,116 +4327,135 @@ requests = ">=2.0.0" [package.extras] rsa = ["oauthlib[signedtoken] (>=3.0.0)"] +[[package]] +name = "rich" +version = "13.9.4" +description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +optional = true +python-versions = ">=3.8.0" +files = [ + {file = "rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90"}, + {file = "rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098"}, +] + +[package.dependencies] +markdown-it-py = ">=2.2.0" +pygments = ">=2.13.0,<3.0.0" +typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.11\""} + +[package.extras] +jupyter = ["ipywidgets (>=7.5.1,<9)"] + [[package]] name = "rpds-py" -version = "0.20.0" +version = "0.22.3" description = "Python bindings to Rust's persistent data structures (rpds)" optional = true -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "rpds_py-0.20.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3ad0fda1635f8439cde85c700f964b23ed5fc2d28016b32b9ee5fe30da5c84e2"}, - {file = "rpds_py-0.20.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9bb4a0d90fdb03437c109a17eade42dfbf6190408f29b2744114d11586611d6f"}, - {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6377e647bbfd0a0b159fe557f2c6c602c159fc752fa316572f012fc0bf67150"}, - {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb851b7df9dda52dc1415ebee12362047ce771fc36914586b2e9fcbd7d293b3e"}, - {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1e0f80b739e5a8f54837be5d5c924483996b603d5502bfff79bf33da06164ee2"}, - {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a8c94dad2e45324fc74dce25e1645d4d14df9a4e54a30fa0ae8bad9a63928e3"}, - {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8e604fe73ba048c06085beaf51147eaec7df856824bfe7b98657cf436623daf"}, - {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:df3de6b7726b52966edf29663e57306b23ef775faf0ac01a3e9f4012a24a4140"}, - {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf258ede5bc22a45c8e726b29835b9303c285ab46fc7c3a4cc770736b5304c9f"}, - {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:55fea87029cded5df854ca7e192ec7bdb7ecd1d9a3f63d5c4eb09148acf4a7ce"}, - {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ae94bd0b2f02c28e199e9bc51485d0c5601f58780636185660f86bf80c89af94"}, - {file = "rpds_py-0.20.0-cp310-none-win32.whl", hash = "sha256:28527c685f237c05445efec62426d285e47a58fb05ba0090a4340b73ecda6dee"}, - {file = "rpds_py-0.20.0-cp310-none-win_amd64.whl", hash = "sha256:238a2d5b1cad28cdc6ed15faf93a998336eb041c4e440dd7f902528b8891b399"}, - {file = "rpds_py-0.20.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:ac2f4f7a98934c2ed6505aead07b979e6f999389f16b714448fb39bbaa86a489"}, - {file = "rpds_py-0.20.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:220002c1b846db9afd83371d08d239fdc865e8f8c5795bbaec20916a76db3318"}, - {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d7919548df3f25374a1f5d01fbcd38dacab338ef5f33e044744b5c36729c8db"}, - {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:758406267907b3781beee0f0edfe4a179fbd97c0be2e9b1154d7f0a1279cf8e5"}, - {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3d61339e9f84a3f0767b1995adfb171a0d00a1185192718a17af6e124728e0f5"}, - {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1259c7b3705ac0a0bd38197565a5d603218591d3f6cee6e614e380b6ba61c6f6"}, - {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c1dc0f53856b9cc9a0ccca0a7cc61d3d20a7088201c0937f3f4048c1718a209"}, - {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7e60cb630f674a31f0368ed32b2a6b4331b8350d67de53c0359992444b116dd3"}, - {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dbe982f38565bb50cb7fb061ebf762c2f254ca3d8c20d4006878766e84266272"}, - {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:514b3293b64187172bc77c8fb0cdae26981618021053b30d8371c3a902d4d5ad"}, - {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d0a26ffe9d4dd35e4dfdd1e71f46401cff0181c75ac174711ccff0459135fa58"}, - {file = "rpds_py-0.20.0-cp311-none-win32.whl", hash = "sha256:89c19a494bf3ad08c1da49445cc5d13d8fefc265f48ee7e7556839acdacf69d0"}, - {file = "rpds_py-0.20.0-cp311-none-win_amd64.whl", hash = "sha256:c638144ce971df84650d3ed0096e2ae7af8e62ecbbb7b201c8935c370df00a2c"}, - {file = "rpds_py-0.20.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a84ab91cbe7aab97f7446652d0ed37d35b68a465aeef8fc41932a9d7eee2c1a6"}, - {file = "rpds_py-0.20.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:56e27147a5a4c2c21633ff8475d185734c0e4befd1c989b5b95a5d0db699b21b"}, - {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2580b0c34583b85efec8c5c5ec9edf2dfe817330cc882ee972ae650e7b5ef739"}, - {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b80d4a7900cf6b66bb9cee5c352b2d708e29e5a37fe9bf784fa97fc11504bf6c"}, - {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50eccbf054e62a7b2209b28dc7a22d6254860209d6753e6b78cfaeb0075d7bee"}, - {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:49a8063ea4296b3a7e81a5dfb8f7b2d73f0b1c20c2af401fb0cdf22e14711a96"}, - {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea438162a9fcbee3ecf36c23e6c68237479f89f962f82dae83dc15feeceb37e4"}, - {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:18d7585c463087bddcfa74c2ba267339f14f2515158ac4db30b1f9cbdb62c8ef"}, - {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d4c7d1a051eeb39f5c9547e82ea27cbcc28338482242e3e0b7768033cb083821"}, - {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e4df1e3b3bec320790f699890d41c59d250f6beda159ea3c44c3f5bac1976940"}, - {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2cf126d33a91ee6eedc7f3197b53e87a2acdac63602c0f03a02dd69e4b138174"}, - {file = "rpds_py-0.20.0-cp312-none-win32.whl", hash = "sha256:8bc7690f7caee50b04a79bf017a8d020c1f48c2a1077ffe172abec59870f1139"}, - {file = "rpds_py-0.20.0-cp312-none-win_amd64.whl", hash = "sha256:0e13e6952ef264c40587d510ad676a988df19adea20444c2b295e536457bc585"}, - {file = "rpds_py-0.20.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:aa9a0521aeca7d4941499a73ad7d4f8ffa3d1affc50b9ea11d992cd7eff18a29"}, - {file = "rpds_py-0.20.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a1f1d51eccb7e6c32ae89243cb352389228ea62f89cd80823ea7dd1b98e0b91"}, - {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a86a9b96070674fc88b6f9f71a97d2c1d3e5165574615d1f9168ecba4cecb24"}, - {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6c8ef2ebf76df43f5750b46851ed1cdf8f109d7787ca40035fe19fbdc1acc5a7"}, - {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b74b25f024b421d5859d156750ea9a65651793d51b76a2e9238c05c9d5f203a9"}, - {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57eb94a8c16ab08fef6404301c38318e2c5a32216bf5de453e2714c964c125c8"}, - {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1940dae14e715e2e02dfd5b0f64a52e8374a517a1e531ad9412319dc3ac7879"}, - {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d20277fd62e1b992a50c43f13fbe13277a31f8c9f70d59759c88f644d66c619f"}, - {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:06db23d43f26478303e954c34c75182356ca9aa7797d22c5345b16871ab9c45c"}, - {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b2a5db5397d82fa847e4c624b0c98fe59d2d9b7cf0ce6de09e4d2e80f8f5b3f2"}, - {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5a35df9f5548fd79cb2f52d27182108c3e6641a4feb0f39067911bf2adaa3e57"}, - {file = "rpds_py-0.20.0-cp313-none-win32.whl", hash = "sha256:fd2d84f40633bc475ef2d5490b9c19543fbf18596dcb1b291e3a12ea5d722f7a"}, - {file = "rpds_py-0.20.0-cp313-none-win_amd64.whl", hash = "sha256:9bc2d153989e3216b0559251b0c260cfd168ec78b1fac33dd485750a228db5a2"}, - {file = "rpds_py-0.20.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:f2fbf7db2012d4876fb0d66b5b9ba6591197b0f165db8d99371d976546472a24"}, - {file = "rpds_py-0.20.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1e5f3cd7397c8f86c8cc72d5a791071431c108edd79872cdd96e00abd8497d29"}, - {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce9845054c13696f7af7f2b353e6b4f676dab1b4b215d7fe5e05c6f8bb06f965"}, - {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c3e130fd0ec56cb76eb49ef52faead8ff09d13f4527e9b0c400307ff72b408e1"}, - {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4b16aa0107ecb512b568244ef461f27697164d9a68d8b35090e9b0c1c8b27752"}, - {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aa7f429242aae2947246587d2964fad750b79e8c233a2367f71b554e9447949c"}, - {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af0fc424a5842a11e28956e69395fbbeab2c97c42253169d87e90aac2886d751"}, - {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b8c00a3b1e70c1d3891f0db1b05292747f0dbcfb49c43f9244d04c70fbc40eb8"}, - {file = "rpds_py-0.20.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:40ce74fc86ee4645d0a225498d091d8bc61f39b709ebef8204cb8b5a464d3c0e"}, - {file = "rpds_py-0.20.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:4fe84294c7019456e56d93e8ababdad5a329cd25975be749c3f5f558abb48253"}, - {file = "rpds_py-0.20.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:338ca4539aad4ce70a656e5187a3a31c5204f261aef9f6ab50e50bcdffaf050a"}, - {file = "rpds_py-0.20.0-cp38-none-win32.whl", hash = "sha256:54b43a2b07db18314669092bb2de584524d1ef414588780261e31e85846c26a5"}, - {file = "rpds_py-0.20.0-cp38-none-win_amd64.whl", hash = "sha256:a1862d2d7ce1674cffa6d186d53ca95c6e17ed2b06b3f4c476173565c862d232"}, - {file = "rpds_py-0.20.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:3fde368e9140312b6e8b6c09fb9f8c8c2f00999d1823403ae90cc00480221b22"}, - {file = "rpds_py-0.20.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9824fb430c9cf9af743cf7aaf6707bf14323fb51ee74425c380f4c846ea70789"}, - {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11ef6ce74616342888b69878d45e9f779b95d4bd48b382a229fe624a409b72c5"}, - {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c52d3f2f82b763a24ef52f5d24358553e8403ce05f893b5347098014f2d9eff2"}, - {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d35cef91e59ebbeaa45214861874bc6f19eb35de96db73e467a8358d701a96c"}, - {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d72278a30111e5b5525c1dd96120d9e958464316f55adb030433ea905866f4de"}, - {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4c29cbbba378759ac5786730d1c3cb4ec6f8ababf5c42a9ce303dc4b3d08cda"}, - {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6632f2d04f15d1bd6fe0eedd3b86d9061b836ddca4c03d5cf5c7e9e6b7c14580"}, - {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d0b67d87bb45ed1cd020e8fbf2307d449b68abc45402fe1a4ac9e46c3c8b192b"}, - {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ec31a99ca63bf3cd7f1a5ac9fe95c5e2d060d3c768a09bc1d16e235840861420"}, - {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:22e6c9976e38f4d8c4a63bd8a8edac5307dffd3ee7e6026d97f3cc3a2dc02a0b"}, - {file = "rpds_py-0.20.0-cp39-none-win32.whl", hash = "sha256:569b3ea770c2717b730b61998b6c54996adee3cef69fc28d444f3e7920313cf7"}, - {file = "rpds_py-0.20.0-cp39-none-win_amd64.whl", hash = "sha256:e6900ecdd50ce0facf703f7a00df12374b74bbc8ad9fe0f6559947fb20f82364"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:617c7357272c67696fd052811e352ac54ed1d9b49ab370261a80d3b6ce385045"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9426133526f69fcaba6e42146b4e12d6bc6c839b8b555097020e2b78ce908dcc"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:deb62214c42a261cb3eb04d474f7155279c1a8a8c30ac89b7dcb1721d92c3c02"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fcaeb7b57f1a1e071ebd748984359fef83ecb026325b9d4ca847c95bc7311c92"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d454b8749b4bd70dd0a79f428731ee263fa6995f83ccb8bada706e8d1d3ff89d"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d807dc2051abe041b6649681dce568f8e10668e3c1c6543ebae58f2d7e617855"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3c20f0ddeb6e29126d45f89206b8291352b8c5b44384e78a6499d68b52ae511"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b7f19250ceef892adf27f0399b9e5afad019288e9be756d6919cb58892129f51"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:4f1ed4749a08379555cebf4650453f14452eaa9c43d0a95c49db50c18b7da075"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:dcedf0b42bcb4cfff4101d7771a10532415a6106062f005ab97d1d0ab5681c60"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:39ed0d010457a78f54090fafb5d108501b5aa5604cc22408fc1c0c77eac14344"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:bb273176be34a746bdac0b0d7e4e2c467323d13640b736c4c477881a3220a989"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f918a1a130a6dfe1d7fe0f105064141342e7dd1611f2e6a21cd2f5c8cb1cfb3e"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:f60012a73aa396be721558caa3a6fd49b3dd0033d1675c6d59c4502e870fcf0c"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d2b1ad682a3dfda2a4e8ad8572f3100f95fad98cb99faf37ff0ddfe9cbf9d03"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:614fdafe9f5f19c63ea02817fa4861c606a59a604a77c8cdef5aa01d28b97921"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fa518bcd7600c584bf42e6617ee8132869e877db2f76bcdc281ec6a4113a53ab"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0475242f447cc6cb8a9dd486d68b2ef7fbee84427124c232bff5f63b1fe11e5"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f90a4cd061914a60bd51c68bcb4357086991bd0bb93d8aa66a6da7701370708f"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:def7400461c3a3f26e49078302e1c1b38f6752342c77e3cf72ce91ca69fb1bc1"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:65794e4048ee837494aea3c21a28ad5fc080994dfba5b036cf84de37f7ad5074"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:faefcc78f53a88f3076b7f8be0a8f8d35133a3ecf7f3770895c25f8813460f08"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:5b4f105deeffa28bbcdff6c49b34e74903139afa690e35d2d9e3c2c2fba18cec"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:fdfc3a892927458d98f3d55428ae46b921d1f7543b89382fdb483f5640daaec8"}, - {file = "rpds_py-0.20.0.tar.gz", hash = "sha256:d72a210824facfdaf8768cf2d7ca25a042c30320b3020de2fa04640920d4e121"}, + {file = "rpds_py-0.22.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:6c7b99ca52c2c1752b544e310101b98a659b720b21db00e65edca34483259967"}, + {file = "rpds_py-0.22.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:be2eb3f2495ba669d2a985f9b426c1797b7d48d6963899276d22f23e33d47e37"}, + {file = "rpds_py-0.22.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70eb60b3ae9245ddea20f8a4190bd79c705a22f8028aaf8bbdebe4716c3fab24"}, + {file = "rpds_py-0.22.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4041711832360a9b75cfb11b25a6a97c8fb49c07b8bd43d0d02b45d0b499a4ff"}, + {file = "rpds_py-0.22.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:64607d4cbf1b7e3c3c8a14948b99345eda0e161b852e122c6bb71aab6d1d798c"}, + {file = "rpds_py-0.22.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e69b0a0e2537f26d73b4e43ad7bc8c8efb39621639b4434b76a3de50c6966e"}, + {file = "rpds_py-0.22.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc27863442d388870c1809a87507727b799c8460573cfbb6dc0eeaef5a11b5ec"}, + {file = "rpds_py-0.22.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e79dd39f1e8c3504be0607e5fc6e86bb60fe3584bec8b782578c3b0fde8d932c"}, + {file = "rpds_py-0.22.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e0fa2d4ec53dc51cf7d3bb22e0aa0143966119f42a0c3e4998293a3dd2856b09"}, + {file = "rpds_py-0.22.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fda7cb070f442bf80b642cd56483b5548e43d366fe3f39b98e67cce780cded00"}, + {file = "rpds_py-0.22.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cff63a0272fcd259dcc3be1657b07c929c466b067ceb1c20060e8d10af56f5bf"}, + {file = "rpds_py-0.22.3-cp310-cp310-win32.whl", hash = "sha256:9bd7228827ec7bb817089e2eb301d907c0d9827a9e558f22f762bb690b131652"}, + {file = "rpds_py-0.22.3-cp310-cp310-win_amd64.whl", hash = "sha256:9beeb01d8c190d7581a4d59522cd3d4b6887040dcfc744af99aa59fef3e041a8"}, + {file = "rpds_py-0.22.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d20cfb4e099748ea39e6f7b16c91ab057989712d31761d3300d43134e26e165f"}, + {file = "rpds_py-0.22.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:68049202f67380ff9aa52f12e92b1c30115f32e6895cd7198fa2a7961621fc5a"}, + {file = "rpds_py-0.22.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb4f868f712b2dd4bcc538b0a0c1f63a2b1d584c925e69a224d759e7070a12d5"}, + {file = "rpds_py-0.22.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bc51abd01f08117283c5ebf64844a35144a0843ff7b2983e0648e4d3d9f10dbb"}, + {file = "rpds_py-0.22.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0f3cec041684de9a4684b1572fe28c7267410e02450f4561700ca5a3bc6695a2"}, + {file = "rpds_py-0.22.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7ef9d9da710be50ff6809fed8f1963fecdfecc8b86656cadfca3bc24289414b0"}, + {file = "rpds_py-0.22.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59f4a79c19232a5774aee369a0c296712ad0e77f24e62cad53160312b1c1eaa1"}, + {file = "rpds_py-0.22.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a60bce91f81ddaac922a40bbb571a12c1070cb20ebd6d49c48e0b101d87300d"}, + {file = "rpds_py-0.22.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e89391e6d60251560f0a8f4bd32137b077a80d9b7dbe6d5cab1cd80d2746f648"}, + {file = "rpds_py-0.22.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e3fb866d9932a3d7d0c82da76d816996d1667c44891bd861a0f97ba27e84fc74"}, + {file = "rpds_py-0.22.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1352ae4f7c717ae8cba93421a63373e582d19d55d2ee2cbb184344c82d2ae55a"}, + {file = "rpds_py-0.22.3-cp311-cp311-win32.whl", hash = "sha256:b0b4136a252cadfa1adb705bb81524eee47d9f6aab4f2ee4fa1e9d3cd4581f64"}, + {file = "rpds_py-0.22.3-cp311-cp311-win_amd64.whl", hash = "sha256:8bd7c8cfc0b8247c8799080fbff54e0b9619e17cdfeb0478ba7295d43f635d7c"}, + {file = "rpds_py-0.22.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:27e98004595899949bd7a7b34e91fa7c44d7a97c40fcaf1d874168bb652ec67e"}, + {file = "rpds_py-0.22.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1978d0021e943aae58b9b0b196fb4895a25cc53d3956b8e35e0b7682eefb6d56"}, + {file = "rpds_py-0.22.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:655ca44a831ecb238d124e0402d98f6212ac527a0ba6c55ca26f616604e60a45"}, + {file = "rpds_py-0.22.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:feea821ee2a9273771bae61194004ee2fc33f8ec7db08117ef9147d4bbcbca8e"}, + {file = "rpds_py-0.22.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:22bebe05a9ffc70ebfa127efbc429bc26ec9e9b4ee4d15a740033efda515cf3d"}, + {file = "rpds_py-0.22.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3af6e48651c4e0d2d166dc1b033b7042ea3f871504b6805ba5f4fe31581d8d38"}, + {file = "rpds_py-0.22.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e67ba3c290821343c192f7eae1d8fd5999ca2dc99994114643e2f2d3e6138b15"}, + {file = "rpds_py-0.22.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:02fbb9c288ae08bcb34fb41d516d5eeb0455ac35b5512d03181d755d80810059"}, + {file = "rpds_py-0.22.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f56a6b404f74ab372da986d240e2e002769a7d7102cc73eb238a4f72eec5284e"}, + {file = "rpds_py-0.22.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0a0461200769ab3b9ab7e513f6013b7a97fdeee41c29b9db343f3c5a8e2b9e61"}, + {file = "rpds_py-0.22.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8633e471c6207a039eff6aa116e35f69f3156b3989ea3e2d755f7bc41754a4a7"}, + {file = "rpds_py-0.22.3-cp312-cp312-win32.whl", hash = "sha256:593eba61ba0c3baae5bc9be2f5232430453fb4432048de28399ca7376de9c627"}, + {file = "rpds_py-0.22.3-cp312-cp312-win_amd64.whl", hash = "sha256:d115bffdd417c6d806ea9069237a4ae02f513b778e3789a359bc5856e0404cc4"}, + {file = "rpds_py-0.22.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:ea7433ce7e4bfc3a85654aeb6747babe3f66eaf9a1d0c1e7a4435bbdf27fea84"}, + {file = "rpds_py-0.22.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6dd9412824c4ce1aca56c47b0991e65bebb7ac3f4edccfd3f156150c96a7bf25"}, + {file = "rpds_py-0.22.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20070c65396f7373f5df4005862fa162db5d25d56150bddd0b3e8214e8ef45b4"}, + {file = "rpds_py-0.22.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0b09865a9abc0ddff4e50b5ef65467cd94176bf1e0004184eb915cbc10fc05c5"}, + {file = "rpds_py-0.22.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3453e8d41fe5f17d1f8e9c383a7473cd46a63661628ec58e07777c2fff7196dc"}, + {file = "rpds_py-0.22.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f5d36399a1b96e1a5fdc91e0522544580dbebeb1f77f27b2b0ab25559e103b8b"}, + {file = "rpds_py-0.22.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:009de23c9c9ee54bf11303a966edf4d9087cd43a6003672e6aa7def643d06518"}, + {file = "rpds_py-0.22.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1aef18820ef3e4587ebe8b3bc9ba6e55892a6d7b93bac6d29d9f631a3b4befbd"}, + {file = "rpds_py-0.22.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f60bd8423be1d9d833f230fdbccf8f57af322d96bcad6599e5a771b151398eb2"}, + {file = "rpds_py-0.22.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:62d9cfcf4948683a18a9aff0ab7e1474d407b7bab2ca03116109f8464698ab16"}, + {file = "rpds_py-0.22.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9253fc214112405f0afa7db88739294295f0e08466987f1d70e29930262b4c8f"}, + {file = "rpds_py-0.22.3-cp313-cp313-win32.whl", hash = "sha256:fb0ba113b4983beac1a2eb16faffd76cb41e176bf58c4afe3e14b9c681f702de"}, + {file = "rpds_py-0.22.3-cp313-cp313-win_amd64.whl", hash = "sha256:c58e2339def52ef6b71b8f36d13c3688ea23fa093353f3a4fee2556e62086ec9"}, + {file = "rpds_py-0.22.3-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:f82a116a1d03628a8ace4859556fb39fd1424c933341a08ea3ed6de1edb0283b"}, + {file = "rpds_py-0.22.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3dfcbc95bd7992b16f3f7ba05af8a64ca694331bd24f9157b49dadeeb287493b"}, + {file = "rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59259dc58e57b10e7e18ce02c311804c10c5a793e6568f8af4dead03264584d1"}, + {file = "rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5725dd9cc02068996d4438d397e255dcb1df776b7ceea3b9cb972bdb11260a83"}, + {file = "rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99b37292234e61325e7a5bb9689e55e48c3f5f603af88b1642666277a81f1fbd"}, + {file = "rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:27b1d3b3915a99208fee9ab092b8184c420f2905b7d7feb4aeb5e4a9c509b8a1"}, + {file = "rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f612463ac081803f243ff13cccc648578e2279295048f2a8d5eb430af2bae6e3"}, + {file = "rpds_py-0.22.3-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f73d3fef726b3243a811121de45193c0ca75f6407fe66f3f4e183c983573e130"}, + {file = "rpds_py-0.22.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:3f21f0495edea7fdbaaa87e633a8689cd285f8f4af5c869f27bc8074638ad69c"}, + {file = "rpds_py-0.22.3-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:1e9663daaf7a63ceccbbb8e3808fe90415b0757e2abddbfc2e06c857bf8c5e2b"}, + {file = "rpds_py-0.22.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a76e42402542b1fae59798fab64432b2d015ab9d0c8c47ba7addddbaf7952333"}, + {file = "rpds_py-0.22.3-cp313-cp313t-win32.whl", hash = "sha256:69803198097467ee7282750acb507fba35ca22cc3b85f16cf45fb01cb9097730"}, + {file = "rpds_py-0.22.3-cp313-cp313t-win_amd64.whl", hash = "sha256:f5cf2a0c2bdadf3791b5c205d55a37a54025c6e18a71c71f82bb536cf9a454bf"}, + {file = "rpds_py-0.22.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:378753b4a4de2a7b34063d6f95ae81bfa7b15f2c1a04a9518e8644e81807ebea"}, + {file = "rpds_py-0.22.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3445e07bf2e8ecfeef6ef67ac83de670358abf2996916039b16a218e3d95e97e"}, + {file = "rpds_py-0.22.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b2513ba235829860b13faa931f3b6846548021846ac808455301c23a101689d"}, + {file = "rpds_py-0.22.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eaf16ae9ae519a0e237a0f528fd9f0197b9bb70f40263ee57ae53c2b8d48aeb3"}, + {file = "rpds_py-0.22.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:583f6a1993ca3369e0f80ba99d796d8e6b1a3a2a442dd4e1a79e652116413091"}, + {file = "rpds_py-0.22.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4617e1915a539a0d9a9567795023de41a87106522ff83fbfaf1f6baf8e85437e"}, + {file = "rpds_py-0.22.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c150c7a61ed4a4f4955a96626574e9baf1adf772c2fb61ef6a5027e52803543"}, + {file = "rpds_py-0.22.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2fa4331c200c2521512595253f5bb70858b90f750d39b8cbfd67465f8d1b596d"}, + {file = "rpds_py-0.22.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:214b7a953d73b5e87f0ebece4a32a5bd83c60a3ecc9d4ec8f1dca968a2d91e99"}, + {file = "rpds_py-0.22.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:f47ad3d5f3258bd7058d2d506852217865afefe6153a36eb4b6928758041d831"}, + {file = "rpds_py-0.22.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:f276b245347e6e36526cbd4a266a417796fc531ddf391e43574cf6466c492520"}, + {file = "rpds_py-0.22.3-cp39-cp39-win32.whl", hash = "sha256:bbb232860e3d03d544bc03ac57855cd82ddf19c7a07651a7c0fdb95e9efea8b9"}, + {file = "rpds_py-0.22.3-cp39-cp39-win_amd64.whl", hash = "sha256:cfbc454a2880389dbb9b5b398e50d439e2e58669160f27b60e5eca11f68ae17c"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:d48424e39c2611ee1b84ad0f44fb3b2b53d473e65de061e3f460fc0be5f1939d"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:24e8abb5878e250f2eb0d7859a8e561846f98910326d06c0d51381fed59357bd"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b232061ca880db21fa14defe219840ad9b74b6158adb52ddf0e87bead9e8493"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac0a03221cdb5058ce0167ecc92a8c89e8d0decdc9e99a2ec23380793c4dcb96"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eb0c341fa71df5a4595f9501df4ac5abfb5a09580081dffbd1ddd4654e6e9123"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bf9db5488121b596dbfc6718c76092fda77b703c1f7533a226a5a9f65248f8ad"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b8db6b5b2d4491ad5b6bdc2bc7c017eec108acbf4e6785f42a9eb0ba234f4c9"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b3d504047aba448d70cf6fa22e06cb09f7cbd761939fdd47604f5e007675c24e"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:e61b02c3f7a1e0b75e20c3978f7135fd13cb6cf551bf4a6d29b999a88830a338"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:e35ba67d65d49080e8e5a1dd40101fccdd9798adb9b050ff670b7d74fa41c566"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:26fd7cac7dd51011a245f29a2cc6489c4608b5a8ce8d75661bb4a1066c52dfbe"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:177c7c0fce2855833819c98e43c262007f42ce86651ffbb84f37883308cb0e7d"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:bb47271f60660803ad11f4c61b42242b8c1312a31c98c578f79ef9387bbde21c"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:70fb28128acbfd264eda9bf47015537ba3fe86e40d046eb2963d75024be4d055"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:44d61b4b7d0c2c9ac019c314e52d7cbda0ae31078aabd0f22e583af3e0d79723"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f0e260eaf54380380ac3808aa4ebe2d8ca28b9087cf411649f96bad6900c728"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b25bc607423935079e05619d7de556c91fb6adeae9d5f80868dde3468657994b"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fb6116dfb8d1925cbdb52595560584db42a7f664617a1f7d7f6e32f138cdf37d"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a63cbdd98acef6570c62b92a1e43266f9e8b21e699c363c0fef13bd530799c11"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2b8f60e1b739a74bab7e01fcbe3dddd4657ec685caa04681df9d562ef15b625f"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:2e8b55d8517a2fda8d95cb45d62a5a8bbf9dd0ad39c5b25c8833efea07b880ca"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:2de29005e11637e7a2361fa151f780ff8eb2543a0da1413bb951e9f14b699ef3"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:666ecce376999bf619756a24ce15bb14c5bfaf04bf00abc7e663ce17c3f34fe7"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:5246b14ca64a8675e0a7161f7af68fe3e910e6b90542b4bfb5439ba752191df6"}, + {file = "rpds_py-0.22.3.tar.gz", hash = "sha256:e32fee8ab45d3c2db6da19a5323bc3362237c8b653c70194414b892fd06a080d"}, ] [[package]] @@ -4240,13 +4474,13 @@ pyasn1 = ">=0.1.3" [[package]] name = "s3transfer" -version = "0.10.3" +version = "0.10.4" description = "An Amazon S3 Transfer Manager" optional = false python-versions = ">=3.8" files = [ - {file = "s3transfer-0.10.3-py3-none-any.whl", hash = "sha256:263ed587a5803c6c708d3ce44dc4dfedaab4c1a32e8329bab818933d79ddcf5d"}, - {file = "s3transfer-0.10.3.tar.gz", hash = "sha256:4f50ed74ab84d474ce614475e0b8d5047ff080810aac5d01ea25231cfc944b0c"}, + {file = "s3transfer-0.10.4-py3-none-any.whl", hash = "sha256:244a76a24355363a68164241438de1b72f8781664920260c48465896b712a41e"}, + {file = "s3transfer-0.10.4.tar.gz", hash = "sha256:29edc09801743c21eb5ecbc617a152df41d3c287f67b615f73e5f750583666a7"}, ] [package.dependencies] @@ -4257,121 +4491,26 @@ crt = ["botocore[crt] (>=1.33.2,<2.0a.0)"] [[package]] name = "safetensors" -version = "0.4.5" +version = "0.5.2" description = "" optional = false python-versions = ">=3.7" files = [ - {file = "safetensors-0.4.5-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:a63eaccd22243c67e4f2b1c3e258b257effc4acd78f3b9d397edc8cf8f1298a7"}, - {file = "safetensors-0.4.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:23fc9b4ec7b602915cbb4ec1a7c1ad96d2743c322f20ab709e2c35d1b66dad27"}, - {file = "safetensors-0.4.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6885016f34bef80ea1085b7e99b3c1f92cb1be78a49839203060f67b40aee761"}, - {file = "safetensors-0.4.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:133620f443450429322f238fda74d512c4008621227fccf2f8cf4a76206fea7c"}, - {file = "safetensors-0.4.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4fb3e0609ec12d2a77e882f07cced530b8262027f64b75d399f1504ffec0ba56"}, - {file = "safetensors-0.4.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d0f1dd769f064adc33831f5e97ad07babbd728427f98e3e1db6902e369122737"}, - {file = "safetensors-0.4.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c6d156bdb26732feada84f9388a9f135528c1ef5b05fae153da365ad4319c4c5"}, - {file = "safetensors-0.4.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9e347d77e2c77eb7624400ccd09bed69d35c0332f417ce8c048d404a096c593b"}, - {file = "safetensors-0.4.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9f556eea3aec1d3d955403159fe2123ddd68e880f83954ee9b4a3f2e15e716b6"}, - {file = "safetensors-0.4.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9483f42be3b6bc8ff77dd67302de8ae411c4db39f7224dec66b0eb95822e4163"}, - {file = "safetensors-0.4.5-cp310-none-win32.whl", hash = "sha256:7389129c03fadd1ccc37fd1ebbc773f2b031483b04700923c3511d2a939252cc"}, - {file = "safetensors-0.4.5-cp310-none-win_amd64.whl", hash = "sha256:e98ef5524f8b6620c8cdef97220c0b6a5c1cef69852fcd2f174bb96c2bb316b1"}, - {file = "safetensors-0.4.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:21f848d7aebd5954f92538552d6d75f7c1b4500f51664078b5b49720d180e47c"}, - {file = "safetensors-0.4.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bb07000b19d41e35eecef9a454f31a8b4718a185293f0d0b1c4b61d6e4487971"}, - {file = "safetensors-0.4.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09dedf7c2fda934ee68143202acff6e9e8eb0ddeeb4cfc24182bef999efa9f42"}, - {file = "safetensors-0.4.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:59b77e4b7a708988d84f26de3ebead61ef1659c73dcbc9946c18f3b1786d2688"}, - {file = "safetensors-0.4.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5d3bc83e14d67adc2e9387e511097f254bd1b43c3020440e708858c684cbac68"}, - {file = "safetensors-0.4.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39371fc551c1072976073ab258c3119395294cf49cdc1f8476794627de3130df"}, - {file = "safetensors-0.4.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6c19feda32b931cae0acd42748a670bdf56bee6476a046af20181ad3fee4090"}, - {file = "safetensors-0.4.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a659467495de201e2f282063808a41170448c78bada1e62707b07a27b05e6943"}, - {file = "safetensors-0.4.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bad5e4b2476949bcd638a89f71b6916fa9a5cae5c1ae7eede337aca2100435c0"}, - {file = "safetensors-0.4.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a3a315a6d0054bc6889a17f5668a73f94f7fe55121ff59e0a199e3519c08565f"}, - {file = "safetensors-0.4.5-cp311-none-win32.whl", hash = "sha256:a01e232e6d3d5cf8b1667bc3b657a77bdab73f0743c26c1d3c5dd7ce86bd3a92"}, - {file = "safetensors-0.4.5-cp311-none-win_amd64.whl", hash = "sha256:cbd39cae1ad3e3ef6f63a6f07296b080c951f24cec60188378e43d3713000c04"}, - {file = "safetensors-0.4.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:473300314e026bd1043cef391bb16a8689453363381561b8a3e443870937cc1e"}, - {file = "safetensors-0.4.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:801183a0f76dc647f51a2d9141ad341f9665602a7899a693207a82fb102cc53e"}, - {file = "safetensors-0.4.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1524b54246e422ad6fb6aea1ac71edeeb77666efa67230e1faf6999df9b2e27f"}, - {file = "safetensors-0.4.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b3139098e3e8b2ad7afbca96d30ad29157b50c90861084e69fcb80dec7430461"}, - {file = "safetensors-0.4.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65573dc35be9059770808e276b017256fa30058802c29e1038eb1c00028502ea"}, - {file = "safetensors-0.4.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fd33da8e9407559f8779c82a0448e2133737f922d71f884da27184549416bfed"}, - {file = "safetensors-0.4.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3685ce7ed036f916316b567152482b7e959dc754fcc4a8342333d222e05f407c"}, - {file = "safetensors-0.4.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dde2bf390d25f67908278d6f5d59e46211ef98e44108727084d4637ee70ab4f1"}, - {file = "safetensors-0.4.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7469d70d3de970b1698d47c11ebbf296a308702cbaae7fcb993944751cf985f4"}, - {file = "safetensors-0.4.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3a6ba28118636a130ccbb968bc33d4684c48678695dba2590169d5ab03a45646"}, - {file = "safetensors-0.4.5-cp312-none-win32.whl", hash = "sha256:c859c7ed90b0047f58ee27751c8e56951452ed36a67afee1b0a87847d065eec6"}, - {file = "safetensors-0.4.5-cp312-none-win_amd64.whl", hash = "sha256:b5a8810ad6a6f933fff6c276eae92c1da217b39b4d8b1bc1c0b8af2d270dc532"}, - {file = "safetensors-0.4.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:25e5f8e2e92a74f05b4ca55686234c32aac19927903792b30ee6d7bd5653d54e"}, - {file = "safetensors-0.4.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:81efb124b58af39fcd684254c645e35692fea81c51627259cdf6d67ff4458916"}, - {file = "safetensors-0.4.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:585f1703a518b437f5103aa9cf70e9bd437cb78eea9c51024329e4fb8a3e3679"}, - {file = "safetensors-0.4.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4b99fbf72e3faf0b2f5f16e5e3458b93b7d0a83984fe8d5364c60aa169f2da89"}, - {file = "safetensors-0.4.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b17b299ca9966ca983ecda1c0791a3f07f9ca6ab5ded8ef3d283fff45f6bcd5f"}, - {file = "safetensors-0.4.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:76ded72f69209c9780fdb23ea89e56d35c54ae6abcdec67ccb22af8e696e449a"}, - {file = "safetensors-0.4.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2783956926303dcfeb1de91a4d1204cd4089ab441e622e7caee0642281109db3"}, - {file = "safetensors-0.4.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d94581aab8c6b204def4d7320f07534d6ee34cd4855688004a4354e63b639a35"}, - {file = "safetensors-0.4.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:67e1e7cb8678bb1b37ac48ec0df04faf689e2f4e9e81e566b5c63d9f23748523"}, - {file = "safetensors-0.4.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:dbd280b07e6054ea68b0cb4b16ad9703e7d63cd6890f577cb98acc5354780142"}, - {file = "safetensors-0.4.5-cp37-cp37m-macosx_10_12_x86_64.whl", hash = "sha256:77d9b228da8374c7262046a36c1f656ba32a93df6cc51cd4453af932011e77f1"}, - {file = "safetensors-0.4.5-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:500cac01d50b301ab7bb192353317035011c5ceeef0fca652f9f43c000bb7f8d"}, - {file = "safetensors-0.4.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:75331c0c746f03158ded32465b7d0b0e24c5a22121743662a2393439c43a45cf"}, - {file = "safetensors-0.4.5-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:670e95fe34e0d591d0529e5e59fd9d3d72bc77b1444fcaa14dccda4f36b5a38b"}, - {file = "safetensors-0.4.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:098923e2574ff237c517d6e840acada8e5b311cb1fa226019105ed82e9c3b62f"}, - {file = "safetensors-0.4.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:13ca0902d2648775089fa6a0c8fc9e6390c5f8ee576517d33f9261656f851e3f"}, - {file = "safetensors-0.4.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f0032bedc869c56f8d26259fe39cd21c5199cd57f2228d817a0e23e8370af25"}, - {file = "safetensors-0.4.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f4b15f51b4f8f2a512341d9ce3475cacc19c5fdfc5db1f0e19449e75f95c7dc8"}, - {file = "safetensors-0.4.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:f6594d130d0ad933d885c6a7b75c5183cb0e8450f799b80a39eae2b8508955eb"}, - {file = "safetensors-0.4.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:60c828a27e852ded2c85fc0f87bf1ec20e464c5cd4d56ff0e0711855cc2e17f8"}, - {file = "safetensors-0.4.5-cp37-none-win32.whl", hash = "sha256:6d3de65718b86c3eeaa8b73a9c3d123f9307a96bbd7be9698e21e76a56443af5"}, - {file = "safetensors-0.4.5-cp37-none-win_amd64.whl", hash = "sha256:5a2d68a523a4cefd791156a4174189a4114cf0bf9c50ceb89f261600f3b2b81a"}, - {file = "safetensors-0.4.5-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:e7a97058f96340850da0601a3309f3d29d6191b0702b2da201e54c6e3e44ccf0"}, - {file = "safetensors-0.4.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:63bfd425e25f5c733f572e2246e08a1c38bd6f2e027d3f7c87e2e43f228d1345"}, - {file = "safetensors-0.4.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3664ac565d0e809b0b929dae7ccd74e4d3273cd0c6d1220c6430035befb678e"}, - {file = "safetensors-0.4.5-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:313514b0b9b73ff4ddfb4edd71860696dbe3c1c9dc4d5cc13dbd74da283d2cbf"}, - {file = "safetensors-0.4.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:31fa33ee326f750a2f2134a6174773c281d9a266ccd000bd4686d8021f1f3dac"}, - {file = "safetensors-0.4.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:09566792588d77b68abe53754c9f1308fadd35c9f87be939e22c623eaacbed6b"}, - {file = "safetensors-0.4.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:309aaec9b66cbf07ad3a2e5cb8a03205663324fea024ba391594423d0f00d9fe"}, - {file = "safetensors-0.4.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:53946c5813b8f9e26103c5efff4a931cc45d874f45229edd68557ffb35ffb9f8"}, - {file = "safetensors-0.4.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:868f9df9e99ad1e7f38c52194063a982bc88fedc7d05096f4f8160403aaf4bd6"}, - {file = "safetensors-0.4.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:9cc9449bd0b0bc538bd5e268221f0c5590bc5c14c1934a6ae359d44410dc68c4"}, - {file = "safetensors-0.4.5-cp38-none-win32.whl", hash = "sha256:83c4f13a9e687335c3928f615cd63a37e3f8ef072a3f2a0599fa09f863fb06a2"}, - {file = "safetensors-0.4.5-cp38-none-win_amd64.whl", hash = "sha256:b98d40a2ffa560653f6274e15b27b3544e8e3713a44627ce268f419f35c49478"}, - {file = "safetensors-0.4.5-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:cf727bb1281d66699bef5683b04d98c894a2803442c490a8d45cd365abfbdeb2"}, - {file = "safetensors-0.4.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:96f1d038c827cdc552d97e71f522e1049fef0542be575421f7684756a748e457"}, - {file = "safetensors-0.4.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:139fbee92570ecea774e6344fee908907db79646d00b12c535f66bc78bd5ea2c"}, - {file = "safetensors-0.4.5-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c36302c1c69eebb383775a89645a32b9d266878fab619819ce660309d6176c9b"}, - {file = "safetensors-0.4.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d641f5b8149ea98deb5ffcf604d764aad1de38a8285f86771ce1abf8e74c4891"}, - {file = "safetensors-0.4.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b4db6a61d968de73722b858038c616a1bebd4a86abe2688e46ca0cc2d17558f2"}, - {file = "safetensors-0.4.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b75a616e02f21b6f1d5785b20cecbab5e2bd3f6358a90e8925b813d557666ec1"}, - {file = "safetensors-0.4.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:788ee7d04cc0e0e7f944c52ff05f52a4415b312f5efd2ee66389fb7685ee030c"}, - {file = "safetensors-0.4.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:87bc42bd04fd9ca31396d3ca0433db0be1411b6b53ac5a32b7845a85d01ffc2e"}, - {file = "safetensors-0.4.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4037676c86365a721a8c9510323a51861d703b399b78a6b4486a54a65a975fca"}, - {file = "safetensors-0.4.5-cp39-none-win32.whl", hash = "sha256:1500418454529d0ed5c1564bda376c4ddff43f30fce9517d9bee7bcce5a8ef50"}, - {file = "safetensors-0.4.5-cp39-none-win_amd64.whl", hash = "sha256:9d1a94b9d793ed8fe35ab6d5cea28d540a46559bafc6aae98f30ee0867000cab"}, - {file = "safetensors-0.4.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:fdadf66b5a22ceb645d5435a0be7a0292ce59648ca1d46b352f13cff3ea80410"}, - {file = "safetensors-0.4.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d42ffd4c2259f31832cb17ff866c111684c87bd930892a1ba53fed28370c918c"}, - {file = "safetensors-0.4.5-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd8a1f6d2063a92cd04145c7fd9e31a1c7d85fbec20113a14b487563fdbc0597"}, - {file = "safetensors-0.4.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:951d2fcf1817f4fb0ef0b48f6696688a4e852a95922a042b3f96aaa67eedc920"}, - {file = "safetensors-0.4.5-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6ac85d9a8c1af0e3132371d9f2d134695a06a96993c2e2f0bbe25debb9e3f67a"}, - {file = "safetensors-0.4.5-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:e3cec4a29eb7fe8da0b1c7988bc3828183080439dd559f720414450de076fcab"}, - {file = "safetensors-0.4.5-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:21742b391b859e67b26c0b2ac37f52c9c0944a879a25ad2f9f9f3cd61e7fda8f"}, - {file = "safetensors-0.4.5-pp37-pypy37_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c7db3006a4915151ce1913652e907cdede299b974641a83fbc092102ac41b644"}, - {file = "safetensors-0.4.5-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f68bf99ea970960a237f416ea394e266e0361895753df06e3e06e6ea7907d98b"}, - {file = "safetensors-0.4.5-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8158938cf3324172df024da511839d373c40fbfaa83e9abf467174b2910d7b4c"}, - {file = "safetensors-0.4.5-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:540ce6c4bf6b58cb0fd93fa5f143bc0ee341c93bb4f9287ccd92cf898cc1b0dd"}, - {file = "safetensors-0.4.5-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:bfeaa1a699c6b9ed514bd15e6a91e74738b71125a9292159e3d6b7f0a53d2cde"}, - {file = "safetensors-0.4.5-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:01c8f00da537af711979e1b42a69a8ec9e1d7112f208e0e9b8a35d2c381085ef"}, - {file = "safetensors-0.4.5-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a0dd565f83b30f2ca79b5d35748d0d99dd4b3454f80e03dfb41f0038e3bdf180"}, - {file = "safetensors-0.4.5-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:023b6e5facda76989f4cba95a861b7e656b87e225f61811065d5c501f78cdb3f"}, - {file = "safetensors-0.4.5-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9633b663393d5796f0b60249549371e392b75a0b955c07e9c6f8708a87fc841f"}, - {file = "safetensors-0.4.5-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78dd8adfb48716233c45f676d6e48534d34b4bceb50162c13d1f0bdf6f78590a"}, - {file = "safetensors-0.4.5-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8e8deb16c4321d61ae72533b8451ec4a9af8656d1c61ff81aa49f966406e4b68"}, - {file = "safetensors-0.4.5-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:52452fa5999dc50c4decaf0c53aa28371f7f1e0fe5c2dd9129059fbe1e1599c7"}, - {file = "safetensors-0.4.5-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:d5f23198821e227cfc52d50fa989813513db381255c6d100927b012f0cfec63d"}, - {file = "safetensors-0.4.5-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f4beb84b6073b1247a773141a6331117e35d07134b3bb0383003f39971d414bb"}, - {file = "safetensors-0.4.5-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:68814d599d25ed2fdd045ed54d370d1d03cf35e02dce56de44c651f828fb9b7b"}, - {file = "safetensors-0.4.5-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0b6453c54c57c1781292c46593f8a37254b8b99004c68d6c3ce229688931a22"}, - {file = "safetensors-0.4.5-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:adaa9c6dead67e2dd90d634f89131e43162012479d86e25618e821a03d1eb1dc"}, - {file = "safetensors-0.4.5-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:73e7d408e9012cd17511b382b43547850969c7979efc2bc353f317abaf23c84c"}, - {file = "safetensors-0.4.5-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:775409ce0fcc58b10773fdb4221ed1eb007de10fe7adbdf8f5e8a56096b6f0bc"}, - {file = "safetensors-0.4.5-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:834001bed193e4440c4a3950a31059523ee5090605c907c66808664c932b549c"}, - {file = "safetensors-0.4.5.tar.gz", hash = "sha256:d73de19682deabb02524b3d5d1f8b3aaba94c72f1bbfc7911b9b9d5d391c0310"}, + {file = "safetensors-0.5.2-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:45b6092997ceb8aa3801693781a71a99909ab9cc776fbc3fa9322d29b1d3bef2"}, + {file = "safetensors-0.5.2-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:6d0d6a8ee2215a440e1296b843edf44fd377b055ba350eaba74655a2fe2c4bae"}, + {file = "safetensors-0.5.2-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:86016d40bcaa3bcc9a56cd74d97e654b5f4f4abe42b038c71e4f00a089c4526c"}, + {file = "safetensors-0.5.2-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:990833f70a5f9c7d3fc82c94507f03179930ff7d00941c287f73b6fcbf67f19e"}, + {file = "safetensors-0.5.2-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3dfa7c2f3fe55db34eba90c29df94bcdac4821043fc391cb5d082d9922013869"}, + {file = "safetensors-0.5.2-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:46ff2116150ae70a4e9c490d2ab6b6e1b1b93f25e520e540abe1b81b48560c3a"}, + {file = "safetensors-0.5.2-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ab696dfdc060caffb61dbe4066b86419107a24c804a4e373ba59be699ebd8d5"}, + {file = "safetensors-0.5.2-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:03c937100f38c9ff4c1507abea9928a6a9b02c9c1c9c3609ed4fb2bf413d4975"}, + {file = "safetensors-0.5.2-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:a00e737948791b94dad83cf0eafc09a02c4d8c2171a239e8c8572fe04e25960e"}, + {file = "safetensors-0.5.2-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:d3a06fae62418ec8e5c635b61a8086032c9e281f16c63c3af46a6efbab33156f"}, + {file = "safetensors-0.5.2-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:1506e4c2eda1431099cebe9abf6c76853e95d0b7a95addceaa74c6019c65d8cf"}, + {file = "safetensors-0.5.2-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5c5b5d9da594f638a259fca766046f44c97244cc7ab8bef161b3e80d04becc76"}, + {file = "safetensors-0.5.2-cp38-abi3-win32.whl", hash = "sha256:fe55c039d97090d1f85277d402954dd6ad27f63034fa81985a9cc59655ac3ee2"}, + {file = "safetensors-0.5.2-cp38-abi3-win_amd64.whl", hash = "sha256:78abdddd03a406646107f973c7843276e7b64e5e32623529dc17f3d94a20f589"}, + {file = "safetensors-0.5.2.tar.gz", hash = "sha256:cb4a8d98ba12fa016f4241932b1fc5e702e5143f5374bba0bbcf7ddc1c4cf2b8"}, ] [package.extras] @@ -4381,7 +4520,7 @@ jax = ["flax (>=0.6.3)", "jax (>=0.3.25)", "jaxlib (>=0.3.25)", "safetensors[num mlx = ["mlx (>=0.0.9)"] numpy = ["numpy (>=1.21.6)"] paddlepaddle = ["paddlepaddle (>=2.4.1)", "safetensors[numpy]"] -pinned-tf = ["safetensors[numpy]", "tensorflow (==2.11.0)"] +pinned-tf = ["safetensors[numpy]", "tensorflow (==2.18.0)"] quality = ["black (==22.3)", "click (==8.0.4)", "flake8 (>=3.8.3)", "isort (>=5.5.4)"] tensorflow = ["safetensors[numpy]", "tensorflow (>=2.11.0)"] testing = ["h5py (>=3.7.0)", "huggingface-hub (>=0.12.1)", "hypothesis (>=6.70.2)", "pytest (>=7.2.0)", "pytest-benchmark (>=4.0.0)", "safetensors[numpy]", "setuptools-rust (>=1.5.2)"] @@ -4451,35 +4590,60 @@ files = [ [[package]] name = "setuptools" -version = "75.2.0" +version = "75.8.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "setuptools-75.2.0-py3-none-any.whl", hash = "sha256:a7fcb66f68b4d9e8e66b42f9876150a3371558f98fa32222ffaa5bced76406f8"}, - {file = "setuptools-75.2.0.tar.gz", hash = "sha256:753bb6ebf1f465a1912e19ed1d41f403a79173a9acf66a42e7e6aec45c3c16ec"}, + {file = "setuptools-75.8.0-py3-none-any.whl", hash = "sha256:e3982f444617239225d675215d51f6ba05f845d4eec313da4418fdbb56fb27e3"}, + {file = "setuptools-75.8.0.tar.gz", hash = "sha256:c5afc8f407c626b8313a86e10311dd3f661c6cd9c09d4bf8c15c0e11f9f2b0e6"}, ] [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.5.2)"] -core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.collections", "jaraco.functools", "jaraco.text (>=3.7)", "more-itertools", "more-itertools (>=8.8)", "packaging", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.8.0)"] +core = ["importlib_metadata (>=6)", "jaraco.collections", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] enabler = ["pytest-enabler (>=2.2)"] -test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] -type = ["importlib-metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.11.*)", "pytest-mypy"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.7.2)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] +type = ["importlib_metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.14.*)", "pytest-mypy"] [[package]] name = "six" -version = "1.16.0" +version = "1.17.0" description = "Python 2 and 3 compatibility utilities" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" files = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, + {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, + {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, ] +[[package]] +name = "smart-open" +version = "7.1.0" +description = "Utils for streaming large files (S3, HDFS, GCS, Azure Blob Storage, gzip, bz2...)" +optional = true +python-versions = "<4.0,>=3.7" +files = [ + {file = "smart_open-7.1.0-py3-none-any.whl", hash = "sha256:4b8489bb6058196258bafe901730c7db0dcf4f083f316e97269c66f45502055b"}, + {file = "smart_open-7.1.0.tar.gz", hash = "sha256:a4f09f84f0f6d3637c6543aca7b5487438877a21360e7368ccf1f704789752ba"}, +] + +[package.dependencies] +wrapt = "*" + +[package.extras] +all = ["azure-common", "azure-core", "azure-storage-blob", "boto3", "google-cloud-storage (>=2.6.0)", "paramiko", "requests", "zstandard"] +azure = ["azure-common", "azure-core", "azure-storage-blob"] +gcs = ["google-cloud-storage (>=2.6.0)"] +http = ["requests"] +s3 = ["boto3"] +ssh = ["paramiko"] +test = ["awscli", "azure-common", "azure-core", "azure-storage-blob", "boto3", "google-cloud-storage (>=2.6.0)", "moto[server]", "numpy", "paramiko", "pyopenssl", "pytest", "pytest-benchmark", "pytest-rerunfailures", "requests", "responses", "zstandard"] +webhdfs = ["requests"] +zst = ["zstandard"] + [[package]] name = "sniffio" version = "1.3.1" @@ -4493,13 +4657,13 @@ files = [ [[package]] name = "starlette" -version = "0.40.0" +version = "0.41.3" description = "The little ASGI library that shines." optional = false python-versions = ">=3.8" files = [ - {file = "starlette-0.40.0-py3-none-any.whl", hash = "sha256:c494a22fae73805376ea6bf88439783ecfba9aac88a43911b48c653437e784c4"}, - {file = "starlette-0.40.0.tar.gz", hash = "sha256:1a3139688fb298ce5e2d661d37046a66ad996ce94be4d4983be019a23a04ea35"}, + {file = "starlette-0.41.3-py3-none-any.whl", hash = "sha256:44cedb2b7c77a9de33a8b74b2b90e9f50d11fcf25d8270ea525ad71a25374ff7"}, + {file = "starlette-0.41.3.tar.gz", hash = "sha256:0e4ab3d16522a255be6b28260b938eae2482f98ce5cc934cb08dce8dc3ba5835"}, ] [package.dependencies] @@ -4540,6 +4704,26 @@ files = [ [package.extras] widechars = ["wcwidth"] +[[package]] +name = "textual" +version = "1.0.0" +description = "Modern Text User Interface framework" +optional = true +python-versions = "<4.0.0,>=3.8.1" +files = [ + {file = "textual-1.0.0-py3-none-any.whl", hash = "sha256:2d4a701781c05104925e463ae370c630567c70c2880e92ab838052e3e23c986f"}, + {file = "textual-1.0.0.tar.gz", hash = "sha256:bec9fe63547c1c552569d1b75d309038b7d456c03f86dfa3706ddb099b151399"}, +] + +[package.dependencies] +markdown-it-py = {version = ">=2.1.0", extras = ["linkify", "plugins"]} +platformdirs = ">=3.6.0,<5" +rich = ">=13.3.3" +typing-extensions = ">=4.4.0,<5.0.0" + +[package.extras] +syntax = ["tree-sitter (>=0.23.0)", "tree-sitter-bash (>=0.23.0)", "tree-sitter-css (>=0.23.0)", "tree-sitter-go (>=0.23.0)", "tree-sitter-html (>=0.23.0)", "tree-sitter-java (>=0.23.0)", "tree-sitter-javascript (>=0.23.0)", "tree-sitter-json (>=0.24.0)", "tree-sitter-markdown (>=0.3.0)", "tree-sitter-python (>=0.23.0)", "tree-sitter-regex (>=0.24.0)", "tree-sitter-rust (>=0.23.0)", "tree-sitter-sql (>=0.3.0)", "tree-sitter-toml (>=0.6.0)", "tree-sitter-xml (>=0.7.0)", "tree-sitter-yaml (>=0.6.0)"] + [[package]] name = "tiktoken" version = "0.7.0" @@ -4605,111 +4789,26 @@ files = [ [[package]] name = "tokenizers" -version = "0.20.1" +version = "0.21.0" description = "" optional = false python-versions = ">=3.7" files = [ - {file = "tokenizers-0.20.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:439261da7c0a5c88bda97acb284d49fbdaf67e9d3b623c0bfd107512d22787a9"}, - {file = "tokenizers-0.20.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:03dae629d99068b1ea5416d50de0fea13008f04129cc79af77a2a6392792d93c"}, - {file = "tokenizers-0.20.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b61f561f329ffe4b28367798b89d60c4abf3f815d37413b6352bc6412a359867"}, - {file = "tokenizers-0.20.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ec870fce1ee5248a10be69f7a8408a234d6f2109f8ea827b4f7ecdbf08c9fd15"}, - {file = "tokenizers-0.20.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d388d1ea8b7447da784e32e3b86a75cce55887e3b22b31c19d0b186b1c677800"}, - {file = "tokenizers-0.20.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:299c85c1d21135bc01542237979bf25c32efa0d66595dd0069ae259b97fb2dbe"}, - {file = "tokenizers-0.20.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e96f6c14c9752bb82145636b614d5a78e9cde95edfbe0a85dad0dd5ddd6ec95c"}, - {file = "tokenizers-0.20.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc9e95ad49c932b80abfbfeaf63b155761e695ad9f8a58c52a47d962d76e310f"}, - {file = "tokenizers-0.20.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:f22dee205329a636148c325921c73cf3e412e87d31f4d9c3153b302a0200057b"}, - {file = "tokenizers-0.20.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a2ffd9a8895575ac636d44500c66dffaef133823b6b25067604fa73bbc5ec09d"}, - {file = "tokenizers-0.20.1-cp310-none-win32.whl", hash = "sha256:2847843c53f445e0f19ea842a4e48b89dd0db4e62ba6e1e47a2749d6ec11f50d"}, - {file = "tokenizers-0.20.1-cp310-none-win_amd64.whl", hash = "sha256:f9aa93eacd865f2798b9e62f7ce4533cfff4f5fbd50c02926a78e81c74e432cd"}, - {file = "tokenizers-0.20.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4a717dcb08f2dabbf27ae4b6b20cbbb2ad7ed78ce05a829fae100ff4b3c7ff15"}, - {file = "tokenizers-0.20.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3f84dad1ff1863c648d80628b1b55353d16303431283e4efbb6ab1af56a75832"}, - {file = "tokenizers-0.20.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:929c8f3afa16a5130a81ab5079c589226273ec618949cce79b46d96e59a84f61"}, - {file = "tokenizers-0.20.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d10766473954397e2d370f215ebed1cc46dcf6fd3906a2a116aa1d6219bfedc3"}, - {file = "tokenizers-0.20.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9300fac73ddc7e4b0330acbdda4efaabf74929a4a61e119a32a181f534a11b47"}, - {file = "tokenizers-0.20.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0ecaf7b0e39caeb1aa6dd6e0975c405716c82c1312b55ac4f716ef563a906969"}, - {file = "tokenizers-0.20.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5170be9ec942f3d1d317817ced8d749b3e1202670865e4fd465e35d8c259de83"}, - {file = "tokenizers-0.20.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef3f1ae08fa9aea5891cbd69df29913e11d3841798e0bfb1ff78b78e4e7ea0a4"}, - {file = "tokenizers-0.20.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ee86d4095d3542d73579e953c2e5e07d9321af2ffea6ecc097d16d538a2dea16"}, - {file = "tokenizers-0.20.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:86dcd08da163912e17b27bbaba5efdc71b4fbffb841530fdb74c5707f3c49216"}, - {file = "tokenizers-0.20.1-cp311-none-win32.whl", hash = "sha256:9af2dc4ee97d037bc6b05fa4429ddc87532c706316c5e11ce2f0596dfcfa77af"}, - {file = "tokenizers-0.20.1-cp311-none-win_amd64.whl", hash = "sha256:899152a78b095559c287b4c6d0099469573bb2055347bb8154db106651296f39"}, - {file = "tokenizers-0.20.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:407ab666b38e02228fa785e81f7cf79ef929f104bcccf68a64525a54a93ceac9"}, - {file = "tokenizers-0.20.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2f13a2d16032ebc8bd812eb8099b035ac65887d8f0c207261472803b9633cf3e"}, - {file = "tokenizers-0.20.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e98eee4dca22849fbb56a80acaa899eec5b72055d79637dd6aa15d5e4b8628c9"}, - {file = "tokenizers-0.20.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:47c1bcdd61e61136087459cb9e0b069ff23b5568b008265e5cbc927eae3387ce"}, - {file = "tokenizers-0.20.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:128c1110e950534426e2274837fc06b118ab5f2fa61c3436e60e0aada0ccfd67"}, - {file = "tokenizers-0.20.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e2e2d47a819d2954f2c1cd0ad51bb58ffac6f53a872d5d82d65d79bf76b9896d"}, - {file = "tokenizers-0.20.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bdd67a0e3503a9a7cf8bc5a4a49cdde5fa5bada09a51e4c7e1c73900297539bd"}, - {file = "tokenizers-0.20.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:689b93d2e26d04da337ac407acec8b5d081d8d135e3e5066a88edd5bdb5aff89"}, - {file = "tokenizers-0.20.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0c6a796ddcd9a19ad13cf146997cd5895a421fe6aec8fd970d69f9117bddb45c"}, - {file = "tokenizers-0.20.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3ea919687aa7001a8ff1ba36ac64f165c4e89035f57998fa6cedcfd877be619d"}, - {file = "tokenizers-0.20.1-cp312-none-win32.whl", hash = "sha256:6d3ac5c1f48358ffe20086bf065e843c0d0a9fce0d7f0f45d5f2f9fba3609ca5"}, - {file = "tokenizers-0.20.1-cp312-none-win_amd64.whl", hash = "sha256:b0874481aea54a178f2bccc45aa2d0c99cd3f79143a0948af6a9a21dcc49173b"}, - {file = "tokenizers-0.20.1-cp37-cp37m-macosx_10_12_x86_64.whl", hash = "sha256:96af92e833bd44760fb17f23f402e07a66339c1dcbe17d79a9b55bb0cc4f038e"}, - {file = "tokenizers-0.20.1-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:65f34e5b731a262dfa562820818533c38ce32a45864437f3d9c82f26c139ca7f"}, - {file = "tokenizers-0.20.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:17f98fccb5c12ab1ce1f471731a9cd86df5d4bd2cf2880c5a66b229802d96145"}, - {file = "tokenizers-0.20.1-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b8c0fc3542cf9370bf92c932eb71bdeb33d2d4aeeb4126d9fd567b60bd04cb30"}, - {file = "tokenizers-0.20.1-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b39356df4575d37f9b187bb623aab5abb7b62c8cb702867a1768002f814800c"}, - {file = "tokenizers-0.20.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfdad27b0e50544f6b838895a373db6114b85112ba5c0cefadffa78d6daae563"}, - {file = "tokenizers-0.20.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:094663dd0e85ee2e573126918747bdb40044a848fde388efb5b09d57bc74c680"}, - {file = "tokenizers-0.20.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14e4cf033a2aa207d7ac790e91adca598b679999710a632c4a494aab0fc3a1b2"}, - {file = "tokenizers-0.20.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:9310951c92c9fb91660de0c19a923c432f110dbfad1a2d429fbc44fa956bf64f"}, - {file = "tokenizers-0.20.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:05e41e302c315bd2ed86c02e917bf03a6cf7d2f652c9cee1a0eb0d0f1ca0d32c"}, - {file = "tokenizers-0.20.1-cp37-none-win32.whl", hash = "sha256:212231ab7dfcdc879baf4892ca87c726259fa7c887e1688e3f3cead384d8c305"}, - {file = "tokenizers-0.20.1-cp37-none-win_amd64.whl", hash = "sha256:896195eb9dfdc85c8c052e29947169c1fcbe75a254c4b5792cdbd451587bce85"}, - {file = "tokenizers-0.20.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:741fb22788482d09d68e73ece1495cfc6d9b29a06c37b3df90564a9cfa688e6d"}, - {file = "tokenizers-0.20.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:10be14ebd8082086a342d969e17fc2d6edc856c59dbdbddd25f158fa40eaf043"}, - {file = "tokenizers-0.20.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:514cf279b22fa1ae0bc08e143458c74ad3b56cd078b319464959685a35c53d5e"}, - {file = "tokenizers-0.20.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a647c5b7cb896d6430cf3e01b4e9a2d77f719c84cefcef825d404830c2071da2"}, - {file = "tokenizers-0.20.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7cdf379219e1e1dd432091058dab325a2e6235ebb23e0aec8d0508567c90cd01"}, - {file = "tokenizers-0.20.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ba72260449e16c4c2f6f3252823b059fbf2d31b32617e582003f2b18b415c39"}, - {file = "tokenizers-0.20.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:910b96ed87316e4277b23c7bcaf667ce849c7cc379a453fa179e7e09290eeb25"}, - {file = "tokenizers-0.20.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e53975a6694428a0586534cc1354b2408d4e010a3103117f617cbb550299797c"}, - {file = "tokenizers-0.20.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:07c4b7be58da142b0730cc4e5fd66bb7bf6f57f4986ddda73833cd39efef8a01"}, - {file = "tokenizers-0.20.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b605c540753e62199bf15cf69c333e934077ef2350262af2ccada46026f83d1c"}, - {file = "tokenizers-0.20.1-cp38-none-win32.whl", hash = "sha256:88b3bc76ab4db1ab95ead623d49c95205411e26302cf9f74203e762ac7e85685"}, - {file = "tokenizers-0.20.1-cp38-none-win_amd64.whl", hash = "sha256:d412a74cf5b3f68a90c615611a5aa4478bb303d1c65961d22db45001df68afcb"}, - {file = "tokenizers-0.20.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a25dcb2f41a0a6aac31999e6c96a75e9152fa0127af8ece46c2f784f23b8197a"}, - {file = "tokenizers-0.20.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a12c3cebb8c92e9c35a23ab10d3852aee522f385c28d0b4fe48c0b7527d59762"}, - {file = "tokenizers-0.20.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:02e18da58cf115b7c40de973609c35bde95856012ba42a41ee919c77935af251"}, - {file = "tokenizers-0.20.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f326a1ac51ae909b9760e34671c26cd0dfe15662f447302a9d5bb2d872bab8ab"}, - {file = "tokenizers-0.20.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0b4872647ea6f25224e2833b044b0b19084e39400e8ead3cfe751238b0802140"}, - {file = "tokenizers-0.20.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce6238a3311bb8e4c15b12600927d35c267b92a52c881ef5717a900ca14793f7"}, - {file = "tokenizers-0.20.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57b7a8880b208866508b06ce365dc631e7a2472a3faa24daa430d046fb56c885"}, - {file = "tokenizers-0.20.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a908c69c2897a68f412aa05ba38bfa87a02980df70f5a72fa8490479308b1f2d"}, - {file = "tokenizers-0.20.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:da1001aa46f4490099c82e2facc4fbc06a6a32bf7de3918ba798010954b775e0"}, - {file = "tokenizers-0.20.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:42c097390e2f0ed0a5c5d569e6669dd4e9fff7b31c6a5ce6e9c66a61687197de"}, - {file = "tokenizers-0.20.1-cp39-none-win32.whl", hash = "sha256:3d4d218573a3d8b121a1f8c801029d70444ffb6d8f129d4cca1c7b672ee4a24c"}, - {file = "tokenizers-0.20.1-cp39-none-win_amd64.whl", hash = "sha256:37d1e6f616c84fceefa7c6484a01df05caf1e207669121c66213cb5b2911d653"}, - {file = "tokenizers-0.20.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:48689da7a395df41114f516208d6550e3e905e1239cc5ad386686d9358e9cef0"}, - {file = "tokenizers-0.20.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:712f90ea33f9bd2586b4a90d697c26d56d0a22fd3c91104c5858c4b5b6489a79"}, - {file = "tokenizers-0.20.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:359eceb6a620c965988fc559cebc0a98db26713758ec4df43fb76d41486a8ed5"}, - {file = "tokenizers-0.20.1-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d3caf244ce89d24c87545aafc3448be15870096e796c703a0d68547187192e1"}, - {file = "tokenizers-0.20.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03b03cf8b9a32254b1bf8a305fb95c6daf1baae0c1f93b27f2b08c9759f41dee"}, - {file = "tokenizers-0.20.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:218e5a3561561ea0f0ef1559c6d95b825308dbec23fb55b70b92589e7ff2e1e8"}, - {file = "tokenizers-0.20.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f40df5e0294a95131cc5f0e0eb91fe86d88837abfbee46b9b3610b09860195a7"}, - {file = "tokenizers-0.20.1-pp37-pypy37_pp73-macosx_10_12_x86_64.whl", hash = "sha256:08aaa0d72bb65058e8c4b0455f61b840b156c557e2aca57627056624c3a93976"}, - {file = "tokenizers-0.20.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:998700177b45f70afeb206ad22c08d9e5f3a80639dae1032bf41e8cbc4dada4b"}, - {file = "tokenizers-0.20.1-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62f7fbd3c2c38b179556d879edae442b45f68312019c3a6013e56c3947a4e648"}, - {file = "tokenizers-0.20.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31e87fca4f6bbf5cc67481b562147fe932f73d5602734de7dd18a8f2eee9c6dd"}, - {file = "tokenizers-0.20.1-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:956f21d359ae29dd51ca5726d2c9a44ffafa041c623f5aa33749da87cfa809b9"}, - {file = "tokenizers-0.20.1-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:1fbbaf17a393c78d8aedb6a334097c91cb4119a9ced4764ab8cfdc8d254dc9f9"}, - {file = "tokenizers-0.20.1-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ebe63e31f9c1a970c53866d814e35ec2ec26fda03097c486f82f3891cee60830"}, - {file = "tokenizers-0.20.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:81970b80b8ac126910295f8aab2d7ef962009ea39e0d86d304769493f69aaa1e"}, - {file = "tokenizers-0.20.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:130e35e76f9337ed6c31be386e75d4925ea807055acf18ca1a9b0eec03d8fe23"}, - {file = "tokenizers-0.20.1-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cd28a8614f5c82a54ab2463554e84ad79526c5184cf4573bbac2efbbbcead457"}, - {file = "tokenizers-0.20.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9041ee665d0fa7f5c4ccf0f81f5e6b7087f797f85b143c094126fc2611fec9d0"}, - {file = "tokenizers-0.20.1-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:62eb9daea2a2c06bcd8113a5824af8ef8ee7405d3a71123ba4d52c79bb3d9f1a"}, - {file = "tokenizers-0.20.1-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f861889707b54a9ab1204030b65fd6c22bdd4a95205deec7994dc22a8baa2ea4"}, - {file = "tokenizers-0.20.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:89d5c337d74ea6e5e7dc8af124cf177be843bbb9ca6e58c01f75ea103c12c8a9"}, - {file = "tokenizers-0.20.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:0b7f515c83397e73292accdbbbedc62264e070bae9682f06061e2ddce67cacaf"}, - {file = "tokenizers-0.20.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e0305fc1ec6b1e5052d30d9c1d5c807081a7bd0cae46a33d03117082e91908c"}, - {file = "tokenizers-0.20.1-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5dc611e6ac0fa00a41de19c3bf6391a05ea201d2d22b757d63f5491ec0e67faa"}, - {file = "tokenizers-0.20.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c5ffe0d7f7bfcfa3b2585776ecf11da2e01c317027c8573c78ebcb8985279e23"}, - {file = "tokenizers-0.20.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:e7edb8ec12c100d5458d15b1e47c0eb30ad606a05641f19af7563bc3d1608c14"}, - {file = "tokenizers-0.20.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:de291633fb9303555793cc544d4a86e858da529b7d0b752bcaf721ae1d74b2c9"}, - {file = "tokenizers-0.20.1.tar.gz", hash = "sha256:84edcc7cdeeee45ceedb65d518fffb77aec69311c9c8e30f77ad84da3025f002"}, + {file = "tokenizers-0.21.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:3c4c93eae637e7d2aaae3d376f06085164e1660f89304c0ab2b1d08a406636b2"}, + {file = "tokenizers-0.21.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:f53ea537c925422a2e0e92a24cce96f6bc5046bbef24a1652a5edc8ba975f62e"}, + {file = "tokenizers-0.21.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b177fb54c4702ef611de0c069d9169f0004233890e0c4c5bd5508ae05abf193"}, + {file = "tokenizers-0.21.0-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6b43779a269f4629bebb114e19c3fca0223296ae9fea8bb9a7a6c6fb0657ff8e"}, + {file = "tokenizers-0.21.0-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9aeb255802be90acfd363626753fda0064a8df06031012fe7d52fd9a905eb00e"}, + {file = "tokenizers-0.21.0-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d8b09dbeb7a8d73ee204a70f94fc06ea0f17dcf0844f16102b9f414f0b7463ba"}, + {file = "tokenizers-0.21.0-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:400832c0904f77ce87c40f1a8a27493071282f785724ae62144324f171377273"}, + {file = "tokenizers-0.21.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e84ca973b3a96894d1707e189c14a774b701596d579ffc7e69debfc036a61a04"}, + {file = "tokenizers-0.21.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:eb7202d231b273c34ec67767378cd04c767e967fda12d4a9e36208a34e2f137e"}, + {file = "tokenizers-0.21.0-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:089d56db6782a73a27fd8abf3ba21779f5b85d4a9f35e3b493c7bbcbbf0d539b"}, + {file = "tokenizers-0.21.0-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:c87ca3dc48b9b1222d984b6b7490355a6fdb411a2d810f6f05977258400ddb74"}, + {file = "tokenizers-0.21.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:4145505a973116f91bc3ac45988a92e618a6f83eb458f49ea0790df94ee243ff"}, + {file = "tokenizers-0.21.0-cp39-abi3-win32.whl", hash = "sha256:eb1702c2f27d25d9dd5b389cc1f2f51813e99f8ca30d9e25348db6585a97e24a"}, + {file = "tokenizers-0.21.0-cp39-abi3-win_amd64.whl", hash = "sha256:87841da5a25a3a5f70c102de371db120f41873b854ba65e52bccd57df5a3780c"}, + {file = "tokenizers-0.21.0.tar.gz", hash = "sha256:ee0894bf311b75b0c03079f33859ae4b2334d675d4e93f5a4132e1eae2834fe4"}, ] [package.dependencies] @@ -4722,42 +4821,69 @@ testing = ["black (==22.3)", "datasets", "numpy", "pytest", "requests", "ruff"] [[package]] name = "tomli" -version = "2.0.2" +version = "2.2.1" description = "A lil' TOML parser" optional = false python-versions = ">=3.8" files = [ - {file = "tomli-2.0.2-py3-none-any.whl", hash = "sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38"}, - {file = "tomli-2.0.2.tar.gz", hash = "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed"}, + {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, + {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"}, + {file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"}, + {file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"}, + {file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"}, + {file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"}, + {file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"}, + {file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"}, + {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"}, + {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, ] [[package]] name = "torch" -version = "2.4.0" +version = "2.5.1" description = "Tensors and Dynamic neural networks in Python with strong GPU acceleration" optional = false python-versions = ">=3.8.0" files = [ - {file = "torch-2.4.0-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:4ed94583e244af51d6a8d28701ca5a9e02d1219e782f5a01dd401f90af17d8ac"}, - {file = "torch-2.4.0-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:c4ca297b7bd58b506bfd6e78ffd14eb97c0e7797dcd7965df62f50bb575d8954"}, - {file = "torch-2.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:2497cbc7b3c951d69b276ca51fe01c2865db67040ac67f5fc20b03e41d16ea4a"}, - {file = "torch-2.4.0-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:685418ab93730efbee71528821ff54005596970dd497bf03c89204fb7e3f71de"}, - {file = "torch-2.4.0-cp311-cp311-manylinux1_x86_64.whl", hash = "sha256:e743adadd8c8152bb8373543964551a7cb7cc20ba898dc8f9c0cdbe47c283de0"}, - {file = "torch-2.4.0-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:7334325c0292cbd5c2eac085f449bf57d3690932eac37027e193ba775703c9e6"}, - {file = "torch-2.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:97730014da4c57ffacb3c09298c6ce05400606e890bd7a05008d13dd086e46b1"}, - {file = "torch-2.4.0-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:f169b4ea6dc93b3a33319611fcc47dc1406e4dd539844dcbd2dec4c1b96e166d"}, - {file = "torch-2.4.0-cp312-cp312-manylinux1_x86_64.whl", hash = "sha256:997084a0f9784d2a89095a6dc67c7925e21bf25dea0b3d069b41195016ccfcbb"}, - {file = "torch-2.4.0-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:bc3988e8b36d1e8b998d143255d9408d8c75da4ab6dd0dcfd23b623dfb0f0f57"}, - {file = "torch-2.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:3374128bbf7e62cdaed6c237bfd39809fbcfaa576bee91e904706840c3f2195c"}, - {file = "torch-2.4.0-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:91aaf00bfe1ffa44dc5b52809d9a95129fca10212eca3ac26420eb11727c6288"}, - {file = "torch-2.4.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:cc30457ea5489c62747d3306438af00c606b509d78822a88f804202ba63111ed"}, - {file = "torch-2.4.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:a046491aaf96d1215e65e1fa85911ef2ded6d49ea34c8df4d0638879f2402eef"}, - {file = "torch-2.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:688eec9240f3ce775f22e1e1a5ab9894f3d5fe60f3f586deb7dbd23a46a83916"}, - {file = "torch-2.4.0-cp38-none-macosx_11_0_arm64.whl", hash = "sha256:3af4de2a618fb065e78404c4ba27a818a7b7957eaeff28c6c66ce7fb504b68b8"}, - {file = "torch-2.4.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:618808d3f610d5f180e47a697d4ec90b810953bb1e020f424b2ac7fb0884b545"}, - {file = "torch-2.4.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:ed765d232d23566052ba83632ec73a4fccde00b4c94ad45d63b471b09d63b7a7"}, - {file = "torch-2.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:a2feb98ac470109472fb10dfef38622a7ee08482a16c357863ebc7bc7db7c8f7"}, - {file = "torch-2.4.0-cp39-none-macosx_11_0_arm64.whl", hash = "sha256:8940fc8b97a4c61fdb5d46a368f21f4a3a562a17879e932eb51a5ec62310cb31"}, + {file = "torch-2.5.1-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:71328e1bbe39d213b8721678f9dcac30dfc452a46d586f1d514a6aa0a99d4744"}, + {file = "torch-2.5.1-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:34bfa1a852e5714cbfa17f27c49d8ce35e1b7af5608c4bc6e81392c352dbc601"}, + {file = "torch-2.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:32a037bd98a241df6c93e4c789b683335da76a2ac142c0973675b715102dc5fa"}, + {file = "torch-2.5.1-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:23d062bf70776a3d04dbe74db950db2a5245e1ba4f27208a87f0d743b0d06e86"}, + {file = "torch-2.5.1-cp311-cp311-manylinux1_x86_64.whl", hash = "sha256:de5b7d6740c4b636ef4db92be922f0edc425b65ed78c5076c43c42d362a45457"}, + {file = "torch-2.5.1-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:340ce0432cad0d37f5a31be666896e16788f1adf8ad7be481196b503dad675b9"}, + {file = "torch-2.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:603c52d2fe06433c18b747d25f5c333f9c1d58615620578c326d66f258686f9a"}, + {file = "torch-2.5.1-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:31f8c39660962f9ae4eeec995e3049b5492eb7360dd4f07377658ef4d728fa4c"}, + {file = "torch-2.5.1-cp312-cp312-manylinux1_x86_64.whl", hash = "sha256:ed231a4b3a5952177fafb661213d690a72caaad97d5824dd4fc17ab9e15cec03"}, + {file = "torch-2.5.1-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:3f4b7f10a247e0dcd7ea97dc2d3bfbfc90302ed36d7f3952b0008d0df264e697"}, + {file = "torch-2.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:73e58e78f7d220917c5dbfad1a40e09df9929d3b95d25e57d9f8558f84c9a11c"}, + {file = "torch-2.5.1-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:8c712df61101964eb11910a846514011f0b6f5920c55dbf567bff8a34163d5b1"}, + {file = "torch-2.5.1-cp313-cp313-manylinux1_x86_64.whl", hash = "sha256:9b61edf3b4f6e3b0e0adda8b3960266b9009d02b37555971f4d1c8f7a05afed7"}, + {file = "torch-2.5.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:1f3b7fb3cf7ab97fae52161423f81be8c6b8afac8d9760823fd623994581e1a3"}, + {file = "torch-2.5.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:7974e3dce28b5a21fb554b73e1bc9072c25dde873fa00d54280861e7a009d7dc"}, + {file = "torch-2.5.1-cp39-cp39-win_amd64.whl", hash = "sha256:46c817d3ea33696ad3b9df5e774dba2257e9a4cd3c4a3afbf92f6bb13ac5ce2d"}, + {file = "torch-2.5.1-cp39-none-macosx_11_0_arm64.whl", hash = "sha256:8046768b7f6d35b85d101b4b38cba8aa2f3cd51952bc4c06a49580f2ce682291"}, ] [package.dependencies] @@ -4765,63 +4891,84 @@ filelock = "*" fsspec = "*" jinja2 = "*" networkx = "*" -nvidia-cublas-cu12 = {version = "12.1.3.1", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} -nvidia-cuda-cupti-cu12 = {version = "12.1.105", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} -nvidia-cuda-nvrtc-cu12 = {version = "12.1.105", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} -nvidia-cuda-runtime-cu12 = {version = "12.1.105", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-cublas-cu12 = {version = "12.4.5.8", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-cuda-cupti-cu12 = {version = "12.4.127", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-cuda-nvrtc-cu12 = {version = "12.4.127", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-cuda-runtime-cu12 = {version = "12.4.127", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} nvidia-cudnn-cu12 = {version = "9.1.0.70", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} -nvidia-cufft-cu12 = {version = "11.0.2.54", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} -nvidia-curand-cu12 = {version = "10.3.2.106", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} -nvidia-cusolver-cu12 = {version = "11.4.5.107", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} -nvidia-cusparse-cu12 = {version = "12.1.0.106", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} -nvidia-nccl-cu12 = {version = "2.20.5", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} -nvidia-nvtx-cu12 = {version = "12.1.105", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} -sympy = "*" -triton = {version = "3.0.0", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version < \"3.13\""} +nvidia-cufft-cu12 = {version = "11.2.1.3", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-curand-cu12 = {version = "10.3.5.147", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-cusolver-cu12 = {version = "11.6.1.9", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-cusparse-cu12 = {version = "12.3.1.170", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-nccl-cu12 = {version = "2.21.5", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-nvjitlink-cu12 = {version = "12.4.127", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-nvtx-cu12 = {version = "12.4.127", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +setuptools = {version = "*", markers = "python_version >= \"3.12\""} +sympy = {version = "1.13.1", markers = "python_version >= \"3.9\""} +triton = {version = "3.1.0", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version < \"3.13\""} typing-extensions = ">=4.8.0" [package.extras] opt-einsum = ["opt-einsum (>=3.3)"] -optree = ["optree (>=0.11.0)"] +optree = ["optree (>=0.12.0)"] + +[[package]] +name = "torchaudio" +version = "2.5.1" +description = "An audio package for PyTorch" +optional = true +python-versions = "*" +files = [ + {file = "torchaudio-2.5.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:901291d770aeeb1f51920bb5aa73ff82e9b7f26354a3c7b90d80ff0b4e9a5044"}, + {file = "torchaudio-2.5.1-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:abacbec3b6d695cf99ada8b1db55db933181c8ff7d283e246e2bbefdde674235"}, + {file = "torchaudio-2.5.1-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:9b3872c5dd5080be6322908d62365581a1dd9250e3dd6d47bab3f5b0854a5d1f"}, + {file = "torchaudio-2.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:4aead2d6b32426d0e657e243f6f5146f8d400bc8db9fe8a8000254baeec1202d"}, + {file = "torchaudio-2.5.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7005354aa7dda9ef908e13c2566ee1fe0bd6d7f5bae0583b5e53016cd229fc34"}, + {file = "torchaudio-2.5.1-cp311-cp311-manylinux1_x86_64.whl", hash = "sha256:7af3f7f92fd33bc9f036a60cdeda4cbeb6bccebd18eae89776dd1e8ed042672e"}, + {file = "torchaudio-2.5.1-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:4ba24769a72bd686903feaf1040c895d710af2ffbcd25ee7a9794ee285561b26"}, + {file = "torchaudio-2.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:cba8ccab1bff0496ccdc71ebbdcd31d0f7bf97ff3c46276425ff86460f6f8967"}, + {file = "torchaudio-2.5.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f1cbfdfd1bbdfbe7289d47a74f36ff6c5d87c3205606202fef5a7fb693f61cf0"}, + {file = "torchaudio-2.5.1-cp312-cp312-manylinux1_x86_64.whl", hash = "sha256:9c8fb06fbd8d2016e7b7caf15a3231867c792a2e3b0f2f8f9013633e9c2ce412"}, + {file = "torchaudio-2.5.1-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:6bb65416405f40e00b20701257c16e7493bfdd7188e02e87cc5b389c31c10c2c"}, + {file = "torchaudio-2.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:ec8f12d6be12aed248a0d65a76c7bb341ee5eef969fe2e9dc3154c7cfba1bdf4"}, + {file = "torchaudio-2.5.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a40a0e4b238564a19bf138c64d593c7b52d97c8737843d85d6ca09216241ae66"}, + {file = "torchaudio-2.5.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f2f0a4fa19137cad247087dcf4b85c56860f924c3ca4a89679299cf0e002ee33"}, + {file = "torchaudio-2.5.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:d29349944955eb061e774fe4d9eea4681c5bc9ff42ea39a877f8f14de1e4ed00"}, + {file = "torchaudio-2.5.1-cp39-cp39-win_amd64.whl", hash = "sha256:0be6d6f5127b17f9f1ac33fb02c8f1127bfea955de630c5cab6eb9daaef4db6d"}, +] + +[package.dependencies] +torch = "2.5.1" [[package]] name = "torchvision" -version = "0.19.0" +version = "0.20.1" description = "image and video datasets and models for torch deep learning" optional = true python-versions = ">=3.8" files = [ - {file = "torchvision-0.19.0-1-cp310-cp310-win_amd64.whl", hash = "sha256:6ed066aae5c50465d7c4761357aefe5dbd2eb7075a33ab8c14b352fc2353ad4c"}, - {file = "torchvision-0.19.0-1-cp311-cp311-win_amd64.whl", hash = "sha256:6b1bce2e4c003d890a18f14ff289528707d918e38539ff890ef02aa31dae1b56"}, - {file = "torchvision-0.19.0-1-cp312-cp312-win_amd64.whl", hash = "sha256:13aee7a46e049c8c1e7d35a0394b0587a7e62ff3d1a822cd2bbbacb675ac4a09"}, - {file = "torchvision-0.19.0-1-cp38-cp38-win_amd64.whl", hash = "sha256:2acc436d043d4f81b3bc6929cbfa4ef1cdae4d8a0b04ec72ec30a497e9a38179"}, - {file = "torchvision-0.19.0-1-cp39-cp39-win_amd64.whl", hash = "sha256:b5f70f5a8bd9c8b00a076bf466b39b5cd679ef62587c47cc048adb04d9c5f155"}, - {file = "torchvision-0.19.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ec874ef85dcb24c69e600f6e276af892c80cde3ffdaeb7275efda463242bc2a8"}, - {file = "torchvision-0.19.0-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:106842b1e475b14d9a04ee0d6f5477d43100e3bb78e9d31e37422384d0d84179"}, - {file = "torchvision-0.19.0-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:d467d434005fd05a227a2ba7af4c591bb67e6d4a97bbd06eda8da83f43e9fd07"}, - {file = "torchvision-0.19.0-cp310-cp310-win_amd64.whl", hash = "sha256:f77ac31f7337d0f6f4b58e65582c6c93b9d9eeec7dfd7478896b5cdc19a2d60d"}, - {file = "torchvision-0.19.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dbf3aa71a3899244fc884303ed3c4604a160824fefac77e82317a5463efc1d9b"}, - {file = "torchvision-0.19.0-cp311-cp311-manylinux1_x86_64.whl", hash = "sha256:ec4162dc71d9db7f0b51d0f92491929c1419605ff436e1305e50de13504a1c30"}, - {file = "torchvision-0.19.0-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:4e6aa4fa3f0bc3599fa071c149e651a3e6bdd67c9161794478f9f91471c406a2"}, - {file = "torchvision-0.19.0-cp311-cp311-win_amd64.whl", hash = "sha256:ac5525d5cc09e425b5cf5752ecf66eefbbbd8c8cd945198ce35eb01a694e6069"}, - {file = "torchvision-0.19.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c09ef8ed184fa877f6251b620226e74f682b8f1d6b341456428d4955b8d9c670"}, - {file = "torchvision-0.19.0-cp312-cp312-manylinux1_x86_64.whl", hash = "sha256:02f1dd5cfc897957535b41b0258ec452d30de044e20c2de2c75869f7708e7656"}, - {file = "torchvision-0.19.0-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:be0f27a28b8e9f2ae98a31af34a4bdd2a5bf154d92bd73a5797c8d2156fb3ab6"}, - {file = "torchvision-0.19.0-cp312-cp312-win_amd64.whl", hash = "sha256:a6ba7756f75c80212e51d3576f85ea204589e0c16efdb9b835dd677bc8929a67"}, - {file = "torchvision-0.19.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:854e967a16a9409e941b5bbe5aa357b23f7158bccb9de35ae20fd4945f05ecd1"}, - {file = "torchvision-0.19.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:d9afb8a3c3ce99a161a64c2a3b91cb545632a72118053cbfb84e87a02a8dcd02"}, - {file = "torchvision-0.19.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:079a696e0b2cb52e4be30afa8e9b3d7d280f02a2b5ffedd7e821fa1efd1a5a8d"}, - {file = "torchvision-0.19.0-cp38-cp38-win_amd64.whl", hash = "sha256:aaa338ff3a55a8c0f94e0e64eff6fe2af1fc933a95fd43812760e72ea66e986b"}, - {file = "torchvision-0.19.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dd1279571d4b68d5a53d9b7a35aedf91c4cb1e0b08099f6a1effa7b25b8c95e7"}, - {file = "torchvision-0.19.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:4d54b5e19b7ebebca7d0b08497b4c6335264cad04c94c05fa35988d9e9eed0c4"}, - {file = "torchvision-0.19.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:5f9a598dcf82bdfc8e4436ce74763b3877dabec3b33f94613b94ede13e3e4dee"}, - {file = "torchvision-0.19.0-cp39-cp39-win_amd64.whl", hash = "sha256:ec1281c10402234d470bfd4d53663d81f4364f293b2f8fe24d4a7a1adc78c90c"}, + {file = "torchvision-0.20.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4878fefb96ef293d06c27210918adc83c399d9faaf34cda5a63e129f772328f1"}, + {file = "torchvision-0.20.1-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:8ffbdf8bf5b30eade22d459f5a313329eeadb20dc75efa142987b53c007098c3"}, + {file = "torchvision-0.20.1-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:75f8a4d51a593c4bab6c9bf7d75bdd88691b00a53b07656678bc55a3a753dd73"}, + {file = "torchvision-0.20.1-cp310-cp310-win_amd64.whl", hash = "sha256:22c2fa44e20eb404b85e42b22b453863a14b0927d25e550fd4f84eea97fa5b39"}, + {file = "torchvision-0.20.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:344b339e15e6bbb59ee0700772616d0afefd209920c762b1604368d8c3458322"}, + {file = "torchvision-0.20.1-cp311-cp311-manylinux1_x86_64.whl", hash = "sha256:86f6523dee420000fe14c3527f6c8e0175139fda7d995b187f54a0b0ebec7eb6"}, + {file = "torchvision-0.20.1-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:a40d766345927639da322c693934e5f91b1ba2218846c7104b868dea2314ce8e"}, + {file = "torchvision-0.20.1-cp311-cp311-win_amd64.whl", hash = "sha256:5b501d5c04b034d2ecda96a31ed050e383cf8201352e4c9276ca249cbecfded0"}, + {file = "torchvision-0.20.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1a31256ff945d64f006bb306813a7c95a531fe16bfb2535c837dd4c104533d7a"}, + {file = "torchvision-0.20.1-cp312-cp312-manylinux1_x86_64.whl", hash = "sha256:17cd78adddf81dac57d7dccc9277a4d686425b1c55715f308769770cb26cad5c"}, + {file = "torchvision-0.20.1-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:9f853ba4497ac4691815ad41b523ee23cf5ba4f87b1ce869d704052e233ca8b7"}, + {file = "torchvision-0.20.1-cp312-cp312-win_amd64.whl", hash = "sha256:4a330422c36dbfc946d3a6c1caec3489db07ecdf3675d83369adb2e5a0ca17c4"}, + {file = "torchvision-0.20.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2cd58406978b813188cf4e9135b218775b57e0bb86d4a88f0339874b8a224819"}, + {file = "torchvision-0.20.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:408766b2f0ada9e1bc880d12346cec9638535af5df6459ba9ac4ce5c46402f91"}, + {file = "torchvision-0.20.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:abcb8005de8dc393dbd1310ecb669dc68ab664b9107af6d698a6341d1d3f2c3c"}, + {file = "torchvision-0.20.1-cp39-cp39-win_amd64.whl", hash = "sha256:ea9678163bbf19568f4f959d927f3751eeb833cc8eac949de507edde38c1fc9f"}, ] [package.dependencies] numpy = "*" pillow = ">=5.3.0,<8.3.dev0 || >=8.4.dev0" -torch = "2.4.0" +torch = "2.5.1" [package.extras] gdown = ["gdown (>=4.7.3)"] @@ -4829,59 +4976,60 @@ scipy = ["scipy"] [[package]] name = "tqdm" -version = "4.66.6" +version = "4.67.1" description = "Fast, Extensible Progress Meter" optional = false python-versions = ">=3.7" files = [ - {file = "tqdm-4.66.6-py3-none-any.whl", hash = "sha256:223e8b5359c2efc4b30555531f09e9f2f3589bcd7fdd389271191031b49b7a63"}, - {file = "tqdm-4.66.6.tar.gz", hash = "sha256:4bdd694238bef1485ce839d67967ab50af8f9272aab687c0d7702a01da0be090"}, + {file = "tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2"}, + {file = "tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2"}, ] [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} [package.extras] -dev = ["pytest (>=6)", "pytest-cov", "pytest-timeout", "pytest-xdist"] +dev = ["nbval", "pytest (>=6)", "pytest-asyncio (>=0.24)", "pytest-cov", "pytest-timeout"] +discord = ["requests"] notebook = ["ipywidgets (>=6)"] slack = ["slack-sdk"] telegram = ["requests"] [[package]] name = "transformers" -version = "4.46.2" +version = "4.48.3" description = "State-of-the-art Machine Learning for JAX, PyTorch and TensorFlow" optional = false -python-versions = ">=3.8.0" +python-versions = ">=3.9.0" files = [ - {file = "transformers-4.46.2-py3-none-any.whl", hash = "sha256:c921f4406b78e6518c97b618c5acd1cf8a4f2315b6b727f4bf9e01496eef849c"}, - {file = "transformers-4.46.2.tar.gz", hash = "sha256:3d85410881e1c074be767877bf33c83231ec11529f274a6044ecb20c157ba14e"}, + {file = "transformers-4.48.3-py3-none-any.whl", hash = "sha256:78697f990f5ef350c23b46bf86d5081ce96b49479ab180b2de7687267de8fd36"}, + {file = "transformers-4.48.3.tar.gz", hash = "sha256:a5e8f1e9a6430aa78215836be70cecd3f872d99eeda300f41ad6cc841724afdb"}, ] [package.dependencies] filelock = "*" -huggingface-hub = ">=0.23.2,<1.0" +huggingface-hub = ">=0.24.0,<1.0" numpy = ">=1.17" packaging = ">=20.0" pyyaml = ">=5.1" regex = "!=2019.12.17" requests = "*" safetensors = ">=0.4.1" -tokenizers = ">=0.20,<0.21" +tokenizers = ">=0.21,<0.22" tqdm = ">=4.27" [package.extras] accelerate = ["accelerate (>=0.26.0)"] -agents = ["Pillow (>=10.0.1,<=15.0)", "accelerate (>=0.26.0)", "datasets (!=2.5.0)", "diffusers", "opencv-python", "sentencepiece (>=0.1.91,!=0.1.92)", "torch"] -all = ["Pillow (>=10.0.1,<=15.0)", "accelerate (>=0.26.0)", "av (==9.2.0)", "codecarbon (==1.2.0)", "flax (>=0.4.1,<=0.7.0)", "jax (>=0.4.1,<=0.4.13)", "jaxlib (>=0.4.1,<=0.4.13)", "kenlm", "keras-nlp (>=0.3.1,<0.14.0)", "librosa", "onnxconverter-common", "optax (>=0.0.8,<=0.1.4)", "optuna", "phonemizer", "protobuf", "pyctcdecode (>=0.4.0)", "ray[tune] (>=2.7.0)", "scipy (<1.13.0)", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "tensorflow (>2.9,<2.16)", "tensorflow-text (<2.16)", "tf2onnx", "timm (<=0.9.16)", "tokenizers (>=0.20,<0.21)", "torch", "torchaudio", "torchvision"] +agents = ["Pillow (>=10.0.1,<=15.0)", "accelerate (>=0.26.0)", "datasets (!=2.5.0)", "diffusers", "opencv-python", "sentencepiece (>=0.1.91,!=0.1.92)", "torch (>=2.0)"] +all = ["Pillow (>=10.0.1,<=15.0)", "accelerate (>=0.26.0)", "av (==9.2.0)", "codecarbon (>=2.8.1)", "flax (>=0.4.1,<=0.7.0)", "jax (>=0.4.1,<=0.4.13)", "jaxlib (>=0.4.1,<=0.4.13)", "kenlm", "keras-nlp (>=0.3.1,<0.14.0)", "librosa", "onnxconverter-common", "optax (>=0.0.8,<=0.1.4)", "optuna", "phonemizer", "protobuf", "pyctcdecode (>=0.4.0)", "ray[tune] (>=2.7.0)", "scipy (<1.13.0)", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "tensorflow (>2.9,<2.16)", "tensorflow-text (<2.16)", "tf2onnx", "timm (<=1.0.11)", "tokenizers (>=0.21,<0.22)", "torch (>=2.0)", "torchaudio", "torchvision"] audio = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)"] benchmark = ["optimum-benchmark (>=0.3.0)"] -codecarbon = ["codecarbon (==1.2.0)"] +codecarbon = ["codecarbon (>=2.8.1)"] deepspeed = ["accelerate (>=0.26.0)", "deepspeed (>=0.9.3)"] -deepspeed-testing = ["GitPython (<3.1.19)", "accelerate (>=0.26.0)", "beautifulsoup4", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "deepspeed (>=0.9.3)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "nltk (<=3.8.1)", "optuna", "parameterized", "protobuf", "psutil", "pydantic", "pytest (>=7.2.0,<8.0.0)", "pytest-rich", "pytest-timeout", "pytest-xdist", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.5.1)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "sentencepiece (>=0.1.91,!=0.1.92)", "tensorboard", "timeout-decorator"] -dev = ["GitPython (<3.1.19)", "Pillow (>=10.0.1,<=15.0)", "accelerate (>=0.26.0)", "av (==9.2.0)", "beautifulsoup4", "codecarbon (==1.2.0)", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "flax (>=0.4.1,<=0.7.0)", "fugashi (>=1.0)", "ipadic (>=1.0.0,<2.0)", "isort (>=5.5.4)", "jax (>=0.4.1,<=0.4.13)", "jaxlib (>=0.4.1,<=0.4.13)", "kenlm", "keras-nlp (>=0.3.1,<0.14.0)", "libcst", "librosa", "nltk (<=3.8.1)", "onnxconverter-common", "optax (>=0.0.8,<=0.1.4)", "optuna", "parameterized", "phonemizer", "protobuf", "psutil", "pyctcdecode (>=0.4.0)", "pydantic", "pytest (>=7.2.0,<8.0.0)", "pytest-rich", "pytest-timeout", "pytest-xdist", "ray[tune] (>=2.7.0)", "rhoknp (>=1.1.0,<1.3.1)", "rich", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.5.1)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "scikit-learn", "scipy (<1.13.0)", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "sudachidict-core (>=20220729)", "sudachipy (>=0.6.6)", "tensorboard", "tensorflow (>2.9,<2.16)", "tensorflow-text (<2.16)", "tf2onnx", "timeout-decorator", "timm (<=0.9.16)", "tokenizers (>=0.20,<0.21)", "torch", "torchaudio", "torchvision", "unidic (>=1.0.2)", "unidic-lite (>=1.0.7)", "urllib3 (<2.0.0)"] -dev-tensorflow = ["GitPython (<3.1.19)", "Pillow (>=10.0.1,<=15.0)", "beautifulsoup4", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "isort (>=5.5.4)", "kenlm", "keras-nlp (>=0.3.1,<0.14.0)", "libcst", "librosa", "nltk (<=3.8.1)", "onnxconverter-common", "onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)", "parameterized", "phonemizer", "protobuf", "psutil", "pyctcdecode (>=0.4.0)", "pydantic", "pytest (>=7.2.0,<8.0.0)", "pytest-rich", "pytest-timeout", "pytest-xdist", "rich", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.5.1)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "scikit-learn", "sentencepiece (>=0.1.91,!=0.1.92)", "tensorboard", "tensorflow (>2.9,<2.16)", "tensorflow-text (<2.16)", "tf2onnx", "timeout-decorator", "tokenizers (>=0.20,<0.21)", "urllib3 (<2.0.0)"] -dev-torch = ["GitPython (<3.1.19)", "Pillow (>=10.0.1,<=15.0)", "accelerate (>=0.26.0)", "beautifulsoup4", "codecarbon (==1.2.0)", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "fugashi (>=1.0)", "ipadic (>=1.0.0,<2.0)", "isort (>=5.5.4)", "kenlm", "libcst", "librosa", "nltk (<=3.8.1)", "onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)", "optuna", "parameterized", "phonemizer", "protobuf", "psutil", "pyctcdecode (>=0.4.0)", "pydantic", "pytest (>=7.2.0,<8.0.0)", "pytest-rich", "pytest-timeout", "pytest-xdist", "ray[tune] (>=2.7.0)", "rhoknp (>=1.1.0,<1.3.1)", "rich", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.5.1)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "scikit-learn", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "sudachidict-core (>=20220729)", "sudachipy (>=0.6.6)", "tensorboard", "timeout-decorator", "timm (<=0.9.16)", "tokenizers (>=0.20,<0.21)", "torch", "torchaudio", "torchvision", "unidic (>=1.0.2)", "unidic-lite (>=1.0.7)", "urllib3 (<2.0.0)"] +deepspeed-testing = ["GitPython (<3.1.19)", "accelerate (>=0.26.0)", "beautifulsoup4", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "deepspeed (>=0.9.3)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "nltk (<=3.8.1)", "optuna", "parameterized", "protobuf", "psutil", "pydantic", "pytest (>=7.2.0,<8.0.0)", "pytest-asyncio", "pytest-rich", "pytest-timeout", "pytest-xdist", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.5.1)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "sentencepiece (>=0.1.91,!=0.1.92)", "tensorboard", "timeout-decorator"] +dev = ["GitPython (<3.1.19)", "Pillow (>=10.0.1,<=15.0)", "accelerate (>=0.26.0)", "av (==9.2.0)", "beautifulsoup4", "codecarbon (>=2.8.1)", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "flax (>=0.4.1,<=0.7.0)", "fugashi (>=1.0)", "ipadic (>=1.0.0,<2.0)", "isort (>=5.5.4)", "jax (>=0.4.1,<=0.4.13)", "jaxlib (>=0.4.1,<=0.4.13)", "kenlm", "keras-nlp (>=0.3.1,<0.14.0)", "libcst", "librosa", "nltk (<=3.8.1)", "onnxconverter-common", "optax (>=0.0.8,<=0.1.4)", "optuna", "parameterized", "phonemizer", "protobuf", "psutil", "pyctcdecode (>=0.4.0)", "pydantic", "pytest (>=7.2.0,<8.0.0)", "pytest-asyncio", "pytest-rich", "pytest-timeout", "pytest-xdist", "ray[tune] (>=2.7.0)", "rhoknp (>=1.1.0,<1.3.1)", "rich", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.5.1)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "scikit-learn", "scipy (<1.13.0)", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "sudachidict-core (>=20220729)", "sudachipy (>=0.6.6)", "tensorboard", "tensorflow (>2.9,<2.16)", "tensorflow-text (<2.16)", "tf2onnx", "timeout-decorator", "timm (<=1.0.11)", "tokenizers (>=0.21,<0.22)", "torch (>=2.0)", "torchaudio", "torchvision", "unidic (>=1.0.2)", "unidic-lite (>=1.0.7)", "urllib3 (<2.0.0)"] +dev-tensorflow = ["GitPython (<3.1.19)", "Pillow (>=10.0.1,<=15.0)", "beautifulsoup4", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "isort (>=5.5.4)", "kenlm", "keras-nlp (>=0.3.1,<0.14.0)", "libcst", "librosa", "nltk (<=3.8.1)", "onnxconverter-common", "onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)", "parameterized", "phonemizer", "protobuf", "psutil", "pyctcdecode (>=0.4.0)", "pydantic", "pytest (>=7.2.0,<8.0.0)", "pytest-asyncio", "pytest-rich", "pytest-timeout", "pytest-xdist", "rich", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.5.1)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "scikit-learn", "sentencepiece (>=0.1.91,!=0.1.92)", "tensorboard", "tensorflow (>2.9,<2.16)", "tensorflow-text (<2.16)", "tf2onnx", "timeout-decorator", "tokenizers (>=0.21,<0.22)", "urllib3 (<2.0.0)"] +dev-torch = ["GitPython (<3.1.19)", "Pillow (>=10.0.1,<=15.0)", "accelerate (>=0.26.0)", "beautifulsoup4", "codecarbon (>=2.8.1)", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "fugashi (>=1.0)", "ipadic (>=1.0.0,<2.0)", "isort (>=5.5.4)", "kenlm", "libcst", "librosa", "nltk (<=3.8.1)", "onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)", "optuna", "parameterized", "phonemizer", "protobuf", "psutil", "pyctcdecode (>=0.4.0)", "pydantic", "pytest (>=7.2.0,<8.0.0)", "pytest-asyncio", "pytest-rich", "pytest-timeout", "pytest-xdist", "ray[tune] (>=2.7.0)", "rhoknp (>=1.1.0,<1.3.1)", "rich", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.5.1)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "scikit-learn", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "sudachidict-core (>=20220729)", "sudachipy (>=0.6.6)", "tensorboard", "timeout-decorator", "timm (<=1.0.11)", "tokenizers (>=0.21,<0.22)", "torch (>=2.0)", "torchaudio", "torchvision", "unidic (>=1.0.2)", "unidic-lite (>=1.0.7)", "urllib3 (<2.0.0)"] flax = ["flax (>=0.4.1,<=0.7.0)", "jax (>=0.4.1,<=0.4.13)", "jaxlib (>=0.4.1,<=0.4.13)", "optax (>=0.0.8,<=0.1.4)", "scipy (<1.13.0)"] flax-speech = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)"] ftfy = ["ftfy"] @@ -4902,32 +5050,32 @@ serving = ["fastapi", "pydantic", "starlette", "uvicorn"] sigopt = ["sigopt"] sklearn = ["scikit-learn"] speech = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)", "torchaudio"] -testing = ["GitPython (<3.1.19)", "beautifulsoup4", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "nltk (<=3.8.1)", "parameterized", "psutil", "pydantic", "pytest (>=7.2.0,<8.0.0)", "pytest-rich", "pytest-timeout", "pytest-xdist", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.5.1)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "sentencepiece (>=0.1.91,!=0.1.92)", "tensorboard", "timeout-decorator"] +testing = ["GitPython (<3.1.19)", "beautifulsoup4", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "nltk (<=3.8.1)", "parameterized", "psutil", "pydantic", "pytest (>=7.2.0,<8.0.0)", "pytest-asyncio", "pytest-rich", "pytest-timeout", "pytest-xdist", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.5.1)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "sentencepiece (>=0.1.91,!=0.1.92)", "tensorboard", "timeout-decorator"] tf = ["keras-nlp (>=0.3.1,<0.14.0)", "onnxconverter-common", "tensorflow (>2.9,<2.16)", "tensorflow-text (<2.16)", "tf2onnx"] tf-cpu = ["keras (>2.9,<2.16)", "keras-nlp (>=0.3.1,<0.14.0)", "onnxconverter-common", "tensorflow-cpu (>2.9,<2.16)", "tensorflow-probability (<0.24)", "tensorflow-text (<2.16)", "tf2onnx"] tf-speech = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)"] tiktoken = ["blobfile", "tiktoken"] -timm = ["timm (<=0.9.16)"] -tokenizers = ["tokenizers (>=0.20,<0.21)"] -torch = ["accelerate (>=0.26.0)", "torch"] +timm = ["timm (<=1.0.11)"] +tokenizers = ["tokenizers (>=0.21,<0.22)"] +torch = ["accelerate (>=0.26.0)", "torch (>=2.0)"] torch-speech = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)", "torchaudio"] torch-vision = ["Pillow (>=10.0.1,<=15.0)", "torchvision"] -torchhub = ["filelock", "huggingface-hub (>=0.23.2,<1.0)", "importlib-metadata", "numpy (>=1.17)", "packaging (>=20.0)", "protobuf", "regex (!=2019.12.17)", "requests", "sentencepiece (>=0.1.91,!=0.1.92)", "tokenizers (>=0.20,<0.21)", "torch", "tqdm (>=4.27)"] +torchhub = ["filelock", "huggingface-hub (>=0.24.0,<1.0)", "importlib-metadata", "numpy (>=1.17)", "packaging (>=20.0)", "protobuf", "regex (!=2019.12.17)", "requests", "sentencepiece (>=0.1.91,!=0.1.92)", "tokenizers (>=0.21,<0.22)", "torch (>=2.0)", "tqdm (>=4.27)"] video = ["av (==9.2.0)"] vision = ["Pillow (>=10.0.1,<=15.0)"] [[package]] name = "triton" -version = "3.0.0" +version = "3.1.0" description = "A language and compiler for custom Deep Learning operations" optional = false python-versions = "*" files = [ - {file = "triton-3.0.0-1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e1efef76935b2febc365bfadf74bcb65a6f959a9872e5bddf44cc9e0adce1e1a"}, - {file = "triton-3.0.0-1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5ce8520437c602fb633f1324cc3871c47bee3b67acf9756c1a66309b60e3216c"}, - {file = "triton-3.0.0-1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:34e509deb77f1c067d8640725ef00c5cbfcb2052a1a3cb6a6d343841f92624eb"}, - {file = "triton-3.0.0-1-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bcbf3b1c48af6a28011a5c40a5b3b9b5330530c3827716b5fbf6d7adcc1e53e9"}, - {file = "triton-3.0.0-1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6e5727202f7078c56f91ff13ad0c1abab14a0e7f2c87e91b12b6f64f3e8ae609"}, + {file = "triton-3.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b0dd10a925263abbe9fa37dcde67a5e9b2383fc269fdf59f5657cac38c5d1d8"}, + {file = "triton-3.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f34f6e7885d1bf0eaaf7ba875a5f0ce6f3c13ba98f9503651c1e6dc6757ed5c"}, + {file = "triton-3.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8182f42fd8080a7d39d666814fa36c5e30cc00ea7eeeb1a2983dbb4c99a0fdc"}, + {file = "triton-3.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6dadaca7fc24de34e180271b5cf864c16755702e9f63a16f62df714a8099126a"}, + {file = "triton-3.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aafa9a20cd0d9fee523cd4504aa7131807a864cd77dcf6efe7e981f18b8c6c11"}, ] [package.dependencies] @@ -4960,6 +5108,20 @@ files = [ {file = "tzdata-2024.2.tar.gz", hash = "sha256:7d85cc416e9382e69095b7bdf4afd9e3880418a2413feec7069d533d6b4e31cc"}, ] +[[package]] +name = "uc-micro-py" +version = "1.0.3" +description = "Micro subset of unicode data files for linkify-it-py projects." +optional = true +python-versions = ">=3.7" +files = [ + {file = "uc-micro-py-1.0.3.tar.gz", hash = "sha256:d321b92cff673ec58027c04015fcaa8bb1e005478643ff4a500882eaab88c48a"}, + {file = "uc_micro_py-1.0.3-py3-none-any.whl", hash = "sha256:db1dffff340817673d7b466ec86114a9dc0e9d4d9b5ba229d9d60e5c12600cd5"}, +] + +[package.extras] +test = ["coverage", "pytest", "pytest-cov"] + [[package]] name = "urllib3" version = "1.26.20" @@ -4978,13 +5140,13 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] [[package]] name = "urllib3" -version = "2.2.3" +version = "2.3.0" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac"}, - {file = "urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9"}, + {file = "urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df"}, + {file = "urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d"}, ] [package.extras] @@ -5070,35 +5232,59 @@ dev = ["Cython (>=3.0,<4.0)", "setuptools (>=60)"] docs = ["Sphinx (>=4.1.2,<4.2.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"] test = ["aiohttp (>=3.10.5)", "flake8 (>=5.0,<6.0)", "mypy (>=0.800)", "psutil", "pyOpenSSL (>=23.0.0,<23.1.0)", "pycodestyle (>=2.9.0,<2.10.0)"] +[[package]] +name = "virtualenv" +version = "20.28.1" +description = "Virtual Python Environment builder" +optional = true +python-versions = ">=3.8" +files = [ + {file = "virtualenv-20.28.1-py3-none-any.whl", hash = "sha256:412773c85d4dab0409b83ec36f7a6499e72eaf08c80e81e9576bca61831c71cb"}, + {file = "virtualenv-20.28.1.tar.gz", hash = "sha256:5d34ab240fdb5d21549b76f9e8ff3af28252f5499fb6d6f031adac4e5a8c5329"}, +] + +[package.dependencies] +distlib = ">=0.3.7,<1" +filelock = ">=3.12.2,<4" +platformdirs = ">=3.9.1,<5" + +[package.extras] +docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] +test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] + [[package]] name = "vllm" -version = "0.6.3.post1" +version = "0.7.2" description = "A high-throughput and memory-efficient inference and serving engine for LLMs" optional = true -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "vllm-0.6.3.post1-cp38-abi3-manylinux1_x86_64.whl", hash = "sha256:691f10edb9869eb8b85bebfe2c0fb3c6a6b2cf2aefad7cdb2ab97688a57ca60e"}, - {file = "vllm-0.6.3.post1.tar.gz", hash = "sha256:0aae6ddd5348f86bf20e4f323c09e77d5ad2638d77f0d69323c5a63a40f8c143"}, + {file = "vllm-0.7.2-cp38-abi3-manylinux1_x86_64.whl", hash = "sha256:51fcb7d60149f75ec85d457fab6ccd88a2b1a6344c28aad46b0f8776b29e9d47"}, + {file = "vllm-0.7.2.tar.gz", hash = "sha256:bdeeda5624182e6a93895cbb7e20b6e88b04d22b8272d8a255741b28b36ae941"}, ] [package.dependencies] aiohttp = "*" -compressed-tensors = "0.6.0" +blake3 = "*" +cloudpickle = "*" +compressed-tensors = "0.9.1" +depyf = "0.18.0" einops = "*" fastapi = {version = ">=0.107.0,<0.113.dev0 || >0.114.0", markers = "python_version >= \"3.9\""} -filelock = ">=3.10.4" +filelock = ">=3.16.1" gguf = "0.10.0" -importlib-metadata = "*" -lm-format-enforcer = "0.10.6" -mistral-common = {version = ">=1.4.4", extras = ["opencv"]} +importlib_metadata = "*" +lark = "1.2.2" +lm-format-enforcer = ">=0.10.9,<0.11" +mistral_common = {version = ">=1.5.0", extras = ["opencv"]} msgspec = "*" numpy = "<2.0.0" -nvidia-ml-py = "*" -openai = ">=1.40.0" -outlines = ">=0.0.43,<0.1" +nvidia-ml-py = ">=12.560.30" +openai = ">=1.52.0" +outlines = "0.1.11" partial-json-parser = "*" pillow = "*" -prometheus-client = ">=0.18.0" +prometheus_client = ">=0.18.0" prometheus-fastapi-instrumentator = ">=7.0.0" protobuf = "*" psutil = "*" @@ -5106,115 +5292,107 @@ py-cpuinfo = "*" pydantic = ">=2.9" pyyaml = "*" pyzmq = "*" -ray = ">=2.9" +ray = {version = ">=2.9", extras = ["default"]} requests = ">=2.26.0" sentencepiece = "*" setuptools = {version = ">=74.1.1", markers = "python_version > \"3.11\""} six = {version = ">=1.16.0", markers = "python_version > \"3.11\""} tiktoken = ">=0.6.0" tokenizers = ">=0.19.1" -torch = "2.4.0" -torchvision = "0.19" +torch = "2.5.1" +torchaudio = "2.5.1" +torchvision = "0.20.1" tqdm = "*" -transformers = ">=4.45.2" -typing-extensions = ">=4.10" +transformers = ">=4.48.2" +typing_extensions = ">=4.10" uvicorn = {version = "*", extras = ["standard"]} -xformers = {version = "0.0.27.post2", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +xformers = {version = "0.0.28.post3", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +xgrammar = {version = ">=0.1.6", markers = "platform_machine == \"x86_64\""} [package.extras] audio = ["librosa", "soundfile"] +runai = ["boto3", "runai-model-streamer", "runai-model-streamer-s3"] tensorizer = ["tensorizer (>=2.9.0)"] +video = ["decord"] [[package]] name = "watchfiles" -version = "0.24.0" +version = "1.0.3" description = "Simple, modern and high performance file watching and code reload in python." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "watchfiles-0.24.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:083dc77dbdeef09fa44bb0f4d1df571d2e12d8a8f985dccde71ac3ac9ac067a0"}, - {file = "watchfiles-0.24.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e94e98c7cb94cfa6e071d401ea3342767f28eb5a06a58fafdc0d2a4974f4f35c"}, - {file = "watchfiles-0.24.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82ae557a8c037c42a6ef26c494d0631cacca040934b101d001100ed93d43f361"}, - {file = "watchfiles-0.24.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:acbfa31e315a8f14fe33e3542cbcafc55703b8f5dcbb7c1eecd30f141df50db3"}, - {file = "watchfiles-0.24.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b74fdffce9dfcf2dc296dec8743e5b0332d15df19ae464f0e249aa871fc1c571"}, - {file = "watchfiles-0.24.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:449f43f49c8ddca87c6b3980c9284cab6bd1f5c9d9a2b00012adaaccd5e7decd"}, - {file = "watchfiles-0.24.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4abf4ad269856618f82dee296ac66b0cd1d71450fc3c98532d93798e73399b7a"}, - {file = "watchfiles-0.24.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f895d785eb6164678ff4bb5cc60c5996b3ee6df3edb28dcdeba86a13ea0465e"}, - {file = "watchfiles-0.24.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7ae3e208b31be8ce7f4c2c0034f33406dd24fbce3467f77223d10cd86778471c"}, - {file = "watchfiles-0.24.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2efec17819b0046dde35d13fb8ac7a3ad877af41ae4640f4109d9154ed30a188"}, - {file = "watchfiles-0.24.0-cp310-none-win32.whl", hash = "sha256:6bdcfa3cd6fdbdd1a068a52820f46a815401cbc2cb187dd006cb076675e7b735"}, - {file = "watchfiles-0.24.0-cp310-none-win_amd64.whl", hash = "sha256:54ca90a9ae6597ae6dc00e7ed0a040ef723f84ec517d3e7ce13e63e4bc82fa04"}, - {file = "watchfiles-0.24.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:bdcd5538e27f188dd3c804b4a8d5f52a7fc7f87e7fd6b374b8e36a4ca03db428"}, - {file = "watchfiles-0.24.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2dadf8a8014fde6addfd3c379e6ed1a981c8f0a48292d662e27cabfe4239c83c"}, - {file = "watchfiles-0.24.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6509ed3f467b79d95fc62a98229f79b1a60d1b93f101e1c61d10c95a46a84f43"}, - {file = "watchfiles-0.24.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8360f7314a070c30e4c976b183d1d8d1585a4a50c5cb603f431cebcbb4f66327"}, - {file = "watchfiles-0.24.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:316449aefacf40147a9efaf3bd7c9bdd35aaba9ac5d708bd1eb5763c9a02bef5"}, - {file = "watchfiles-0.24.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73bde715f940bea845a95247ea3e5eb17769ba1010efdc938ffcb967c634fa61"}, - {file = "watchfiles-0.24.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3770e260b18e7f4e576edca4c0a639f704088602e0bc921c5c2e721e3acb8d15"}, - {file = "watchfiles-0.24.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa0fd7248cf533c259e59dc593a60973a73e881162b1a2f73360547132742823"}, - {file = "watchfiles-0.24.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d7a2e3b7f5703ffbd500dabdefcbc9eafeff4b9444bbdd5d83d79eedf8428fab"}, - {file = "watchfiles-0.24.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d831ee0a50946d24a53821819b2327d5751b0c938b12c0653ea5be7dea9c82ec"}, - {file = "watchfiles-0.24.0-cp311-none-win32.whl", hash = "sha256:49d617df841a63b4445790a254013aea2120357ccacbed00253f9c2b5dc24e2d"}, - {file = "watchfiles-0.24.0-cp311-none-win_amd64.whl", hash = "sha256:d3dcb774e3568477275cc76554b5a565024b8ba3a0322f77c246bc7111c5bb9c"}, - {file = "watchfiles-0.24.0-cp311-none-win_arm64.whl", hash = "sha256:9301c689051a4857d5b10777da23fafb8e8e921bcf3abe6448a058d27fb67633"}, - {file = "watchfiles-0.24.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:7211b463695d1e995ca3feb38b69227e46dbd03947172585ecb0588f19b0d87a"}, - {file = "watchfiles-0.24.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4b8693502d1967b00f2fb82fc1e744df128ba22f530e15b763c8d82baee15370"}, - {file = "watchfiles-0.24.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdab9555053399318b953a1fe1f586e945bc8d635ce9d05e617fd9fe3a4687d6"}, - {file = "watchfiles-0.24.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:34e19e56d68b0dad5cff62273107cf5d9fbaf9d75c46277aa5d803b3ef8a9e9b"}, - {file = "watchfiles-0.24.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:41face41f036fee09eba33a5b53a73e9a43d5cb2c53dad8e61fa6c9f91b5a51e"}, - {file = "watchfiles-0.24.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5148c2f1ea043db13ce9b0c28456e18ecc8f14f41325aa624314095b6aa2e9ea"}, - {file = "watchfiles-0.24.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7e4bd963a935aaf40b625c2499f3f4f6bbd0c3776f6d3bc7c853d04824ff1c9f"}, - {file = "watchfiles-0.24.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c79d7719d027b7a42817c5d96461a99b6a49979c143839fc37aa5748c322f234"}, - {file = "watchfiles-0.24.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:32aa53a9a63b7f01ed32e316e354e81e9da0e6267435c7243bf8ae0f10b428ef"}, - {file = "watchfiles-0.24.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ce72dba6a20e39a0c628258b5c308779b8697f7676c254a845715e2a1039b968"}, - {file = "watchfiles-0.24.0-cp312-none-win32.whl", hash = "sha256:d9018153cf57fc302a2a34cb7564870b859ed9a732d16b41a9b5cb2ebed2d444"}, - {file = "watchfiles-0.24.0-cp312-none-win_amd64.whl", hash = "sha256:551ec3ee2a3ac9cbcf48a4ec76e42c2ef938a7e905a35b42a1267fa4b1645896"}, - {file = "watchfiles-0.24.0-cp312-none-win_arm64.whl", hash = "sha256:b52a65e4ea43c6d149c5f8ddb0bef8d4a1e779b77591a458a893eb416624a418"}, - {file = "watchfiles-0.24.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:3d2e3ab79a1771c530233cadfd277fcc762656d50836c77abb2e5e72b88e3a48"}, - {file = "watchfiles-0.24.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:327763da824817b38ad125dcd97595f942d720d32d879f6c4ddf843e3da3fe90"}, - {file = "watchfiles-0.24.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd82010f8ab451dabe36054a1622870166a67cf3fce894f68895db6f74bbdc94"}, - {file = "watchfiles-0.24.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d64ba08db72e5dfd5c33be1e1e687d5e4fcce09219e8aee893a4862034081d4e"}, - {file = "watchfiles-0.24.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1cf1f6dd7825053f3d98f6d33f6464ebdd9ee95acd74ba2c34e183086900a827"}, - {file = "watchfiles-0.24.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:43e3e37c15a8b6fe00c1bce2473cfa8eb3484bbeecf3aefbf259227e487a03df"}, - {file = "watchfiles-0.24.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88bcd4d0fe1d8ff43675360a72def210ebad3f3f72cabfeac08d825d2639b4ab"}, - {file = "watchfiles-0.24.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:999928c6434372fde16c8f27143d3e97201160b48a614071261701615a2a156f"}, - {file = "watchfiles-0.24.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:30bbd525c3262fd9f4b1865cb8d88e21161366561cd7c9e1194819e0a33ea86b"}, - {file = "watchfiles-0.24.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:edf71b01dec9f766fb285b73930f95f730bb0943500ba0566ae234b5c1618c18"}, - {file = "watchfiles-0.24.0-cp313-none-win32.whl", hash = "sha256:f4c96283fca3ee09fb044f02156d9570d156698bc3734252175a38f0e8975f07"}, - {file = "watchfiles-0.24.0-cp313-none-win_amd64.whl", hash = "sha256:a974231b4fdd1bb7f62064a0565a6b107d27d21d9acb50c484d2cdba515b9366"}, - {file = "watchfiles-0.24.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:ee82c98bed9d97cd2f53bdb035e619309a098ea53ce525833e26b93f673bc318"}, - {file = "watchfiles-0.24.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:fd92bbaa2ecdb7864b7600dcdb6f2f1db6e0346ed425fbd01085be04c63f0b05"}, - {file = "watchfiles-0.24.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f83df90191d67af5a831da3a33dd7628b02a95450e168785586ed51e6d28943c"}, - {file = "watchfiles-0.24.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fca9433a45f18b7c779d2bae7beeec4f740d28b788b117a48368d95a3233ed83"}, - {file = "watchfiles-0.24.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b995bfa6bf01a9e09b884077a6d37070464b529d8682d7691c2d3b540d357a0c"}, - {file = "watchfiles-0.24.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ed9aba6e01ff6f2e8285e5aa4154e2970068fe0fc0998c4380d0e6278222269b"}, - {file = "watchfiles-0.24.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5171ef898299c657685306d8e1478a45e9303ddcd8ac5fed5bd52ad4ae0b69b"}, - {file = "watchfiles-0.24.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4933a508d2f78099162da473841c652ad0de892719043d3f07cc83b33dfd9d91"}, - {file = "watchfiles-0.24.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:95cf3b95ea665ab03f5a54765fa41abf0529dbaf372c3b83d91ad2cfa695779b"}, - {file = "watchfiles-0.24.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:01def80eb62bd5db99a798d5e1f5f940ca0a05986dcfae21d833af7a46f7ee22"}, - {file = "watchfiles-0.24.0-cp38-none-win32.whl", hash = "sha256:4d28cea3c976499475f5b7a2fec6b3a36208656963c1a856d328aeae056fc5c1"}, - {file = "watchfiles-0.24.0-cp38-none-win_amd64.whl", hash = "sha256:21ab23fdc1208086d99ad3f69c231ba265628014d4aed31d4e8746bd59e88cd1"}, - {file = "watchfiles-0.24.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:b665caeeda58625c3946ad7308fbd88a086ee51ccb706307e5b1fa91556ac886"}, - {file = "watchfiles-0.24.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5c51749f3e4e269231510da426ce4a44beb98db2dce9097225c338f815b05d4f"}, - {file = "watchfiles-0.24.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82b2509f08761f29a0fdad35f7e1638b8ab1adfa2666d41b794090361fb8b855"}, - {file = "watchfiles-0.24.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9a60e2bf9dc6afe7f743e7c9b149d1fdd6dbf35153c78fe3a14ae1a9aee3d98b"}, - {file = "watchfiles-0.24.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f7d9b87c4c55e3ea8881dfcbf6d61ea6775fffed1fedffaa60bd047d3c08c430"}, - {file = "watchfiles-0.24.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:78470906a6be5199524641f538bd2c56bb809cd4bf29a566a75051610bc982c3"}, - {file = "watchfiles-0.24.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:07cdef0c84c03375f4e24642ef8d8178e533596b229d32d2bbd69e5128ede02a"}, - {file = "watchfiles-0.24.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d337193bbf3e45171c8025e291530fb7548a93c45253897cd764a6a71c937ed9"}, - {file = "watchfiles-0.24.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ec39698c45b11d9694a1b635a70946a5bad066b593af863460a8e600f0dff1ca"}, - {file = "watchfiles-0.24.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2e28d91ef48eab0afb939fa446d8ebe77e2f7593f5f463fd2bb2b14132f95b6e"}, - {file = "watchfiles-0.24.0-cp39-none-win32.whl", hash = "sha256:7138eff8baa883aeaa074359daabb8b6c1e73ffe69d5accdc907d62e50b1c0da"}, - {file = "watchfiles-0.24.0-cp39-none-win_amd64.whl", hash = "sha256:b3ef2c69c655db63deb96b3c3e587084612f9b1fa983df5e0c3379d41307467f"}, - {file = "watchfiles-0.24.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:632676574429bee8c26be8af52af20e0c718cc7f5f67f3fb658c71928ccd4f7f"}, - {file = "watchfiles-0.24.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:a2a9891723a735d3e2540651184be6fd5b96880c08ffe1a98bae5017e65b544b"}, - {file = "watchfiles-0.24.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a7fa2bc0efef3e209a8199fd111b8969fe9db9c711acc46636686331eda7dd4"}, - {file = "watchfiles-0.24.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01550ccf1d0aed6ea375ef259706af76ad009ef5b0203a3a4cce0f6024f9b68a"}, - {file = "watchfiles-0.24.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:96619302d4374de5e2345b2b622dc481257a99431277662c30f606f3e22f42be"}, - {file = "watchfiles-0.24.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:85d5f0c7771dcc7a26c7a27145059b6bb0ce06e4e751ed76cdf123d7039b60b5"}, - {file = "watchfiles-0.24.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:951088d12d339690a92cef2ec5d3cfd957692834c72ffd570ea76a6790222777"}, - {file = "watchfiles-0.24.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49fb58bcaa343fedc6a9e91f90195b20ccb3135447dc9e4e2570c3a39565853e"}, - {file = "watchfiles-0.24.0.tar.gz", hash = "sha256:afb72325b74fa7a428c009c1b8be4b4d7c2afedafb2982827ef2156646df2fe1"}, + {file = "watchfiles-1.0.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:1da46bb1eefb5a37a8fb6fd52ad5d14822d67c498d99bda8754222396164ae42"}, + {file = "watchfiles-1.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2b961b86cd3973f5822826017cad7f5a75795168cb645c3a6b30c349094e02e3"}, + {file = "watchfiles-1.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34e87c7b3464d02af87f1059fedda5484e43b153ef519e4085fe1a03dd94801e"}, + {file = "watchfiles-1.0.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d9dd2b89a16cf7ab9c1170b5863e68de6bf83db51544875b25a5f05a7269e678"}, + {file = "watchfiles-1.0.3-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b4691234d31686dca133c920f94e478b548a8e7c750f28dbbc2e4333e0d3da9"}, + {file = "watchfiles-1.0.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:90b0fe1fcea9bd6e3084b44875e179b4adcc4057a3b81402658d0eb58c98edf8"}, + {file = "watchfiles-1.0.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0b90651b4cf9e158d01faa0833b073e2e37719264bcee3eac49fc3c74e7d304b"}, + {file = "watchfiles-1.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2e9fe695ff151b42ab06501820f40d01310fbd58ba24da8923ace79cf6d702d"}, + {file = "watchfiles-1.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62691f1c0894b001c7cde1195c03b7801aaa794a837bd6eef24da87d1542838d"}, + {file = "watchfiles-1.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:275c1b0e942d335fccb6014d79267d1b9fa45b5ac0639c297f1e856f2f532552"}, + {file = "watchfiles-1.0.3-cp310-cp310-win32.whl", hash = "sha256:06ce08549e49ba69ccc36fc5659a3d0ff4e3a07d542b895b8a9013fcab46c2dc"}, + {file = "watchfiles-1.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:f280b02827adc9d87f764972fbeb701cf5611f80b619c20568e1982a277d6146"}, + {file = "watchfiles-1.0.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:ffe709b1d0bc2e9921257569675674cafb3a5f8af689ab9f3f2b3f88775b960f"}, + {file = "watchfiles-1.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:418c5ce332f74939ff60691e5293e27c206c8164ce2b8ce0d9abf013003fb7fe"}, + {file = "watchfiles-1.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f492d2907263d6d0d52f897a68647195bc093dafed14508a8d6817973586b6b"}, + {file = "watchfiles-1.0.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:48c9f3bc90c556a854f4cab6a79c16974099ccfa3e3e150673d82d47a4bc92c9"}, + {file = "watchfiles-1.0.3-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:75d3bcfa90454dba8df12adc86b13b6d85fda97d90e708efc036c2760cc6ba44"}, + {file = "watchfiles-1.0.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5691340f259b8f76b45fb31b98e594d46c36d1dc8285efa7975f7f50230c9093"}, + {file = "watchfiles-1.0.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1e263cc718545b7f897baeac1f00299ab6fabe3e18caaacacb0edf6d5f35513c"}, + {file = "watchfiles-1.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c6cf7709ed3e55704cc06f6e835bf43c03bc8e3cb8ff946bf69a2e0a78d9d77"}, + {file = "watchfiles-1.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:703aa5e50e465be901e0e0f9d5739add15e696d8c26c53bc6fc00eb65d7b9469"}, + {file = "watchfiles-1.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:bfcae6aecd9e0cb425f5145afee871465b98b75862e038d42fe91fd753ddd780"}, + {file = "watchfiles-1.0.3-cp311-cp311-win32.whl", hash = "sha256:6a76494d2c5311584f22416c5a87c1e2cb954ff9b5f0988027bc4ef2a8a67181"}, + {file = "watchfiles-1.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:cf745cbfad6389c0e331786e5fe9ae3f06e9d9c2ce2432378e1267954793975c"}, + {file = "watchfiles-1.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:2dcc3f60c445f8ce14156854a072ceb36b83807ed803d37fdea2a50e898635d6"}, + {file = "watchfiles-1.0.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:93436ed550e429da007fbafb723e0769f25bae178fbb287a94cb4ccdf42d3af3"}, + {file = "watchfiles-1.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c18f3502ad0737813c7dad70e3e1cc966cc147fbaeef47a09463bbffe70b0a00"}, + {file = "watchfiles-1.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a5bc3ca468bb58a2ef50441f953e1f77b9a61bd1b8c347c8223403dc9b4ac9a"}, + {file = "watchfiles-1.0.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0d1ec043f02ca04bf21b1b32cab155ce90c651aaf5540db8eb8ad7f7e645cba8"}, + {file = "watchfiles-1.0.3-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f58d3bfafecf3d81c15d99fc0ecf4319e80ac712c77cf0ce2661c8cf8bf84066"}, + {file = "watchfiles-1.0.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1df924ba82ae9e77340101c28d56cbaff2c991bd6fe8444a545d24075abb0a87"}, + {file = "watchfiles-1.0.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:632a52dcaee44792d0965c17bdfe5dc0edad5b86d6a29e53d6ad4bf92dc0ff49"}, + {file = "watchfiles-1.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bf4b459d94a0387617a1b499f314aa04d8a64b7a0747d15d425b8c8b151da0"}, + {file = "watchfiles-1.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ca94c85911601b097d53caeeec30201736ad69a93f30d15672b967558df02885"}, + {file = "watchfiles-1.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:65ab1fb635476f6170b07e8e21db0424de94877e4b76b7feabfe11f9a5fc12b5"}, + {file = "watchfiles-1.0.3-cp312-cp312-win32.whl", hash = "sha256:49bc1bc26abf4f32e132652f4b3bfeec77d8f8f62f57652703ef127e85a3e38d"}, + {file = "watchfiles-1.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:48681c86f2cb08348631fed788a116c89c787fdf1e6381c5febafd782f6c3b44"}, + {file = "watchfiles-1.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:9e080cf917b35b20c889225a13f290f2716748362f6071b859b60b8847a6aa43"}, + {file = "watchfiles-1.0.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:e153a690b7255c5ced17895394b4f109d5dcc2a4f35cb809374da50f0e5c456a"}, + {file = "watchfiles-1.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ac1be85fe43b4bf9a251978ce5c3bb30e1ada9784290441f5423a28633a958a7"}, + {file = "watchfiles-1.0.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a2ec98e31e1844eac860e70d9247db9d75440fc8f5f679c37d01914568d18721"}, + {file = "watchfiles-1.0.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0179252846be03fa97d4d5f8233d1c620ef004855f0717712ae1c558f1974a16"}, + {file = "watchfiles-1.0.3-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:995c374e86fa82126c03c5b4630c4e312327ecfe27761accb25b5e1d7ab50ec8"}, + {file = "watchfiles-1.0.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:29b9cb35b7f290db1c31fb2fdf8fc6d3730cfa4bca4b49761083307f441cac5a"}, + {file = "watchfiles-1.0.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f8dc09ae69af50bead60783180f656ad96bd33ffbf6e7a6fce900f6d53b08f1"}, + {file = "watchfiles-1.0.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:489b80812f52a8d8c7b0d10f0d956db0efed25df2821c7a934f6143f76938bd6"}, + {file = "watchfiles-1.0.3-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:228e2247de583475d4cebf6b9af5dc9918abb99d1ef5ee737155bb39fb33f3c0"}, + {file = "watchfiles-1.0.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:1550be1a5cb3be08a3fb84636eaafa9b7119b70c71b0bed48726fd1d5aa9b868"}, + {file = "watchfiles-1.0.3-cp313-cp313-win32.whl", hash = "sha256:16db2d7e12f94818cbf16d4c8938e4d8aaecee23826344addfaaa671a1527b07"}, + {file = "watchfiles-1.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:160eff7d1267d7b025e983ca8460e8cc67b328284967cbe29c05f3c3163711a3"}, + {file = "watchfiles-1.0.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:c05b021f7b5aa333124f2a64d56e4cb9963b6efdf44e8d819152237bbd93ba15"}, + {file = "watchfiles-1.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:310505ad305e30cb6c5f55945858cdbe0eb297fc57378f29bacceb534ac34199"}, + {file = "watchfiles-1.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ddff3f8b9fa24a60527c137c852d0d9a7da2a02cf2151650029fdc97c852c974"}, + {file = "watchfiles-1.0.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:46e86ed457c3486080a72bc837300dd200e18d08183f12b6ca63475ab64ed651"}, + {file = "watchfiles-1.0.3-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f79fe7993e230a12172ce7d7c7db061f046f672f2b946431c81aff8f60b2758b"}, + {file = "watchfiles-1.0.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ea2b51c5f38bad812da2ec0cd7eec09d25f521a8b6b6843cbccedd9a1d8a5c15"}, + {file = "watchfiles-1.0.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fe4e740ea94978b2b2ab308cbf9270a246bcbb44401f77cc8740348cbaeac3d"}, + {file = "watchfiles-1.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9af037d3df7188ae21dc1c7624501f2f90d81be6550904e07869d8d0e6766655"}, + {file = "watchfiles-1.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:52bb50a4c4ca2a689fdba84ba8ecc6a4e6210f03b6af93181bb61c4ec3abaf86"}, + {file = "watchfiles-1.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c14a07bdb475eb696f85c715dbd0f037918ccbb5248290448488a0b4ef201aad"}, + {file = "watchfiles-1.0.3-cp39-cp39-win32.whl", hash = "sha256:be37f9b1f8934cd9e7eccfcb5612af9fb728fecbe16248b082b709a9d1b348bf"}, + {file = "watchfiles-1.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:ef9ec8068cf23458dbf36a08e0c16f0a2df04b42a8827619646637be1769300a"}, + {file = "watchfiles-1.0.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:84fac88278f42d61c519a6c75fb5296fd56710b05bbdcc74bdf85db409a03780"}, + {file = "watchfiles-1.0.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:c68be72b1666d93b266714f2d4092d78dc53bd11cf91ed5a3c16527587a52e29"}, + {file = "watchfiles-1.0.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:889a37e2acf43c377b5124166bece139b4c731b61492ab22e64d371cce0e6e80"}, + {file = "watchfiles-1.0.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ca05cacf2e5c4a97d02a2878a24020daca21dbb8823b023b978210a75c79098"}, + {file = "watchfiles-1.0.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:8af4b582d5fc1b8465d1d2483e5e7b880cc1a4e99f6ff65c23d64d070867ac58"}, + {file = "watchfiles-1.0.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:127de3883bdb29dbd3b21f63126bb8fa6e773b74eaef46521025a9ce390e1073"}, + {file = "watchfiles-1.0.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:713f67132346bdcb4c12df185c30cf04bdf4bf6ea3acbc3ace0912cab6b7cb8c"}, + {file = "watchfiles-1.0.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:abd85de513eb83f5ec153a802348e7a5baa4588b818043848247e3e8986094e8"}, + {file = "watchfiles-1.0.3.tar.gz", hash = "sha256:f3ff7da165c99a5412fe5dd2304dd2dbaaaa5da718aad942dcb3a178eaa70c56"}, ] [package.dependencies] @@ -5238,344 +5416,289 @@ test = ["websockets"] [[package]] name = "websockets" -version = "13.1" +version = "14.1" description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" optional = false +python-versions = ">=3.9" +files = [ + {file = "websockets-14.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a0adf84bc2e7c86e8a202537b4fd50e6f7f0e4a6b6bf64d7ccb96c4cd3330b29"}, + {file = "websockets-14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:90b5d9dfbb6d07a84ed3e696012610b6da074d97453bd01e0e30744b472c8179"}, + {file = "websockets-14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2177ee3901075167f01c5e335a6685e71b162a54a89a56001f1c3e9e3d2ad250"}, + {file = "websockets-14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f14a96a0034a27f9d47fd9788913924c89612225878f8078bb9d55f859272b0"}, + {file = "websockets-14.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f874ba705deea77bcf64a9da42c1f5fc2466d8f14daf410bc7d4ceae0a9fcb0"}, + {file = "websockets-14.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9607b9a442392e690a57909c362811184ea429585a71061cd5d3c2b98065c199"}, + {file = "websockets-14.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:bea45f19b7ca000380fbd4e02552be86343080120d074b87f25593ce1700ad58"}, + {file = "websockets-14.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:219c8187b3ceeadbf2afcf0f25a4918d02da7b944d703b97d12fb01510869078"}, + {file = "websockets-14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ad2ab2547761d79926effe63de21479dfaf29834c50f98c4bf5b5480b5838434"}, + {file = "websockets-14.1-cp310-cp310-win32.whl", hash = "sha256:1288369a6a84e81b90da5dbed48610cd7e5d60af62df9851ed1d1d23a9069f10"}, + {file = "websockets-14.1-cp310-cp310-win_amd64.whl", hash = "sha256:e0744623852f1497d825a49a99bfbec9bea4f3f946df6eb9d8a2f0c37a2fec2e"}, + {file = "websockets-14.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:449d77d636f8d9c17952628cc7e3b8faf6e92a17ec581ec0c0256300717e1512"}, + {file = "websockets-14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a35f704be14768cea9790d921c2c1cc4fc52700410b1c10948511039be824aac"}, + {file = "websockets-14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b1f3628a0510bd58968c0f60447e7a692933589b791a6b572fcef374053ca280"}, + {file = "websockets-14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c3deac3748ec73ef24fc7be0b68220d14d47d6647d2f85b2771cb35ea847aa1"}, + {file = "websockets-14.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7048eb4415d46368ef29d32133134c513f507fff7d953c18c91104738a68c3b3"}, + {file = "websockets-14.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6cf0ad281c979306a6a34242b371e90e891bce504509fb6bb5246bbbf31e7b6"}, + {file = "websockets-14.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cc1fc87428c1d18b643479caa7b15db7d544652e5bf610513d4a3478dbe823d0"}, + {file = "websockets-14.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f95ba34d71e2fa0c5d225bde3b3bdb152e957150100e75c86bc7f3964c450d89"}, + {file = "websockets-14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9481a6de29105d73cf4515f2bef8eb71e17ac184c19d0b9918a3701c6c9c4f23"}, + {file = "websockets-14.1-cp311-cp311-win32.whl", hash = "sha256:368a05465f49c5949e27afd6fbe0a77ce53082185bbb2ac096a3a8afaf4de52e"}, + {file = "websockets-14.1-cp311-cp311-win_amd64.whl", hash = "sha256:6d24fc337fc055c9e83414c94e1ee0dee902a486d19d2a7f0929e49d7d604b09"}, + {file = "websockets-14.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ed907449fe5e021933e46a3e65d651f641975a768d0649fee59f10c2985529ed"}, + {file = "websockets-14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:87e31011b5c14a33b29f17eb48932e63e1dcd3fa31d72209848652310d3d1f0d"}, + {file = "websockets-14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bc6ccf7d54c02ae47a48ddf9414c54d48af9c01076a2e1023e3b486b6e72c707"}, + {file = "websockets-14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9777564c0a72a1d457f0848977a1cbe15cfa75fa2f67ce267441e465717dcf1a"}, + {file = "websockets-14.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a655bde548ca98f55b43711b0ceefd2a88a71af6350b0c168aa77562104f3f45"}, + {file = "websockets-14.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a3dfff83ca578cada2d19e665e9c8368e1598d4e787422a460ec70e531dbdd58"}, + {file = "websockets-14.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6a6c9bcf7cdc0fd41cc7b7944447982e8acfd9f0d560ea6d6845428ed0562058"}, + {file = "websockets-14.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4b6caec8576e760f2c7dd878ba817653144d5f369200b6ddf9771d64385b84d4"}, + {file = "websockets-14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:eb6d38971c800ff02e4a6afd791bbe3b923a9a57ca9aeab7314c21c84bf9ff05"}, + {file = "websockets-14.1-cp312-cp312-win32.whl", hash = "sha256:1d045cbe1358d76b24d5e20e7b1878efe578d9897a25c24e6006eef788c0fdf0"}, + {file = "websockets-14.1-cp312-cp312-win_amd64.whl", hash = "sha256:90f4c7a069c733d95c308380aae314f2cb45bd8a904fb03eb36d1a4983a4993f"}, + {file = "websockets-14.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:3630b670d5057cd9e08b9c4dab6493670e8e762a24c2c94ef312783870736ab9"}, + {file = "websockets-14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:36ebd71db3b89e1f7b1a5deaa341a654852c3518ea7a8ddfdf69cc66acc2db1b"}, + {file = "websockets-14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5b918d288958dc3fa1c5a0b9aa3256cb2b2b84c54407f4813c45d52267600cd3"}, + {file = "websockets-14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00fe5da3f037041da1ee0cf8e308374e236883f9842c7c465aa65098b1c9af59"}, + {file = "websockets-14.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8149a0f5a72ca36720981418eeffeb5c2729ea55fa179091c81a0910a114a5d2"}, + {file = "websockets-14.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77569d19a13015e840b81550922056acabc25e3f52782625bc6843cfa034e1da"}, + {file = "websockets-14.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cf5201a04550136ef870aa60ad3d29d2a59e452a7f96b94193bee6d73b8ad9a9"}, + {file = "websockets-14.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:88cf9163ef674b5be5736a584c999e98daf3aabac6e536e43286eb74c126b9c7"}, + {file = "websockets-14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:836bef7ae338a072e9d1863502026f01b14027250a4545672673057997d5c05a"}, + {file = "websockets-14.1-cp313-cp313-win32.whl", hash = "sha256:0d4290d559d68288da9f444089fd82490c8d2744309113fc26e2da6e48b65da6"}, + {file = "websockets-14.1-cp313-cp313-win_amd64.whl", hash = "sha256:8621a07991add373c3c5c2cf89e1d277e49dc82ed72c75e3afc74bd0acc446f0"}, + {file = "websockets-14.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:01bb2d4f0a6d04538d3c5dfd27c0643269656c28045a53439cbf1c004f90897a"}, + {file = "websockets-14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:414ffe86f4d6f434a8c3b7913655a1a5383b617f9bf38720e7c0799fac3ab1c6"}, + {file = "websockets-14.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8fda642151d5affdee8a430bd85496f2e2517be3a2b9d2484d633d5712b15c56"}, + {file = "websockets-14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd7c11968bc3860d5c78577f0dbc535257ccec41750675d58d8dc66aa47fe52c"}, + {file = "websockets-14.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a032855dc7db987dff813583d04f4950d14326665d7e714d584560b140ae6b8b"}, + {file = "websockets-14.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7e7ea2f782408c32d86b87a0d2c1fd8871b0399dd762364c731d86c86069a78"}, + {file = "websockets-14.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:39450e6215f7d9f6f7bc2a6da21d79374729f5d052333da4d5825af8a97e6735"}, + {file = "websockets-14.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ceada5be22fa5a5a4cdeec74e761c2ee7db287208f54c718f2df4b7e200b8d4a"}, + {file = "websockets-14.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:3fc753451d471cff90b8f467a1fc0ae64031cf2d81b7b34e1811b7e2691bc4bc"}, + {file = "websockets-14.1-cp39-cp39-win32.whl", hash = "sha256:14839f54786987ccd9d03ed7f334baec0f02272e7ec4f6e9d427ff584aeea8b4"}, + {file = "websockets-14.1-cp39-cp39-win_amd64.whl", hash = "sha256:d9fd19ecc3a4d5ae82ddbfb30962cf6d874ff943e56e0c81f5169be2fda62979"}, + {file = "websockets-14.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e5dc25a9dbd1a7f61eca4b7cb04e74ae4b963d658f9e4f9aad9cd00b688692c8"}, + {file = "websockets-14.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:04a97aca96ca2acedf0d1f332c861c5a4486fdcba7bcef35873820f940c4231e"}, + {file = "websockets-14.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df174ece723b228d3e8734a6f2a6febbd413ddec39b3dc592f5a4aa0aff28098"}, + {file = "websockets-14.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:034feb9f4286476f273b9a245fb15f02c34d9586a5bc936aff108c3ba1b21beb"}, + {file = "websockets-14.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:660c308dabd2b380807ab64b62985eaccf923a78ebc572bd485375b9ca2b7dc7"}, + {file = "websockets-14.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5a42d3ecbb2db5080fc578314439b1d79eef71d323dc661aa616fb492436af5d"}, + {file = "websockets-14.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ddaa4a390af911da6f680be8be4ff5aaf31c4c834c1a9147bc21cbcbca2d4370"}, + {file = "websockets-14.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a4c805c6034206143fbabd2d259ec5e757f8b29d0a2f0bf3d2fe5d1f60147a4a"}, + {file = "websockets-14.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:205f672a6c2c671a86d33f6d47c9b35781a998728d2c7c2a3e1cf3333fcb62b7"}, + {file = "websockets-14.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef440054124728cc49b01c33469de06755e5a7a4e83ef61934ad95fc327fbb0"}, + {file = "websockets-14.1-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7591d6f440af7f73c4bd9404f3772bfee064e639d2b6cc8c94076e71b2471c1"}, + {file = "websockets-14.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:25225cc79cfebc95ba1d24cd3ab86aaa35bcd315d12fa4358939bd55e9bd74a5"}, + {file = "websockets-14.1-py3-none-any.whl", hash = "sha256:4d4fc827a20abe6d544a119896f6b78ee13fe81cbfef416f3f2ddf09a03f0e2e"}, + {file = "websockets-14.1.tar.gz", hash = "sha256:398b10c77d471c0aab20a845e7a60076b6390bfdaac7a6d2edb0d2c59d75e8d8"}, +] + +[[package]] +name = "wrapt" +version = "1.17.0" +description = "Module for decorators, wrappers and monkey patching." +optional = true python-versions = ">=3.8" files = [ - {file = "websockets-13.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f48c749857f8fb598fb890a75f540e3221d0976ed0bf879cf3c7eef34151acee"}, - {file = "websockets-13.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c7e72ce6bda6fb9409cc1e8164dd41d7c91466fb599eb047cfda72fe758a34a7"}, - {file = "websockets-13.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f779498eeec470295a2b1a5d97aa1bc9814ecd25e1eb637bd9d1c73a327387f6"}, - {file = "websockets-13.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4676df3fe46956fbb0437d8800cd5f2b6d41143b6e7e842e60554398432cf29b"}, - {file = "websockets-13.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7affedeb43a70351bb811dadf49493c9cfd1ed94c9c70095fd177e9cc1541fa"}, - {file = "websockets-13.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1971e62d2caa443e57588e1d82d15f663b29ff9dfe7446d9964a4b6f12c1e700"}, - {file = "websockets-13.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5f2e75431f8dc4a47f31565a6e1355fb4f2ecaa99d6b89737527ea917066e26c"}, - {file = "websockets-13.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:58cf7e75dbf7e566088b07e36ea2e3e2bd5676e22216e4cad108d4df4a7402a0"}, - {file = "websockets-13.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c90d6dec6be2c7d03378a574de87af9b1efea77d0c52a8301dd831ece938452f"}, - {file = "websockets-13.1-cp310-cp310-win32.whl", hash = "sha256:730f42125ccb14602f455155084f978bd9e8e57e89b569b4d7f0f0c17a448ffe"}, - {file = "websockets-13.1-cp310-cp310-win_amd64.whl", hash = "sha256:5993260f483d05a9737073be197371940c01b257cc45ae3f1d5d7adb371b266a"}, - {file = "websockets-13.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:61fc0dfcda609cda0fc9fe7977694c0c59cf9d749fbb17f4e9483929e3c48a19"}, - {file = "websockets-13.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ceec59f59d092c5007e815def4ebb80c2de330e9588e101cf8bd94c143ec78a5"}, - {file = "websockets-13.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c1dca61c6db1166c48b95198c0b7d9c990b30c756fc2923cc66f68d17dc558fd"}, - {file = "websockets-13.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:308e20f22c2c77f3f39caca508e765f8725020b84aa963474e18c59accbf4c02"}, - {file = "websockets-13.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62d516c325e6540e8a57b94abefc3459d7dab8ce52ac75c96cad5549e187e3a7"}, - {file = "websockets-13.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87c6e35319b46b99e168eb98472d6c7d8634ee37750d7693656dc766395df096"}, - {file = "websockets-13.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5f9fee94ebafbc3117c30be1844ed01a3b177bb6e39088bc6b2fa1dc15572084"}, - {file = "websockets-13.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7c1e90228c2f5cdde263253fa5db63e6653f1c00e7ec64108065a0b9713fa1b3"}, - {file = "websockets-13.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6548f29b0e401eea2b967b2fdc1c7c7b5ebb3eeb470ed23a54cd45ef078a0db9"}, - {file = "websockets-13.1-cp311-cp311-win32.whl", hash = "sha256:c11d4d16e133f6df8916cc5b7e3e96ee4c44c936717d684a94f48f82edb7c92f"}, - {file = "websockets-13.1-cp311-cp311-win_amd64.whl", hash = "sha256:d04f13a1d75cb2b8382bdc16ae6fa58c97337253826dfe136195b7f89f661557"}, - {file = "websockets-13.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:9d75baf00138f80b48f1eac72ad1535aac0b6461265a0bcad391fc5aba875cfc"}, - {file = "websockets-13.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9b6f347deb3dcfbfde1c20baa21c2ac0751afaa73e64e5b693bb2b848efeaa49"}, - {file = "websockets-13.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de58647e3f9c42f13f90ac7e5f58900c80a39019848c5547bc691693098ae1bd"}, - {file = "websockets-13.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1b54689e38d1279a51d11e3467dd2f3a50f5f2e879012ce8f2d6943f00e83f0"}, - {file = "websockets-13.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf1781ef73c073e6b0f90af841aaf98501f975d306bbf6221683dd594ccc52b6"}, - {file = "websockets-13.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d23b88b9388ed85c6faf0e74d8dec4f4d3baf3ecf20a65a47b836d56260d4b9"}, - {file = "websockets-13.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3c78383585f47ccb0fcf186dcb8a43f5438bd7d8f47d69e0b56f71bf431a0a68"}, - {file = "websockets-13.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d6d300f8ec35c24025ceb9b9019ae9040c1ab2f01cddc2bcc0b518af31c75c14"}, - {file = "websockets-13.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a9dcaf8b0cc72a392760bb8755922c03e17a5a54e08cca58e8b74f6902b433cf"}, - {file = "websockets-13.1-cp312-cp312-win32.whl", hash = "sha256:2f85cf4f2a1ba8f602298a853cec8526c2ca42a9a4b947ec236eaedb8f2dc80c"}, - {file = "websockets-13.1-cp312-cp312-win_amd64.whl", hash = "sha256:38377f8b0cdeee97c552d20cf1865695fcd56aba155ad1b4ca8779a5b6ef4ac3"}, - {file = "websockets-13.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a9ab1e71d3d2e54a0aa646ab6d4eebfaa5f416fe78dfe4da2839525dc5d765c6"}, - {file = "websockets-13.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b9d7439d7fab4dce00570bb906875734df13d9faa4b48e261c440a5fec6d9708"}, - {file = "websockets-13.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:327b74e915cf13c5931334c61e1a41040e365d380f812513a255aa804b183418"}, - {file = "websockets-13.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:325b1ccdbf5e5725fdcb1b0e9ad4d2545056479d0eee392c291c1bf76206435a"}, - {file = "websockets-13.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:346bee67a65f189e0e33f520f253d5147ab76ae42493804319b5716e46dddf0f"}, - {file = "websockets-13.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91a0fa841646320ec0d3accdff5b757b06e2e5c86ba32af2e0815c96c7a603c5"}, - {file = "websockets-13.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:18503d2c5f3943e93819238bf20df71982d193f73dcecd26c94514f417f6b135"}, - {file = "websockets-13.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:a9cd1af7e18e5221d2878378fbc287a14cd527fdd5939ed56a18df8a31136bb2"}, - {file = "websockets-13.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:70c5be9f416aa72aab7a2a76c90ae0a4fe2755c1816c153c1a2bcc3333ce4ce6"}, - {file = "websockets-13.1-cp313-cp313-win32.whl", hash = "sha256:624459daabeb310d3815b276c1adef475b3e6804abaf2d9d2c061c319f7f187d"}, - {file = "websockets-13.1-cp313-cp313-win_amd64.whl", hash = "sha256:c518e84bb59c2baae725accd355c8dc517b4a3ed8db88b4bc93c78dae2974bf2"}, - {file = "websockets-13.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:c7934fd0e920e70468e676fe7f1b7261c1efa0d6c037c6722278ca0228ad9d0d"}, - {file = "websockets-13.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:149e622dc48c10ccc3d2760e5f36753db9cacf3ad7bc7bbbfd7d9c819e286f23"}, - {file = "websockets-13.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a569eb1b05d72f9bce2ebd28a1ce2054311b66677fcd46cf36204ad23acead8c"}, - {file = "websockets-13.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95df24ca1e1bd93bbca51d94dd049a984609687cb2fb08a7f2c56ac84e9816ea"}, - {file = "websockets-13.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d8dbb1bf0c0a4ae8b40bdc9be7f644e2f3fb4e8a9aca7145bfa510d4a374eeb7"}, - {file = "websockets-13.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:035233b7531fb92a76beefcbf479504db8c72eb3bff41da55aecce3a0f729e54"}, - {file = "websockets-13.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:e4450fc83a3df53dec45922b576e91e94f5578d06436871dce3a6be38e40f5db"}, - {file = "websockets-13.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:463e1c6ec853202dd3657f156123d6b4dad0c546ea2e2e38be2b3f7c5b8e7295"}, - {file = "websockets-13.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:6d6855bbe70119872c05107e38fbc7f96b1d8cb047d95c2c50869a46c65a8e96"}, - {file = "websockets-13.1-cp38-cp38-win32.whl", hash = "sha256:204e5107f43095012b00f1451374693267adbb832d29966a01ecc4ce1db26faf"}, - {file = "websockets-13.1-cp38-cp38-win_amd64.whl", hash = "sha256:485307243237328c022bc908b90e4457d0daa8b5cf4b3723fd3c4a8012fce4c6"}, - {file = "websockets-13.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9b37c184f8b976f0c0a231a5f3d6efe10807d41ccbe4488df8c74174805eea7d"}, - {file = "websockets-13.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:163e7277e1a0bd9fb3c8842a71661ad19c6aa7bb3d6678dc7f89b17fbcc4aeb7"}, - {file = "websockets-13.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4b889dbd1342820cc210ba44307cf75ae5f2f96226c0038094455a96e64fb07a"}, - {file = "websockets-13.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:586a356928692c1fed0eca68b4d1c2cbbd1ca2acf2ac7e7ebd3b9052582deefa"}, - {file = "websockets-13.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7bd6abf1e070a6b72bfeb71049d6ad286852e285f146682bf30d0296f5fbadfa"}, - {file = "websockets-13.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2aad13a200e5934f5a6767492fb07151e1de1d6079c003ab31e1823733ae79"}, - {file = "websockets-13.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:df01aea34b6e9e33572c35cd16bae5a47785e7d5c8cb2b54b2acdb9678315a17"}, - {file = "websockets-13.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e54affdeb21026329fb0744ad187cf812f7d3c2aa702a5edb562b325191fcab6"}, - {file = "websockets-13.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9ef8aa8bdbac47f4968a5d66462a2a0935d044bf35c0e5a8af152d58516dbeb5"}, - {file = "websockets-13.1-cp39-cp39-win32.whl", hash = "sha256:deeb929efe52bed518f6eb2ddc00cc496366a14c726005726ad62c2dd9017a3c"}, - {file = "websockets-13.1-cp39-cp39-win_amd64.whl", hash = "sha256:7c65ffa900e7cc958cd088b9a9157a8141c991f8c53d11087e6fb7277a03f81d"}, - {file = "websockets-13.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5dd6da9bec02735931fccec99d97c29f47cc61f644264eb995ad6c0c27667238"}, - {file = "websockets-13.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:2510c09d8e8df777177ee3d40cd35450dc169a81e747455cc4197e63f7e7bfe5"}, - {file = "websockets-13.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1c3cf67185543730888b20682fb186fc8d0fa6f07ccc3ef4390831ab4b388d9"}, - {file = "websockets-13.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bcc03c8b72267e97b49149e4863d57c2d77f13fae12066622dc78fe322490fe6"}, - {file = "websockets-13.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:004280a140f220c812e65f36944a9ca92d766b6cc4560be652a0a3883a79ed8a"}, - {file = "websockets-13.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e2620453c075abeb0daa949a292e19f56de518988e079c36478bacf9546ced23"}, - {file = "websockets-13.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9156c45750b37337f7b0b00e6248991a047be4aa44554c9886fe6bdd605aab3b"}, - {file = "websockets-13.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:80c421e07973a89fbdd93e6f2003c17d20b69010458d3a8e37fb47874bd67d51"}, - {file = "websockets-13.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82d0ba76371769d6a4e56f7e83bb8e81846d17a6190971e38b5de108bde9b0d7"}, - {file = "websockets-13.1-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e9875a0143f07d74dc5e1ded1c4581f0d9f7ab86c78994e2ed9e95050073c94d"}, - {file = "websockets-13.1-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a11e38ad8922c7961447f35c7b17bffa15de4d17c70abd07bfbe12d6faa3e027"}, - {file = "websockets-13.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:4059f790b6ae8768471cddb65d3c4fe4792b0ab48e154c9f0a04cefaabcd5978"}, - {file = "websockets-13.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:25c35bf84bf7c7369d247f0b8cfa157f989862c49104c5cf85cb5436a641d93e"}, - {file = "websockets-13.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:83f91d8a9bb404b8c2c41a707ac7f7f75b9442a0a876df295de27251a856ad09"}, - {file = "websockets-13.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a43cfdcddd07f4ca2b1afb459824dd3c6d53a51410636a2c7fc97b9a8cf4842"}, - {file = "websockets-13.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48a2ef1381632a2f0cb4efeff34efa97901c9fbc118e01951ad7cfc10601a9bb"}, - {file = "websockets-13.1-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:459bf774c754c35dbb487360b12c5727adab887f1622b8aed5755880a21c4a20"}, - {file = "websockets-13.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:95858ca14a9f6fa8413d29e0a585b31b278388aa775b8a81fa24830123874678"}, - {file = "websockets-13.1-py3-none-any.whl", hash = "sha256:a9a396a6ad26130cdae92ae10c36af09d9bfe6cafe69670fd3b6da9b07b4044f"}, - {file = "websockets-13.1.tar.gz", hash = "sha256:a3b3366087c1bc0a2795111edcadddb8b3b59509d5db5d7ea3fdd69f954a8878"}, + {file = "wrapt-1.17.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2a0c23b8319848426f305f9cb0c98a6e32ee68a36264f45948ccf8e7d2b941f8"}, + {file = "wrapt-1.17.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1ca5f060e205f72bec57faae5bd817a1560fcfc4af03f414b08fa29106b7e2d"}, + {file = "wrapt-1.17.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e185ec6060e301a7e5f8461c86fb3640a7beb1a0f0208ffde7a65ec4074931df"}, + {file = "wrapt-1.17.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb90765dd91aed05b53cd7a87bd7f5c188fcd95960914bae0d32c5e7f899719d"}, + {file = "wrapt-1.17.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:879591c2b5ab0a7184258274c42a126b74a2c3d5a329df16d69f9cee07bba6ea"}, + {file = "wrapt-1.17.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fce6fee67c318fdfb7f285c29a82d84782ae2579c0e1b385b7f36c6e8074fffb"}, + {file = "wrapt-1.17.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0698d3a86f68abc894d537887b9bbf84d29bcfbc759e23f4644be27acf6da301"}, + {file = "wrapt-1.17.0-cp310-cp310-win32.whl", hash = "sha256:69d093792dc34a9c4c8a70e4973a3361c7a7578e9cd86961b2bbf38ca71e4e22"}, + {file = "wrapt-1.17.0-cp310-cp310-win_amd64.whl", hash = "sha256:f28b29dc158ca5d6ac396c8e0a2ef45c4e97bb7e65522bfc04c989e6fe814575"}, + {file = "wrapt-1.17.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:74bf625b1b4caaa7bad51d9003f8b07a468a704e0644a700e936c357c17dd45a"}, + {file = "wrapt-1.17.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f2a28eb35cf99d5f5bd12f5dd44a0f41d206db226535b37b0c60e9da162c3ed"}, + {file = "wrapt-1.17.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:81b1289e99cf4bad07c23393ab447e5e96db0ab50974a280f7954b071d41b489"}, + {file = "wrapt-1.17.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f2939cd4a2a52ca32bc0b359015718472d7f6de870760342e7ba295be9ebaf9"}, + {file = "wrapt-1.17.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6a9653131bda68a1f029c52157fd81e11f07d485df55410401f745007bd6d339"}, + {file = "wrapt-1.17.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4e4b4385363de9052dac1a67bfb535c376f3d19c238b5f36bddc95efae15e12d"}, + {file = "wrapt-1.17.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bdf62d25234290db1837875d4dceb2151e4ea7f9fff2ed41c0fde23ed542eb5b"}, + {file = "wrapt-1.17.0-cp311-cp311-win32.whl", hash = "sha256:5d8fd17635b262448ab8f99230fe4dac991af1dabdbb92f7a70a6afac8a7e346"}, + {file = "wrapt-1.17.0-cp311-cp311-win_amd64.whl", hash = "sha256:92a3d214d5e53cb1db8b015f30d544bc9d3f7179a05feb8f16df713cecc2620a"}, + {file = "wrapt-1.17.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:89fc28495896097622c3fc238915c79365dd0ede02f9a82ce436b13bd0ab7569"}, + {file = "wrapt-1.17.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:875d240fdbdbe9e11f9831901fb8719da0bd4e6131f83aa9f69b96d18fae7504"}, + {file = "wrapt-1.17.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5ed16d95fd142e9c72b6c10b06514ad30e846a0d0917ab406186541fe68b451"}, + {file = "wrapt-1.17.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18b956061b8db634120b58f668592a772e87e2e78bc1f6a906cfcaa0cc7991c1"}, + {file = "wrapt-1.17.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:daba396199399ccabafbfc509037ac635a6bc18510ad1add8fd16d4739cdd106"}, + {file = "wrapt-1.17.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4d63f4d446e10ad19ed01188d6c1e1bb134cde8c18b0aa2acfd973d41fcc5ada"}, + {file = "wrapt-1.17.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8a5e7cc39a45fc430af1aefc4d77ee6bad72c5bcdb1322cfde852c15192b8bd4"}, + {file = "wrapt-1.17.0-cp312-cp312-win32.whl", hash = "sha256:0a0a1a1ec28b641f2a3a2c35cbe86c00051c04fffcfcc577ffcdd707df3f8635"}, + {file = "wrapt-1.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:3c34f6896a01b84bab196f7119770fd8466c8ae3dfa73c59c0bb281e7b588ce7"}, + {file = "wrapt-1.17.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:714c12485aa52efbc0fc0ade1e9ab3a70343db82627f90f2ecbc898fdf0bb181"}, + {file = "wrapt-1.17.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da427d311782324a376cacb47c1a4adc43f99fd9d996ffc1b3e8529c4074d393"}, + {file = "wrapt-1.17.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba1739fb38441a27a676f4de4123d3e858e494fac05868b7a281c0a383c098f4"}, + {file = "wrapt-1.17.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e711fc1acc7468463bc084d1b68561e40d1eaa135d8c509a65dd534403d83d7b"}, + {file = "wrapt-1.17.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:140ea00c87fafc42739bd74a94a5a9003f8e72c27c47cd4f61d8e05e6dec8721"}, + {file = "wrapt-1.17.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:73a96fd11d2b2e77d623a7f26e004cc31f131a365add1ce1ce9a19e55a1eef90"}, + {file = "wrapt-1.17.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0b48554952f0f387984da81ccfa73b62e52817a4386d070c75e4db7d43a28c4a"}, + {file = "wrapt-1.17.0-cp313-cp313-win32.whl", hash = "sha256:498fec8da10e3e62edd1e7368f4b24aa362ac0ad931e678332d1b209aec93045"}, + {file = "wrapt-1.17.0-cp313-cp313-win_amd64.whl", hash = "sha256:fd136bb85f4568fffca995bd3c8d52080b1e5b225dbf1c2b17b66b4c5fa02838"}, + {file = "wrapt-1.17.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:17fcf043d0b4724858f25b8826c36e08f9fb2e475410bece0ec44a22d533da9b"}, + {file = "wrapt-1.17.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4a557d97f12813dc5e18dad9fa765ae44ddd56a672bb5de4825527c847d6379"}, + {file = "wrapt-1.17.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0229b247b0fc7dee0d36176cbb79dbaf2a9eb7ecc50ec3121f40ef443155fb1d"}, + {file = "wrapt-1.17.0-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8425cfce27b8b20c9b89d77fb50e368d8306a90bf2b6eef2cdf5cd5083adf83f"}, + {file = "wrapt-1.17.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9c900108df470060174108012de06d45f514aa4ec21a191e7ab42988ff42a86c"}, + {file = "wrapt-1.17.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:4e547b447073fc0dbfcbff15154c1be8823d10dab4ad401bdb1575e3fdedff1b"}, + {file = "wrapt-1.17.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:914f66f3b6fc7b915d46c1cc424bc2441841083de01b90f9e81109c9759e43ab"}, + {file = "wrapt-1.17.0-cp313-cp313t-win32.whl", hash = "sha256:a4192b45dff127c7d69b3bdfb4d3e47b64179a0b9900b6351859f3001397dabf"}, + {file = "wrapt-1.17.0-cp313-cp313t-win_amd64.whl", hash = "sha256:4f643df3d4419ea3f856c5c3f40fec1d65ea2e89ec812c83f7767c8730f9827a"}, + {file = "wrapt-1.17.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:69c40d4655e078ede067a7095544bcec5a963566e17503e75a3a3e0fe2803b13"}, + {file = "wrapt-1.17.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f495b6754358979379f84534f8dd7a43ff8cff2558dcdea4a148a6e713a758f"}, + {file = "wrapt-1.17.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:baa7ef4e0886a6f482e00d1d5bcd37c201b383f1d314643dfb0367169f94f04c"}, + {file = "wrapt-1.17.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8fc931382e56627ec4acb01e09ce66e5c03c384ca52606111cee50d931a342d"}, + {file = "wrapt-1.17.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:8f8909cdb9f1b237786c09a810e24ee5e15ef17019f7cecb207ce205b9b5fcce"}, + {file = "wrapt-1.17.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:ad47b095f0bdc5585bced35bd088cbfe4177236c7df9984b3cc46b391cc60627"}, + {file = "wrapt-1.17.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:948a9bd0fb2c5120457b07e59c8d7210cbc8703243225dbd78f4dfc13c8d2d1f"}, + {file = "wrapt-1.17.0-cp38-cp38-win32.whl", hash = "sha256:5ae271862b2142f4bc687bdbfcc942e2473a89999a54231aa1c2c676e28f29ea"}, + {file = "wrapt-1.17.0-cp38-cp38-win_amd64.whl", hash = "sha256:f335579a1b485c834849e9075191c9898e0731af45705c2ebf70e0cd5d58beed"}, + {file = "wrapt-1.17.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d751300b94e35b6016d4b1e7d0e7bbc3b5e1751e2405ef908316c2a9024008a1"}, + {file = "wrapt-1.17.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7264cbb4a18dc4acfd73b63e4bcfec9c9802614572025bdd44d0721983fc1d9c"}, + {file = "wrapt-1.17.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:33539c6f5b96cf0b1105a0ff4cf5db9332e773bb521cc804a90e58dc49b10578"}, + {file = "wrapt-1.17.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c30970bdee1cad6a8da2044febd824ef6dc4cc0b19e39af3085c763fdec7de33"}, + {file = "wrapt-1.17.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:bc7f729a72b16ee21795a943f85c6244971724819819a41ddbaeb691b2dd85ad"}, + {file = "wrapt-1.17.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:6ff02a91c4fc9b6a94e1c9c20f62ea06a7e375f42fe57587f004d1078ac86ca9"}, + {file = "wrapt-1.17.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2dfb7cff84e72e7bf975b06b4989477873dcf160b2fd89959c629535df53d4e0"}, + {file = "wrapt-1.17.0-cp39-cp39-win32.whl", hash = "sha256:2399408ac33ffd5b200480ee858baa58d77dd30e0dd0cab6a8a9547135f30a88"}, + {file = "wrapt-1.17.0-cp39-cp39-win_amd64.whl", hash = "sha256:4f763a29ee6a20c529496a20a7bcb16a73de27f5da6a843249c7047daf135977"}, + {file = "wrapt-1.17.0-py3-none-any.whl", hash = "sha256:d2c63b93548eda58abf5188e505ffed0229bf675f7c3090f8e36ad55b8cbc371"}, + {file = "wrapt-1.17.0.tar.gz", hash = "sha256:16187aa2317c731170a88ef35e8937ae0f533c402872c1ee5e6d079fcf320801"}, ] [[package]] name = "xformers" -version = "0.0.27.post2" +version = "0.0.28.post3" description = "XFormers: A collection of composable Transformer building blocks." optional = true -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ - {file = "xformers-0.0.27.post2-cp310-cp310-manylinux2014_x86_64.whl", hash = "sha256:7d4bd4876559fdc3cccbcaea717d3a28ca7f2fb09ab84cac3d859449ba247830"}, - {file = "xformers-0.0.27.post2-cp310-cp310-win_amd64.whl", hash = "sha256:cd476c23fea18aa7298753c031c4827a544d919ee542cec771c01bc824d0127d"}, - {file = "xformers-0.0.27.post2-cp311-cp311-manylinux2014_x86_64.whl", hash = "sha256:373a1c5d65dab89ea058d17a37c07e5da5ff3b9bebf61425e26a0a5293b0d4a9"}, - {file = "xformers-0.0.27.post2-cp311-cp311-win_amd64.whl", hash = "sha256:7a26febb550b642c7da9864b6a46ccb89461571d27cfef54f7a252f03060d07c"}, - {file = "xformers-0.0.27.post2-cp312-cp312-manylinux2014_x86_64.whl", hash = "sha256:3500f5ff3614aa18762fddc945e4057e7225e23f35986c6666cc49d6cb1c8ee7"}, - {file = "xformers-0.0.27.post2-cp312-cp312-win_amd64.whl", hash = "sha256:0454ba745babf43500320b1d171ef4e44dec38f279fab9df1710ca5ce44a749c"}, - {file = "xformers-0.0.27.post2-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:ab39ba95a7529c412c3d81e7817e2b42c31980b72e1937055fd4ba4503a34b3d"}, - {file = "xformers-0.0.27.post2-cp38-cp38-win_amd64.whl", hash = "sha256:5a83aa75a7208c359b2755af318e97e70855dbae6cdf998227f6dee220a56203"}, - {file = "xformers-0.0.27.post2-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:f9fca3af140a8649678e946d55e32c8be59917c4b7d0e96437c057a4bbe30e11"}, - {file = "xformers-0.0.27.post2-cp39-cp39-win_amd64.whl", hash = "sha256:d555ef6d3722941cd785cf4b7c6039b06e8a0ca1360ff661200cf043393ca5a2"}, - {file = "xformers-0.0.27.post2.tar.gz", hash = "sha256:5c3bcefabf29532cac7eb7556fb684be209698955b004aac069742e49373bdb3"}, + {file = "xformers-0.0.28.post3-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:648483325366fb3c6a42246f99646101d3cfd678725b3ffc50a4708a222ae973"}, + {file = "xformers-0.0.28.post3-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:825563e129f6b22885c2a91e464ff617382f4d78876cc92d96ff618e875aaee3"}, + {file = "xformers-0.0.28.post3-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:c550f72bb4e55b67bd847e9272b7f41d27ac82b6b99f35a710a1292f2f218a3a"}, + {file = "xformers-0.0.28.post3-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:e5ae1269ceea51c0d3a0a03ebe729aaae8e29dc4ca5e0f5a3bcd482045905811"}, + {file = "xformers-0.0.28.post3.tar.gz", hash = "sha256:c7a2392c874dfd8f38b73e14492baf048a4f50f77ddf522bfcf6ebf5ee84d567"}, ] [package.dependencies] numpy = "*" -torch = "2.4.0" +torch = "2.5.1" [[package]] -name = "xxhash" -version = "3.5.0" -description = "Python binding for xxHash" +name = "xgrammar" +version = "0.1.9" +description = "Efficient, Flexible and Portable Structured Generation" optional = true -python-versions = ">=3.7" +python-versions = "<4,>=3.8" files = [ - {file = "xxhash-3.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ece616532c499ee9afbb83078b1b952beffef121d989841f7f4b3dc5ac0fd212"}, - {file = "xxhash-3.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3171f693dbc2cef6477054a665dc255d996646b4023fe56cb4db80e26f4cc520"}, - {file = "xxhash-3.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c5d3e570ef46adaf93fc81b44aca6002b5a4d8ca11bd0580c07eac537f36680"}, - {file = "xxhash-3.5.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7cb29a034301e2982df8b1fe6328a84f4b676106a13e9135a0d7e0c3e9f806da"}, - {file = "xxhash-3.5.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5d0d307d27099bb0cbeea7260eb39ed4fdb99c5542e21e94bb6fd29e49c57a23"}, - {file = "xxhash-3.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0342aafd421795d740e514bc9858ebddfc705a75a8c5046ac56d85fe97bf196"}, - {file = "xxhash-3.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3dbbd9892c5ebffeca1ed620cf0ade13eb55a0d8c84e0751a6653adc6ac40d0c"}, - {file = "xxhash-3.5.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4cc2d67fdb4d057730c75a64c5923abfa17775ae234a71b0200346bfb0a7f482"}, - {file = "xxhash-3.5.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:ec28adb204b759306a3d64358a5e5c07d7b1dd0ccbce04aa76cb9377b7b70296"}, - {file = "xxhash-3.5.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:1328f6d8cca2b86acb14104e381225a3d7b42c92c4b86ceae814e5c400dbb415"}, - {file = "xxhash-3.5.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8d47ebd9f5d9607fd039c1fbf4994e3b071ea23eff42f4ecef246ab2b7334198"}, - {file = "xxhash-3.5.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b96d559e0fcddd3343c510a0fe2b127fbff16bf346dd76280b82292567523442"}, - {file = "xxhash-3.5.0-cp310-cp310-win32.whl", hash = "sha256:61c722ed8d49ac9bc26c7071eeaa1f6ff24053d553146d5df031802deffd03da"}, - {file = "xxhash-3.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:9bed5144c6923cc902cd14bb8963f2d5e034def4486ab0bbe1f58f03f042f9a9"}, - {file = "xxhash-3.5.0-cp310-cp310-win_arm64.whl", hash = "sha256:893074d651cf25c1cc14e3bea4fceefd67f2921b1bb8e40fcfeba56820de80c6"}, - {file = "xxhash-3.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:02c2e816896dc6f85922ced60097bcf6f008dedfc5073dcba32f9c8dd786f3c1"}, - {file = "xxhash-3.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6027dcd885e21581e46d3c7f682cfb2b870942feeed58a21c29583512c3f09f8"}, - {file = "xxhash-3.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1308fa542bbdbf2fa85e9e66b1077eea3a88bef38ee8a06270b4298a7a62a166"}, - {file = "xxhash-3.5.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c28b2fdcee797e1c1961cd3bcd3d545cab22ad202c846235197935e1df2f8ef7"}, - {file = "xxhash-3.5.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:924361811732ddad75ff23e90efd9ccfda4f664132feecb90895bade6a1b4623"}, - {file = "xxhash-3.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89997aa1c4b6a5b1e5b588979d1da048a3c6f15e55c11d117a56b75c84531f5a"}, - {file = "xxhash-3.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:685c4f4e8c59837de103344eb1c8a3851f670309eb5c361f746805c5471b8c88"}, - {file = "xxhash-3.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dbd2ecfbfee70bc1a4acb7461fa6af7748ec2ab08ac0fa298f281c51518f982c"}, - {file = "xxhash-3.5.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:25b5a51dc3dfb20a10833c8eee25903fd2e14059e9afcd329c9da20609a307b2"}, - {file = "xxhash-3.5.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a8fb786fb754ef6ff8c120cb96629fb518f8eb5a61a16aac3a979a9dbd40a084"}, - {file = "xxhash-3.5.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:a905ad00ad1e1c34fe4e9d7c1d949ab09c6fa90c919860c1534ff479f40fd12d"}, - {file = "xxhash-3.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:963be41bcd49f53af6d795f65c0da9b4cc518c0dd9c47145c98f61cb464f4839"}, - {file = "xxhash-3.5.0-cp311-cp311-win32.whl", hash = "sha256:109b436096d0a2dd039c355fa3414160ec4d843dfecc64a14077332a00aeb7da"}, - {file = "xxhash-3.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:b702f806693201ad6c0a05ddbbe4c8f359626d0b3305f766077d51388a6bac58"}, - {file = "xxhash-3.5.0-cp311-cp311-win_arm64.whl", hash = "sha256:c4dcb4120d0cc3cc448624147dba64e9021b278c63e34a38789b688fd0da9bf3"}, - {file = "xxhash-3.5.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:14470ace8bd3b5d51318782cd94e6f94431974f16cb3b8dc15d52f3b69df8e00"}, - {file = "xxhash-3.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:59aa1203de1cb96dbeab595ded0ad0c0056bb2245ae11fac11c0ceea861382b9"}, - {file = "xxhash-3.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:08424f6648526076e28fae6ea2806c0a7d504b9ef05ae61d196d571e5c879c84"}, - {file = "xxhash-3.5.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:61a1ff00674879725b194695e17f23d3248998b843eb5e933007ca743310f793"}, - {file = "xxhash-3.5.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2f2c61bee5844d41c3eb015ac652a0229e901074951ae48581d58bfb2ba01be"}, - {file = "xxhash-3.5.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d32a592cac88d18cc09a89172e1c32d7f2a6e516c3dfde1b9adb90ab5df54a6"}, - {file = "xxhash-3.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:70dabf941dede727cca579e8c205e61121afc9b28516752fd65724be1355cc90"}, - {file = "xxhash-3.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e5d0ddaca65ecca9c10dcf01730165fd858533d0be84c75c327487c37a906a27"}, - {file = "xxhash-3.5.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3e5b5e16c5a480fe5f59f56c30abdeba09ffd75da8d13f6b9b6fd224d0b4d0a2"}, - {file = "xxhash-3.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:149b7914451eb154b3dfaa721315117ea1dac2cc55a01bfbd4df7c68c5dd683d"}, - {file = "xxhash-3.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:eade977f5c96c677035ff39c56ac74d851b1cca7d607ab3d8f23c6b859379cab"}, - {file = "xxhash-3.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fa9f547bd98f5553d03160967866a71056a60960be00356a15ecc44efb40ba8e"}, - {file = "xxhash-3.5.0-cp312-cp312-win32.whl", hash = "sha256:f7b58d1fd3551b8c80a971199543379be1cee3d0d409e1f6d8b01c1a2eebf1f8"}, - {file = "xxhash-3.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:fa0cafd3a2af231b4e113fba24a65d7922af91aeb23774a8b78228e6cd785e3e"}, - {file = "xxhash-3.5.0-cp312-cp312-win_arm64.whl", hash = "sha256:586886c7e89cb9828bcd8a5686b12e161368e0064d040e225e72607b43858ba2"}, - {file = "xxhash-3.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:37889a0d13b0b7d739cfc128b1c902f04e32de17b33d74b637ad42f1c55101f6"}, - {file = "xxhash-3.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:97a662338797c660178e682f3bc180277b9569a59abfb5925e8620fba00b9fc5"}, - {file = "xxhash-3.5.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f85e0108d51092bdda90672476c7d909c04ada6923c14ff9d913c4f7dc8a3bc"}, - {file = "xxhash-3.5.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd2fd827b0ba763ac919440042302315c564fdb797294d86e8cdd4578e3bc7f3"}, - {file = "xxhash-3.5.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:82085c2abec437abebf457c1d12fccb30cc8b3774a0814872511f0f0562c768c"}, - {file = "xxhash-3.5.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07fda5de378626e502b42b311b049848c2ef38784d0d67b6f30bb5008642f8eb"}, - {file = "xxhash-3.5.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c279f0d2b34ef15f922b77966640ade58b4ccdfef1c4d94b20f2a364617a493f"}, - {file = "xxhash-3.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:89e66ceed67b213dec5a773e2f7a9e8c58f64daeb38c7859d8815d2c89f39ad7"}, - {file = "xxhash-3.5.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:bcd51708a633410737111e998ceb3b45d3dbc98c0931f743d9bb0a209033a326"}, - {file = "xxhash-3.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3ff2c0a34eae7df88c868be53a8dd56fbdf592109e21d4bfa092a27b0bf4a7bf"}, - {file = "xxhash-3.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:4e28503dccc7d32e0b9817aa0cbfc1f45f563b2c995b7a66c4c8a0d232e840c7"}, - {file = "xxhash-3.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a6c50017518329ed65a9e4829154626f008916d36295b6a3ba336e2458824c8c"}, - {file = "xxhash-3.5.0-cp313-cp313-win32.whl", hash = "sha256:53a068fe70301ec30d868ece566ac90d873e3bb059cf83c32e76012c889b8637"}, - {file = "xxhash-3.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:80babcc30e7a1a484eab952d76a4f4673ff601f54d5142c26826502740e70b43"}, - {file = "xxhash-3.5.0-cp313-cp313-win_arm64.whl", hash = "sha256:4811336f1ce11cac89dcbd18f3a25c527c16311709a89313c3acaf771def2d4b"}, - {file = "xxhash-3.5.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6e5f70f6dca1d3b09bccb7daf4e087075ff776e3da9ac870f86ca316736bb4aa"}, - {file = "xxhash-3.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e76e83efc7b443052dd1e585a76201e40b3411fe3da7af4fe434ec51b2f163b"}, - {file = "xxhash-3.5.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:33eac61d0796ca0591f94548dcfe37bb193671e0c9bcf065789b5792f2eda644"}, - {file = "xxhash-3.5.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ec70a89be933ea49222fafc3999987d7899fc676f688dd12252509434636622"}, - {file = "xxhash-3.5.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86b8e7f703ec6ff4f351cfdb9f428955859537125904aa8c963604f2e9d3e7"}, - {file = "xxhash-3.5.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0adfbd36003d9f86c8c97110039f7539b379f28656a04097e7434d3eaf9aa131"}, - {file = "xxhash-3.5.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:63107013578c8a730419adc05608756c3fa640bdc6abe806c3123a49fb829f43"}, - {file = "xxhash-3.5.0-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:683b94dbd1ca67557850b86423318a2e323511648f9f3f7b1840408a02b9a48c"}, - {file = "xxhash-3.5.0-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:5d2a01dcce81789cf4b12d478b5464632204f4c834dc2d064902ee27d2d1f0ee"}, - {file = "xxhash-3.5.0-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:a9d360a792cbcce2fe7b66b8d51274ec297c53cbc423401480e53b26161a290d"}, - {file = "xxhash-3.5.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:f0b48edbebea1b7421a9c687c304f7b44d0677c46498a046079d445454504737"}, - {file = "xxhash-3.5.0-cp37-cp37m-win32.whl", hash = "sha256:7ccb800c9418e438b44b060a32adeb8393764da7441eb52aa2aa195448935306"}, - {file = "xxhash-3.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:c3bc7bf8cb8806f8d1c9bf149c18708cb1c406520097d6b0a73977460ea03602"}, - {file = "xxhash-3.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:74752ecaa544657d88b1d1c94ae68031e364a4d47005a90288f3bab3da3c970f"}, - {file = "xxhash-3.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:dee1316133c9b463aa81aca676bc506d3f80d8f65aeb0bba2b78d0b30c51d7bd"}, - {file = "xxhash-3.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:602d339548d35a8579c6b013339fb34aee2df9b4e105f985443d2860e4d7ffaa"}, - {file = "xxhash-3.5.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:695735deeddfb35da1677dbc16a083445360e37ff46d8ac5c6fcd64917ff9ade"}, - {file = "xxhash-3.5.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1030a39ba01b0c519b1a82f80e8802630d16ab95dc3f2b2386a0b5c8ed5cbb10"}, - {file = "xxhash-3.5.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5bc08f33c4966f4eb6590d6ff3ceae76151ad744576b5fc6c4ba8edd459fdec"}, - {file = "xxhash-3.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:160e0c19ee500482ddfb5d5570a0415f565d8ae2b3fd69c5dcfce8a58107b1c3"}, - {file = "xxhash-3.5.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:f1abffa122452481a61c3551ab3c89d72238e279e517705b8b03847b1d93d738"}, - {file = "xxhash-3.5.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:d5e9db7ef3ecbfc0b4733579cea45713a76852b002cf605420b12ef3ef1ec148"}, - {file = "xxhash-3.5.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:23241ff6423378a731d84864bf923a41649dc67b144debd1077f02e6249a0d54"}, - {file = "xxhash-3.5.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:82b833d5563fefd6fceafb1aed2f3f3ebe19f84760fdd289f8b926731c2e6e91"}, - {file = "xxhash-3.5.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0a80ad0ffd78bef9509eee27b4a29e56f5414b87fb01a888353e3d5bda7038bd"}, - {file = "xxhash-3.5.0-cp38-cp38-win32.whl", hash = "sha256:50ac2184ffb1b999e11e27c7e3e70cc1139047e7ebc1aa95ed12f4269abe98d4"}, - {file = "xxhash-3.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:392f52ebbb932db566973693de48f15ce787cabd15cf6334e855ed22ea0be5b3"}, - {file = "xxhash-3.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bfc8cdd7f33d57f0468b0614ae634cc38ab9202c6957a60e31d285a71ebe0301"}, - {file = "xxhash-3.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e0c48b6300cd0b0106bf49169c3e0536408dfbeb1ccb53180068a18b03c662ab"}, - {file = "xxhash-3.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe1a92cfbaa0a1253e339ccec42dbe6db262615e52df591b68726ab10338003f"}, - {file = "xxhash-3.5.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:33513d6cc3ed3b559134fb307aae9bdd94d7e7c02907b37896a6c45ff9ce51bd"}, - {file = "xxhash-3.5.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eefc37f6138f522e771ac6db71a6d4838ec7933939676f3753eafd7d3f4c40bc"}, - {file = "xxhash-3.5.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a606c8070ada8aa2a88e181773fa1ef17ba65ce5dd168b9d08038e2a61b33754"}, - {file = "xxhash-3.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:42eca420c8fa072cc1dd62597635d140e78e384a79bb4944f825fbef8bfeeef6"}, - {file = "xxhash-3.5.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:604253b2143e13218ff1ef0b59ce67f18b8bd1c4205d2ffda22b09b426386898"}, - {file = "xxhash-3.5.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:6e93a5ad22f434d7876665444a97e713a8f60b5b1a3521e8df11b98309bff833"}, - {file = "xxhash-3.5.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:7a46e1d6d2817ba8024de44c4fd79913a90e5f7265434cef97026215b7d30df6"}, - {file = "xxhash-3.5.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:30eb2efe6503c379b7ab99c81ba4a779748e3830241f032ab46bd182bf5873af"}, - {file = "xxhash-3.5.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c8aa771ff2c13dd9cda8166d685d7333d389fae30a4d2bb39d63ab5775de8606"}, - {file = "xxhash-3.5.0-cp39-cp39-win32.whl", hash = "sha256:5ed9ebc46f24cf91034544b26b131241b699edbfc99ec5e7f8f3d02d6eb7fba4"}, - {file = "xxhash-3.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:220f3f896c6b8d0316f63f16c077d52c412619e475f9372333474ee15133a558"}, - {file = "xxhash-3.5.0-cp39-cp39-win_arm64.whl", hash = "sha256:a7b1d8315d9b5e9f89eb2933b73afae6ec9597a258d52190944437158b49d38e"}, - {file = "xxhash-3.5.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:2014c5b3ff15e64feecb6b713af12093f75b7926049e26a580e94dcad3c73d8c"}, - {file = "xxhash-3.5.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fab81ef75003eda96239a23eda4e4543cedc22e34c373edcaf744e721a163986"}, - {file = "xxhash-3.5.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e2febf914ace002132aa09169cc572e0d8959d0f305f93d5828c4836f9bc5a6"}, - {file = "xxhash-3.5.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5d3a10609c51da2a1c0ea0293fc3968ca0a18bd73838455b5bca3069d7f8e32b"}, - {file = "xxhash-3.5.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5a74f23335b9689b66eb6dbe2a931a88fcd7a4c2cc4b1cb0edba8ce381c7a1da"}, - {file = "xxhash-3.5.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2b4154c00eb22e4d543f472cfca430e7962a0f1d0f3778334f2e08a7ba59363c"}, - {file = "xxhash-3.5.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d30bbc1644f726b825b3278764240f449d75f1a8bdda892e641d4a688b1494ae"}, - {file = "xxhash-3.5.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fa0b72f2423e2aa53077e54a61c28e181d23effeaafd73fcb9c494e60930c8e"}, - {file = "xxhash-3.5.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:13de2b76c1835399b2e419a296d5b38dc4855385d9e96916299170085ef72f57"}, - {file = "xxhash-3.5.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:0691bfcc4f9c656bcb96cc5db94b4d75980b9d5589f2e59de790091028580837"}, - {file = "xxhash-3.5.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:297595fe6138d4da2c8ce9e72a04d73e58725bb60f3a19048bc96ab2ff31c692"}, - {file = "xxhash-3.5.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc1276d369452040cbb943300dc8abeedab14245ea44056a2943183822513a18"}, - {file = "xxhash-3.5.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2061188a1ba352fc699c82bff722f4baacb4b4b8b2f0c745d2001e56d0dfb514"}, - {file = "xxhash-3.5.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38c384c434021e4f62b8d9ba0bc9467e14d394893077e2c66d826243025e1f81"}, - {file = "xxhash-3.5.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:e6a4dd644d72ab316b580a1c120b375890e4c52ec392d4aef3c63361ec4d77d1"}, - {file = "xxhash-3.5.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:531af8845aaadcadf951b7e0c1345c6b9c68a990eeb74ff9acd8501a0ad6a1c9"}, - {file = "xxhash-3.5.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ce379bcaa9fcc00f19affa7773084dd09f5b59947b3fb47a1ceb0179f91aaa1"}, - {file = "xxhash-3.5.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd1b2281d01723f076df3c8188f43f2472248a6b63118b036e641243656b1b0f"}, - {file = "xxhash-3.5.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9c770750cc80e8694492244bca7251385188bc5597b6a39d98a9f30e8da984e0"}, - {file = "xxhash-3.5.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:b150b8467852e1bd844387459aa6fbe11d7f38b56e901f9f3b3e6aba0d660240"}, - {file = "xxhash-3.5.0.tar.gz", hash = "sha256:84f2caddf951c9cbf8dc2e22a89d4ccf5d86391ac6418fe81e3c67d0cf60b45f"}, + {file = "xgrammar-0.1.9-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d8dd6955267b53604a8888db56166e1a1a62dc30082fb09ae97d0a680fd46527"}, + {file = "xgrammar-0.1.9-cp310-cp310-win_amd64.whl", hash = "sha256:938493a26204d8d7900cd3805cea97c5a4e08c3b3c9b7bea5e53820280b41af8"}, + {file = "xgrammar-0.1.9-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:344f02ed0f550d7fcb3ea837f8369faf357c44051974c86b7363517412829700"}, + {file = "xgrammar-0.1.9-cp311-cp311-win_amd64.whl", hash = "sha256:63893935796af60bdb5afd2b656baddbaf5dfa9d42a71fe347eda33b4584cb51"}, + {file = "xgrammar-0.1.9-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:473a6dc30cba150faa2f1102f576deace98342a293ceb2757a7d5ddd240f6260"}, + {file = "xgrammar-0.1.9-cp312-cp312-win_amd64.whl", hash = "sha256:d3ba9ec2c18fbb94bdee36e66e026770e8f47398127b0192b36b9c80b6d2b868"}, + {file = "xgrammar-0.1.9-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4e99200b16462d71850b41cf77b5dff0157e1c7615737d20ddc6b1e001085f2a"}, + {file = "xgrammar-0.1.9-cp39-cp39-win_amd64.whl", hash = "sha256:c1a7fd1bce1edd6e2727ab426e11878b0f9ff117dae8c02ea03835c5138d943a"}, ] +[package.dependencies] +pybind11 = "*" +pydantic = "*" +pytest = "*" +sentencepiece = "*" +tiktoken = "*" +torch = "*" +transformers = "*" + [[package]] name = "yarl" -version = "1.16.0" +version = "1.18.3" description = "Yet another URL library" optional = true python-versions = ">=3.9" files = [ - {file = "yarl-1.16.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:32468f41242d72b87ab793a86d92f885355bcf35b3355aa650bfa846a5c60058"}, - {file = "yarl-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:234f3a3032b505b90e65b5bc6652c2329ea7ea8855d8de61e1642b74b4ee65d2"}, - {file = "yarl-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8a0296040e5cddf074c7f5af4a60f3fc42c0237440df7bcf5183be5f6c802ed5"}, - {file = "yarl-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:de6c14dd7c7c0badba48157474ea1f03ebee991530ba742d381b28d4f314d6f3"}, - {file = "yarl-1.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b140e532fe0266003c936d017c1ac301e72ee4a3fd51784574c05f53718a55d8"}, - {file = "yarl-1.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:019f5d58093402aa8f6661e60fd82a28746ad6d156f6c5336a70a39bd7b162b9"}, - {file = "yarl-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c42998fd1cbeb53cd985bff0e4bc25fbe55fd6eb3a545a724c1012d69d5ec84"}, - {file = "yarl-1.16.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c7c30fb38c300fe8140df30a046a01769105e4cf4282567a29b5cdb635b66c4"}, - {file = "yarl-1.16.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e49e0fd86c295e743fd5be69b8b0712f70a686bc79a16e5268386c2defacaade"}, - {file = "yarl-1.16.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:b9ca7b9147eb1365c8bab03c003baa1300599575effad765e0b07dd3501ea9af"}, - {file = "yarl-1.16.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:27e11db3f1e6a51081a981509f75617b09810529de508a181319193d320bc5c7"}, - {file = "yarl-1.16.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8994c42f4ca25df5380ddf59f315c518c81df6a68fed5bb0c159c6cb6b92f120"}, - {file = "yarl-1.16.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:542fa8e09a581bcdcbb30607c7224beff3fdfb598c798ccd28a8184ffc18b7eb"}, - {file = "yarl-1.16.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2bd6a51010c7284d191b79d3b56e51a87d8e1c03b0902362945f15c3d50ed46b"}, - {file = "yarl-1.16.0-cp310-cp310-win32.whl", hash = "sha256:178ccb856e265174a79f59721031060f885aca428983e75c06f78aa24b91d929"}, - {file = "yarl-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:fe8bba2545427418efc1929c5c42852bdb4143eb8d0a46b09de88d1fe99258e7"}, - {file = "yarl-1.16.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d8643975a0080f361639787415a038bfc32d29208a4bf6b783ab3075a20b1ef3"}, - {file = "yarl-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:676d96bafc8c2d0039cea0cd3fd44cee7aa88b8185551a2bb93354668e8315c2"}, - {file = "yarl-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d9525f03269e64310416dbe6c68d3b23e5d34aaa8f47193a1c45ac568cecbc49"}, - {file = "yarl-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b37d5ec034e668b22cf0ce1074d6c21fd2a08b90d11b1b73139b750a8b0dd97"}, - {file = "yarl-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4f32c4cb7386b41936894685f6e093c8dfaf0960124d91fe0ec29fe439e201d0"}, - {file = "yarl-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5b8e265a0545637492a7e12fd7038370d66c9375a61d88c5567d0e044ded9202"}, - {file = "yarl-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:789a3423f28a5fff46fbd04e339863c169ece97c827b44de16e1a7a42bc915d2"}, - {file = "yarl-1.16.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f1d1f45e3e8d37c804dca99ab3cf4ab3ed2e7a62cd82542924b14c0a4f46d243"}, - {file = "yarl-1.16.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:621280719c4c5dad4c1391160a9b88925bb8b0ff6a7d5af3224643024871675f"}, - {file = "yarl-1.16.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:ed097b26f18a1f5ff05f661dc36528c5f6735ba4ce8c9645e83b064665131349"}, - {file = "yarl-1.16.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:2f1fe2b2e3ee418862f5ebc0c0083c97f6f6625781382f828f6d4e9b614eba9b"}, - {file = "yarl-1.16.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:87dd10bc0618991c66cee0cc65fa74a45f4ecb13bceec3c62d78ad2e42b27a16"}, - {file = "yarl-1.16.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:4199db024b58a8abb2cfcedac7b1292c3ad421684571aeb622a02f242280e8d6"}, - {file = "yarl-1.16.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:99a9dcd4b71dd5f5f949737ab3f356cfc058c709b4f49833aeffedc2652dac56"}, - {file = "yarl-1.16.0-cp311-cp311-win32.whl", hash = "sha256:a9394c65ae0ed95679717d391c862dece9afacd8fa311683fc8b4362ce8a410c"}, - {file = "yarl-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:5b9101f528ae0f8f65ac9d64dda2bb0627de8a50344b2f582779f32fda747c1d"}, - {file = "yarl-1.16.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:4ffb7c129707dd76ced0a4a4128ff452cecf0b0e929f2668ea05a371d9e5c104"}, - {file = "yarl-1.16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:1a5e9d8ce1185723419c487758d81ac2bde693711947032cce600ca7c9cda7d6"}, - {file = "yarl-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d743e3118b2640cef7768ea955378c3536482d95550222f908f392167fe62059"}, - {file = "yarl-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26768342f256e6e3c37533bf9433f5f15f3e59e3c14b2409098291b3efaceacb"}, - {file = "yarl-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d1b0796168b953bca6600c5f97f5ed407479889a36ad7d17183366260f29a6b9"}, - {file = "yarl-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:858728086914f3a407aa7979cab743bbda1fe2bdf39ffcd991469a370dd7414d"}, - {file = "yarl-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5570e6d47bcb03215baf4c9ad7bf7c013e56285d9d35013541f9ac2b372593e7"}, - {file = "yarl-1.16.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66ea8311422a7ba1fc79b4c42c2baa10566469fe5a78500d4e7754d6e6db8724"}, - {file = "yarl-1.16.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:649bddcedee692ee8a9b7b6e38582cb4062dc4253de9711568e5620d8707c2a3"}, - {file = "yarl-1.16.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:3a91654adb7643cb21b46f04244c5a315a440dcad63213033826549fa2435f71"}, - {file = "yarl-1.16.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b439cae82034ade094526a8f692b9a2b5ee936452de5e4c5f0f6c48df23f8604"}, - {file = "yarl-1.16.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:571f781ae8ac463ce30bacebfaef2c6581543776d5970b2372fbe31d7bf31a07"}, - {file = "yarl-1.16.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:aa7943f04f36d6cafc0cf53ea89824ac2c37acbdb4b316a654176ab8ffd0f968"}, - {file = "yarl-1.16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1a5cf32539373ff39d97723e39a9283a7277cbf1224f7aef0c56c9598b6486c3"}, - {file = "yarl-1.16.0-cp312-cp312-win32.whl", hash = "sha256:a5b6c09b9b4253d6a208b0f4a2f9206e511ec68dce9198e0fbec4f160137aa67"}, - {file = "yarl-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:1208ca14eed2fda324042adf8d6c0adf4a31522fa95e0929027cd487875f0240"}, - {file = "yarl-1.16.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a5ace0177520bd4caa99295a9b6fb831d0e9a57d8e0501a22ffaa61b4c024283"}, - {file = "yarl-1.16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7118bdb5e3ed81acaa2095cba7ec02a0fe74b52a16ab9f9ac8e28e53ee299732"}, - {file = "yarl-1.16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:38fec8a2a94c58bd47c9a50a45d321ab2285ad133adefbbadf3012c054b7e656"}, - {file = "yarl-1.16.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8791d66d81ee45866a7bb15a517b01a2bcf583a18ebf5d72a84e6064c417e64b"}, - {file = "yarl-1.16.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1cf936ba67bc6c734f3aa1c01391da74ab7fc046a9f8bbfa230b8393b90cf472"}, - {file = "yarl-1.16.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d1aab176dd55b59f77a63b27cffaca67d29987d91a5b615cbead41331e6b7428"}, - {file = "yarl-1.16.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:995d0759004c08abd5d1b81300a91d18c8577c6389300bed1c7c11675105a44d"}, - {file = "yarl-1.16.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1bc22e00edeb068f71967ab99081e9406cd56dbed864fc3a8259442999d71552"}, - {file = "yarl-1.16.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:35b4f7842154176523e0a63c9b871168c69b98065d05a4f637fce342a6a2693a"}, - {file = "yarl-1.16.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:7ace71c4b7a0c41f317ae24be62bb61e9d80838d38acb20e70697c625e71f120"}, - {file = "yarl-1.16.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:8f639e3f5795a6568aa4f7d2ac6057c757dcd187593679f035adbf12b892bb00"}, - {file = "yarl-1.16.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:e8be3aff14f0120ad049121322b107f8a759be76a6a62138322d4c8a337a9e2c"}, - {file = "yarl-1.16.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:122d8e7986043d0549e9eb23c7fd23be078be4b70c9eb42a20052b3d3149c6f2"}, - {file = "yarl-1.16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0fd9c227990f609c165f56b46107d0bc34553fe0387818c42c02f77974402c36"}, - {file = "yarl-1.16.0-cp313-cp313-win32.whl", hash = "sha256:595ca5e943baed31d56b33b34736461a371c6ea0038d3baec399949dd628560b"}, - {file = "yarl-1.16.0-cp313-cp313-win_amd64.whl", hash = "sha256:921b81b8d78f0e60242fb3db615ea3f368827a76af095d5a69f1c3366db3f596"}, - {file = "yarl-1.16.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ab2b2ac232110a1fdb0d3ffcd087783edd3d4a6ced432a1bf75caf7b7be70916"}, - {file = "yarl-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7f8713717a09acbfee7c47bfc5777e685539fefdd34fa72faf504c8be2f3df4e"}, - {file = "yarl-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cdcffe1dbcb4477d2b4202f63cd972d5baa155ff5a3d9e35801c46a415b7f71a"}, - {file = "yarl-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9a91217208306d82357c67daeef5162a41a28c8352dab7e16daa82e3718852a7"}, - {file = "yarl-1.16.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3ab3ed42c78275477ea8e917491365e9a9b69bb615cb46169020bd0aa5e2d6d3"}, - {file = "yarl-1.16.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:707ae579ccb3262dfaef093e202b4c3fb23c3810e8df544b1111bd2401fd7b09"}, - {file = "yarl-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad7a852d1cd0b8d8b37fc9d7f8581152add917a98cfe2ea6e241878795f917ae"}, - {file = "yarl-1.16.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d3f1cc3d3d4dc574bebc9b387f6875e228ace5748a7c24f49d8f01ac1bc6c31b"}, - {file = "yarl-1.16.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5ff96da263740779b0893d02b718293cc03400c3a208fc8d8cd79d9b0993e532"}, - {file = "yarl-1.16.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:3d375a19ba2bfe320b6d873f3fb165313b002cef8b7cc0a368ad8b8a57453837"}, - {file = "yarl-1.16.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:62c7da0ad93a07da048b500514ca47b759459ec41924143e2ddb5d7e20fd3db5"}, - {file = "yarl-1.16.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:147b0fcd0ee33b4b5f6edfea80452d80e419e51b9a3f7a96ce98eaee145c1581"}, - {file = "yarl-1.16.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:504e1fe1cc4f170195320eb033d2b0ccf5c6114ce5bf2f617535c01699479bca"}, - {file = "yarl-1.16.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:bdcf667a5dec12a48f669e485d70c54189f0639c2157b538a4cffd24a853624f"}, - {file = "yarl-1.16.0-cp39-cp39-win32.whl", hash = "sha256:e9951afe6557c75a71045148890052cb942689ee4c9ec29f5436240e1fcc73b7"}, - {file = "yarl-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:7d7aaa8ff95d0840e289423e7dc35696c2b058d635f945bf05b5cd633146b027"}, - {file = "yarl-1.16.0-py3-none-any.whl", hash = "sha256:e6980a558d8461230c457218bd6c92dfc1d10205548215c2c21d79dc8d0a96f3"}, - {file = "yarl-1.16.0.tar.gz", hash = "sha256:b6f687ced5510a9a2474bbae96a4352e5ace5fa34dc44a217b0537fec1db00b4"}, + {file = "yarl-1.18.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7df647e8edd71f000a5208fe6ff8c382a1de8edfbccdbbfe649d263de07d8c34"}, + {file = "yarl-1.18.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c69697d3adff5aa4f874b19c0e4ed65180ceed6318ec856ebc423aa5850d84f7"}, + {file = "yarl-1.18.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:602d98f2c2d929f8e697ed274fbadc09902c4025c5a9963bf4e9edfc3ab6f7ed"}, + {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c654d5207c78e0bd6d749f6dae1dcbbfde3403ad3a4b11f3c5544d9906969dde"}, + {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5094d9206c64181d0f6e76ebd8fb2f8fe274950a63890ee9e0ebfd58bf9d787b"}, + {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35098b24e0327fc4ebdc8ffe336cee0a87a700c24ffed13161af80124b7dc8e5"}, + {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3236da9272872443f81fedc389bace88408f64f89f75d1bdb2256069a8730ccc"}, + {file = "yarl-1.18.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2c08cc9b16f4f4bc522771d96734c7901e7ebef70c6c5c35dd0f10845270bcd"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:80316a8bd5109320d38eef8833ccf5f89608c9107d02d2a7f985f98ed6876990"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:c1e1cc06da1491e6734f0ea1e6294ce00792193c463350626571c287c9a704db"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fea09ca13323376a2fdfb353a5fa2e59f90cd18d7ca4eaa1fd31f0a8b4f91e62"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e3b9fd71836999aad54084906f8663dffcd2a7fb5cdafd6c37713b2e72be1760"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:757e81cae69244257d125ff31663249b3013b5dc0a8520d73694aed497fb195b"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b1771de9944d875f1b98a745bc547e684b863abf8f8287da8466cf470ef52690"}, + {file = "yarl-1.18.3-cp310-cp310-win32.whl", hash = "sha256:8874027a53e3aea659a6d62751800cf6e63314c160fd607489ba5c2edd753cf6"}, + {file = "yarl-1.18.3-cp310-cp310-win_amd64.whl", hash = "sha256:93b2e109287f93db79210f86deb6b9bbb81ac32fc97236b16f7433db7fc437d8"}, + {file = "yarl-1.18.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8503ad47387b8ebd39cbbbdf0bf113e17330ffd339ba1144074da24c545f0069"}, + {file = "yarl-1.18.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:02ddb6756f8f4517a2d5e99d8b2f272488e18dd0bfbc802f31c16c6c20f22193"}, + {file = "yarl-1.18.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:67a283dd2882ac98cc6318384f565bffc751ab564605959df4752d42483ad889"}, + {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d980e0325b6eddc81331d3f4551e2a333999fb176fd153e075c6d1c2530aa8a8"}, + {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b643562c12680b01e17239be267bc306bbc6aac1f34f6444d1bded0c5ce438ca"}, + {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c017a3b6df3a1bd45b9fa49a0f54005e53fbcad16633870104b66fa1a30a29d8"}, + {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75674776d96d7b851b6498f17824ba17849d790a44d282929c42dbb77d4f17ae"}, + {file = "yarl-1.18.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ccaa3a4b521b780a7e771cc336a2dba389a0861592bbce09a476190bb0c8b4b3"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2d06d3005e668744e11ed80812e61efd77d70bb7f03e33c1598c301eea20efbb"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:9d41beda9dc97ca9ab0b9888cb71f7539124bc05df02c0cff6e5acc5a19dcc6e"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ba23302c0c61a9999784e73809427c9dbedd79f66a13d84ad1b1943802eaaf59"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:6748dbf9bfa5ba1afcc7556b71cda0d7ce5f24768043a02a58846e4a443d808d"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0b0cad37311123211dc91eadcb322ef4d4a66008d3e1bdc404808992260e1a0e"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0fb2171a4486bb075316ee754c6d8382ea6eb8b399d4ec62fde2b591f879778a"}, + {file = "yarl-1.18.3-cp311-cp311-win32.whl", hash = "sha256:61b1a825a13bef4a5f10b1885245377d3cd0bf87cba068e1d9a88c2ae36880e1"}, + {file = "yarl-1.18.3-cp311-cp311-win_amd64.whl", hash = "sha256:b9d60031cf568c627d028239693fd718025719c02c9f55df0a53e587aab951b5"}, + {file = "yarl-1.18.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1dd4bdd05407ced96fed3d7f25dbbf88d2ffb045a0db60dbc247f5b3c5c25d50"}, + {file = "yarl-1.18.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7c33dd1931a95e5d9a772d0ac5e44cac8957eaf58e3c8da8c1414de7dd27c576"}, + {file = "yarl-1.18.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:25b411eddcfd56a2f0cd6a384e9f4f7aa3efee14b188de13048c25b5e91f1640"}, + {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:436c4fc0a4d66b2badc6c5fc5ef4e47bb10e4fd9bf0c79524ac719a01f3607c2"}, + {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e35ef8683211db69ffe129a25d5634319a677570ab6b2eba4afa860f54eeaf75"}, + {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:84b2deecba4a3f1a398df819151eb72d29bfeb3b69abb145a00ddc8d30094512"}, + {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00e5a1fea0fd4f5bfa7440a47eff01d9822a65b4488f7cff83155a0f31a2ecba"}, + {file = "yarl-1.18.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d0e883008013c0e4aef84dcfe2a0b172c4d23c2669412cf5b3371003941f72bb"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5a3f356548e34a70b0172d8890006c37be92995f62d95a07b4a42e90fba54272"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ccd17349166b1bee6e529b4add61727d3f55edb7babbe4069b5764c9587a8cc6"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b958ddd075ddba5b09bb0be8a6d9906d2ce933aee81100db289badbeb966f54e"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c7d79f7d9aabd6011004e33b22bc13056a3e3fb54794d138af57f5ee9d9032cb"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:4891ed92157e5430874dad17b15eb1fda57627710756c27422200c52d8a4e393"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ce1af883b94304f493698b00d0f006d56aea98aeb49d75ec7d98cd4a777e9285"}, + {file = "yarl-1.18.3-cp312-cp312-win32.whl", hash = "sha256:f91c4803173928a25e1a55b943c81f55b8872f0018be83e3ad4938adffb77dd2"}, + {file = "yarl-1.18.3-cp312-cp312-win_amd64.whl", hash = "sha256:7e2ee16578af3b52ac2f334c3b1f92262f47e02cc6193c598502bd46f5cd1477"}, + {file = "yarl-1.18.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:90adb47ad432332d4f0bc28f83a5963f426ce9a1a8809f5e584e704b82685dcb"}, + {file = "yarl-1.18.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:913829534200eb0f789d45349e55203a091f45c37a2674678744ae52fae23efa"}, + {file = "yarl-1.18.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ef9f7768395923c3039055c14334ba4d926f3baf7b776c923c93d80195624782"}, + {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88a19f62ff30117e706ebc9090b8ecc79aeb77d0b1f5ec10d2d27a12bc9f66d0"}, + {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e17c9361d46a4d5addf777c6dd5eab0715a7684c2f11b88c67ac37edfba6c482"}, + {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a74a13a4c857a84a845505fd2d68e54826a2cd01935a96efb1e9d86c728e186"}, + {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41f7ce59d6ee7741af71d82020346af364949314ed3d87553763a2df1829cc58"}, + {file = "yarl-1.18.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f52a265001d830bc425f82ca9eabda94a64a4d753b07d623a9f2863fde532b53"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:82123d0c954dc58db301f5021a01854a85bf1f3bb7d12ae0c01afc414a882ca2"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:2ec9bbba33b2d00999af4631a3397d1fd78290c48e2a3e52d8dd72db3a067ac8"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:fbd6748e8ab9b41171bb95c6142faf068f5ef1511935a0aa07025438dd9a9bc1"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:877d209b6aebeb5b16c42cbb377f5f94d9e556626b1bfff66d7b0d115be88d0a"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b464c4ab4bfcb41e3bfd3f1c26600d038376c2de3297760dfe064d2cb7ea8e10"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8d39d351e7faf01483cc7ff7c0213c412e38e5a340238826be7e0e4da450fdc8"}, + {file = "yarl-1.18.3-cp313-cp313-win32.whl", hash = "sha256:61ee62ead9b68b9123ec24bc866cbef297dd266175d53296e2db5e7f797f902d"}, + {file = "yarl-1.18.3-cp313-cp313-win_amd64.whl", hash = "sha256:578e281c393af575879990861823ef19d66e2b1d0098414855dd367e234f5b3c"}, + {file = "yarl-1.18.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:61e5e68cb65ac8f547f6b5ef933f510134a6bf31bb178be428994b0cb46c2a04"}, + {file = "yarl-1.18.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fe57328fbc1bfd0bd0514470ac692630f3901c0ee39052ae47acd1d90a436719"}, + {file = "yarl-1.18.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a440a2a624683108a1b454705ecd7afc1c3438a08e890a1513d468671d90a04e"}, + {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09c7907c8548bcd6ab860e5f513e727c53b4a714f459b084f6580b49fa1b9cee"}, + {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b4f6450109834af88cb4cc5ecddfc5380ebb9c228695afc11915a0bf82116789"}, + {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9ca04806f3be0ac6d558fffc2fdf8fcef767e0489d2684a21912cc4ed0cd1b8"}, + {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77a6e85b90a7641d2e07184df5557132a337f136250caafc9ccaa4a2a998ca2c"}, + {file = "yarl-1.18.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6333c5a377c8e2f5fae35e7b8f145c617b02c939d04110c76f29ee3676b5f9a5"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0b3c92fa08759dbf12b3a59579a4096ba9af8dd344d9a813fc7f5070d86bbab1"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:4ac515b860c36becb81bb84b667466885096b5fc85596948548b667da3bf9f24"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:045b8482ce9483ada4f3f23b3774f4e1bf4f23a2d5c912ed5170f68efb053318"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:a4bb030cf46a434ec0225bddbebd4b89e6471814ca851abb8696170adb163985"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:54d6921f07555713b9300bee9c50fb46e57e2e639027089b1d795ecd9f7fa910"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1d407181cfa6e70077df3377938c08012d18893f9f20e92f7d2f314a437c30b1"}, + {file = "yarl-1.18.3-cp39-cp39-win32.whl", hash = "sha256:ac36703a585e0929b032fbaab0707b75dc12703766d0b53486eabd5139ebadd5"}, + {file = "yarl-1.18.3-cp39-cp39-win_amd64.whl", hash = "sha256:ba87babd629f8af77f557b61e49e7c7cac36f22f871156b91e10a6e9d4f829e9"}, + {file = "yarl-1.18.3-py3-none-any.whl", hash = "sha256:b57f4f58099328dfb26c6a771d09fb20dbbae81d20cfb66141251ea063bd101b"}, + {file = "yarl-1.18.3.tar.gz", hash = "sha256:ac1801c45cbf77b6c99242eeff4fffb5e4e73a800b5c4ad4fc0be5def634d2e1"}, ] [package.dependencies] @@ -5585,13 +5708,13 @@ propcache = ">=0.2.0" [[package]] name = "zipp" -version = "3.20.2" +version = "3.21.0" description = "Backport of pathlib-compatible object wrapper for zip files" optional = true -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "zipp-3.20.2-py3-none-any.whl", hash = "sha256:a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350"}, - {file = "zipp-3.20.2.tar.gz", hash = "sha256:bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29"}, + {file = "zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931"}, + {file = "zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4"}, ] [package.extras] @@ -5608,4 +5731,4 @@ vllm = ["vllm"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.13" -content-hash = "2fc504f3facbfbd64b2442d18a7f7913517c6e5da64083f8083a0af3f9059d9f" +content-hash = "6f32a9a38e7205ffcfa3d178b69388b5cc78891b38ef683d0b57b53983cba407" diff --git a/python/huggingfaceserver/pyproject.toml b/python/huggingfaceserver/pyproject.toml index 919d47f11fb..5421fced1fc 100644 --- a/python/huggingfaceserver/pyproject.toml +++ b/python/huggingfaceserver/pyproject.toml @@ -1,27 +1,23 @@ [tool.poetry] name = "huggingfaceserver" -version = "0.14.0" +version = "0.15.0rc0" description = "Model Server implementation for huggingface. Not intended for use outside KServe Frameworks Images." authors = ["Dan Sun "] license = "https://github.com/kserve/kserve/blob/master/LICENSE" readme = "README.md" -packages = [ - { include = "huggingfaceserver" }, -] +packages = [{ include = "huggingfaceserver" }] [tool.poetry.dependencies] python = ">=3.9,<3.13" kserve = { path = "../kserve", extras = ["storage"], develop = true } -transformers = ">=4.45.0" +transformers = ">=4.48.3" accelerate = "~0.32.0" -torch = "~2.4.0" -vllm = { version = "^0.6.3.post1", optional = true } -setuptools = {version = ">=70.0.0", python = "3.12"} # setuptools is not part of python 3.12 +torch = "~2.5.0" +vllm = { version = "^0.7.2", optional = true } +setuptools = { version = ">=70.0.0" } [tool.poetry.extras] -vllm = [ - "vllm", -] +vllm = ["vllm"] [tool.poetry.group.test] optional = true diff --git a/python/huggingfaceserver/test_health_check.py b/python/huggingfaceserver/test_health_check.py index b3542159861..38bac68c2de 100644 --- a/python/huggingfaceserver/test_health_check.py +++ b/python/huggingfaceserver/test_health_check.py @@ -15,29 +15,16 @@ import unittest import requests from unittest.mock import patch, MagicMock -import health_check +import health_check as health_check class TestHealthCheck(unittest.TestCase): - @patch("health_check.ray.init") def test_initialize_ray_cluster(self, mock_ray_init): mock_ray_init.return_value = MagicMock() - result = health_check.initialize_ray_cluster() - # mock_ray_init.assert_called_once_with(address="auto") - self.assertEqual(result, "Ray initialized") - - @patch("health_check.ray.init") - def test_perform_health_check_success(self, mock_ray_init): - mock_ray_init.return_value = MagicMock() - result = health_check.check_startup() - self.assertEqual(result, "Healthy") - - @patch("health_check.ray.init") - def test_perform_health_check_failure(self, mock_ray_init): - mock_ray_init.side_effect = Exception("Ray init failed") - result = health_check.check_startup() - self.assertEqual(result, "Unhealthy") + result = health_check.initialize_ray_cluster("auto") + mock_ray_init.assert_called_once_with(address="auto") + self.assertEqual(result, None) # Test check_gpu_usage with healthy GPU usage @patch("health_check.ray.init") @@ -60,7 +47,7 @@ def test_check_gpu_usage_healthy(mock_ray_init, mock_ray_nodes, capsys): }, }, ] - status = health_check.check_gpu_usage("Test GPU Usage") + status = health_check.check_gpu_usage("auto") assert status == "Healthy" # Test check_gpu_usage with unhealthy GPU usage @@ -84,7 +71,7 @@ def test_check_gpu_usage_ungihealthy(mock_ray_init, mock_ray_nodes, capsys): }, }, ] - status = health_check.check_gpu_usage("Test GPU Usage") + status = health_check.check_gpu_usage("auto") assert status == "Unhealthy" # Test check_registered_nodes with correct number of nodes @@ -102,7 +89,7 @@ def test_check_registered_nodes_healthy(mock_ray_init, mock_ray_nodes, capsys): "Alive": True, }, ] - status = health_check.check_registered_nodes(2) + status = health_check.check_registered_nodes(2, "auto") assert status == "Healthy" # Test check_registered_nodes with incorrect number of nodes @@ -116,35 +103,47 @@ def test_check_registered_nodes_unhealthy(mock_ray_init, mock_ray_nodes, capsys) "Alive": True, } ] - status = health_check.check_registered_nodes(2) + status = health_check.check_registered_nodes(2, "auto") assert status == "Unhealthy" @patch("health_check.requests.get") - def test_check_runtime_health_healthy(self, mock_get): + def test_check_runtime_health_healthy_without_retries(self, mock_get): mock_get.return_value.status_code = 200 health_check_url = "http://example.com/health" - status = health_check.check_runtime_health(health_check_url) - + status = health_check.check_runtime_health(health_check_url, retries=0) assert status == "Healthy" mock_get.assert_called_once_with(health_check_url, timeout=5) @patch("health_check.requests.get") - def test_check_runtime_health_unhealthy_status_code(self, mock_get): - mock_get.return_value.status_code = 500 + def test_check_runtime_health_healthy_with_retries(self, mock_get): + mock_get.side_effect = [ + MagicMock(status_code=500), # First call + MagicMock(status_code=200), # Second call + ] health_check_url = "http://example.com/health" - status = health_check.check_runtime_health(health_check_url) + status = health_check.check_runtime_health(health_check_url, retries=1) + assert status == "Healthy" + @patch("health_check.requests.get") + def test_check_runtime_health_unhealthy_status_code_with_retries(self, mock_get): + mock_get.side_effect = [ + MagicMock(status_code=500), # First call + MagicMock(status_code=500), # Second call + ] + health_check_url = "http://example.com/health" + status = health_check.check_runtime_health(health_check_url, retries=1) assert status == "Unhealthy" - mock_get.assert_called_once_with(health_check_url, timeout=5) @patch("health_check.requests.get") - def test_check_runtime_health_request_exception(self, mock_get): - mock_get.side_effect = requests.RequestException + def test_check_runtime_health_request_exception_with_retries(self, mock_get): + mock_get.side_effect = [ + requests.ConnectionError(), + requests.ConnectionError(), + ] + # mock_get.side_effect = requests.RequestException health_check_url = "http://example.com/health" - status = health_check.check_runtime_health(health_check_url) - + status = health_check.check_runtime_health(health_check_url, retries=1) assert status == "Unhealthy" - mock_get.assert_called_once_with(health_check_url, timeout=5) if __name__ == "__main__": diff --git a/python/huggingfaceserver/tests/test_vllm_model.py b/python/huggingfaceserver/tests/test_vllm_model.py index a543556c3b7..9926287d38e 100644 --- a/python/huggingfaceserver/tests/test_vllm_model.py +++ b/python/huggingfaceserver/tests/test_vllm_model.py @@ -77,6 +77,7 @@ async def mock_get_model_config(): seed=0, dtype=dtype, max_model_len=max_model_len, + task="auto", ) mock_vllm_engine = mock.AsyncMock(spec=AsyncLLMEngine) diff --git a/python/kserve/docs/V1beta1CustomExplainer.md b/python/kserve/docs/V1beta1CustomExplainer.md index f31a63b7aa5..f5fcfead744 100644 --- a/python/kserve/docs/V1beta1CustomExplainer.md +++ b/python/kserve/docs/V1beta1CustomExplainer.md @@ -20,7 +20,7 @@ Name | Type | Description | Notes **hostname** | **str** | Specifies the hostname of the Pod If not specified, the pod's hostname will be set to a system-defined value. | [optional] **image_pull_secrets** | [**list[V1LocalObjectReference]**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1LocalObjectReference.md) | ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod | [optional] **init_containers** | [**list[V1Container]**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1Container.md) | List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ | [optional] -**node_name** | **str** | NodeName is a request to schedule this pod onto a specific node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits resource requirements. | [optional] +**node_name** | **str** | NodeName indicates in which node this pod is scheduled. If empty, this pod is a candidate for scheduling by the scheduler defined in schedulerName. Once this field is set, the kubelet for this node becomes responsible for the lifecycle of this pod. This field should not be used to express a desire for the pod to be scheduled on a specific node. https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodename | [optional] **node_selector** | **dict(str, str)** | NodeSelector is a selector which must be true for the pod to fit on a node. Selector which must match a node's labels for the pod to be scheduled on that node. More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ | [optional] **os** | [**V1PodOS**](V1PodOS.md) | | [optional] **overhead** | [**dict(str, ResourceQuantity)**](ResourceQuantity.md) | Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md | [optional] @@ -29,6 +29,7 @@ Name | Type | Description | Notes **priority_class_name** | **str** | If specified, indicates the pod's priority. \"system-node-critical\" and \"system-cluster-critical\" are two special keywords which indicate the highest priorities with the former being the highest priority. Any other name must be defined by creating a PriorityClass object with that name. If not specified, the pod priority will be default or zero if there is no default. | [optional] **readiness_gates** | [**list[V1PodReadinessGate]**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1PodReadinessGate.md) | If specified, all readiness gates will be evaluated for pod readiness. A pod is ready when all its containers are ready AND all conditions specified in the readiness gates have status equal to \"True\" More info: https://git.k8s.io/enhancements/keps/sig-network/580-pod-readiness-gates | [optional] **resource_claims** | [**list[V1PodResourceClaim]**](V1PodResourceClaim.md) | ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name. This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. This field is immutable. | [optional] +**resources** | [**V1ResourceRequirements**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1ResourceRequirements.md) | | [optional] **restart_policy** | **str** | Restart policy for all containers within the pod. One of Always, OnFailure, Never. In some contexts, only a subset of those values may be permitted. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy | [optional] **runtime_class_name** | **str** | RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class | [optional] **scheduler_name** | **str** | If specified, the pod will be dispatched by specified scheduler. If not specified, the pod will be dispatched by default scheduler. | [optional] diff --git a/python/kserve/docs/V1beta1CustomPredictor.md b/python/kserve/docs/V1beta1CustomPredictor.md index 551cf7ce1fa..a596458d717 100644 --- a/python/kserve/docs/V1beta1CustomPredictor.md +++ b/python/kserve/docs/V1beta1CustomPredictor.md @@ -20,7 +20,7 @@ Name | Type | Description | Notes **hostname** | **str** | Specifies the hostname of the Pod If not specified, the pod's hostname will be set to a system-defined value. | [optional] **image_pull_secrets** | [**list[V1LocalObjectReference]**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1LocalObjectReference.md) | ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod | [optional] **init_containers** | [**list[V1Container]**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1Container.md) | List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ | [optional] -**node_name** | **str** | NodeName is a request to schedule this pod onto a specific node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits resource requirements. | [optional] +**node_name** | **str** | NodeName indicates in which node this pod is scheduled. If empty, this pod is a candidate for scheduling by the scheduler defined in schedulerName. Once this field is set, the kubelet for this node becomes responsible for the lifecycle of this pod. This field should not be used to express a desire for the pod to be scheduled on a specific node. https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodename | [optional] **node_selector** | **dict(str, str)** | NodeSelector is a selector which must be true for the pod to fit on a node. Selector which must match a node's labels for the pod to be scheduled on that node. More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ | [optional] **os** | [**V1PodOS**](V1PodOS.md) | | [optional] **overhead** | [**dict(str, ResourceQuantity)**](ResourceQuantity.md) | Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md | [optional] @@ -29,6 +29,7 @@ Name | Type | Description | Notes **priority_class_name** | **str** | If specified, indicates the pod's priority. \"system-node-critical\" and \"system-cluster-critical\" are two special keywords which indicate the highest priorities with the former being the highest priority. Any other name must be defined by creating a PriorityClass object with that name. If not specified, the pod priority will be default or zero if there is no default. | [optional] **readiness_gates** | [**list[V1PodReadinessGate]**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1PodReadinessGate.md) | If specified, all readiness gates will be evaluated for pod readiness. A pod is ready when all its containers are ready AND all conditions specified in the readiness gates have status equal to \"True\" More info: https://git.k8s.io/enhancements/keps/sig-network/580-pod-readiness-gates | [optional] **resource_claims** | [**list[V1PodResourceClaim]**](V1PodResourceClaim.md) | ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name. This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. This field is immutable. | [optional] +**resources** | [**V1ResourceRequirements**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1ResourceRequirements.md) | | [optional] **restart_policy** | **str** | Restart policy for all containers within the pod. One of Always, OnFailure, Never. In some contexts, only a subset of those values may be permitted. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy | [optional] **runtime_class_name** | **str** | RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class | [optional] **scheduler_name** | **str** | If specified, the pod will be dispatched by specified scheduler. If not specified, the pod will be dispatched by default scheduler. | [optional] diff --git a/python/kserve/docs/V1beta1CustomTransformer.md b/python/kserve/docs/V1beta1CustomTransformer.md index 4f9fca7a764..b1c18569c09 100644 --- a/python/kserve/docs/V1beta1CustomTransformer.md +++ b/python/kserve/docs/V1beta1CustomTransformer.md @@ -20,7 +20,7 @@ Name | Type | Description | Notes **hostname** | **str** | Specifies the hostname of the Pod If not specified, the pod's hostname will be set to a system-defined value. | [optional] **image_pull_secrets** | [**list[V1LocalObjectReference]**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1LocalObjectReference.md) | ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod | [optional] **init_containers** | [**list[V1Container]**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1Container.md) | List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ | [optional] -**node_name** | **str** | NodeName is a request to schedule this pod onto a specific node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits resource requirements. | [optional] +**node_name** | **str** | NodeName indicates in which node this pod is scheduled. If empty, this pod is a candidate for scheduling by the scheduler defined in schedulerName. Once this field is set, the kubelet for this node becomes responsible for the lifecycle of this pod. This field should not be used to express a desire for the pod to be scheduled on a specific node. https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodename | [optional] **node_selector** | **dict(str, str)** | NodeSelector is a selector which must be true for the pod to fit on a node. Selector which must match a node's labels for the pod to be scheduled on that node. More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ | [optional] **os** | [**V1PodOS**](V1PodOS.md) | | [optional] **overhead** | [**dict(str, ResourceQuantity)**](ResourceQuantity.md) | Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md | [optional] @@ -29,6 +29,7 @@ Name | Type | Description | Notes **priority_class_name** | **str** | If specified, indicates the pod's priority. \"system-node-critical\" and \"system-cluster-critical\" are two special keywords which indicate the highest priorities with the former being the highest priority. Any other name must be defined by creating a PriorityClass object with that name. If not specified, the pod priority will be default or zero if there is no default. | [optional] **readiness_gates** | [**list[V1PodReadinessGate]**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1PodReadinessGate.md) | If specified, all readiness gates will be evaluated for pod readiness. A pod is ready when all its containers are ready AND all conditions specified in the readiness gates have status equal to \"True\" More info: https://git.k8s.io/enhancements/keps/sig-network/580-pod-readiness-gates | [optional] **resource_claims** | [**list[V1PodResourceClaim]**](V1PodResourceClaim.md) | ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name. This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. This field is immutable. | [optional] +**resources** | [**V1ResourceRequirements**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1ResourceRequirements.md) | | [optional] **restart_policy** | **str** | Restart policy for all containers within the pod. One of Always, OnFailure, Never. In some contexts, only a subset of those values may be permitted. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy | [optional] **runtime_class_name** | **str** | RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class | [optional] **scheduler_name** | **str** | If specified, the pod will be dispatched by specified scheduler. If not specified, the pod will be dispatched by default scheduler. | [optional] diff --git a/python/kserve/docs/V1beta1ExplainerSpec.md b/python/kserve/docs/V1beta1ExplainerSpec.md index 5c005695e3f..20f0ca0dabf 100644 --- a/python/kserve/docs/V1beta1ExplainerSpec.md +++ b/python/kserve/docs/V1beta1ExplainerSpec.md @@ -17,36 +17,37 @@ Name | Type | Description | Notes **dns_config** | [**V1PodDNSConfig**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1PodDNSConfig.md) | | [optional] **dns_policy** | **str** | Set DNS policy for the pod. Defaults to \"ClusterFirst\". Valid values are 'ClusterFirstWithHostNet', 'ClusterFirst', 'Default' or 'None'. DNS parameters given in DNSConfig will be merged with the policy selected with DNSPolicy. To have DNS options set along with hostNetwork, you have to specify DNS policy explicitly to 'ClusterFirstWithHostNet'. | [optional] **enable_service_links** | **bool** | EnableServiceLinks indicates whether information about services should be injected into pod's environment variables, matching the syntax of Docker links. Optional: Defaults to true. | [optional] -**ephemeral_containers** | [**list[V1EphemeralContainer]**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1EphemeralContainer.md) | List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource. This field is beta-level and available on clusters that haven't disabled the EphemeralContainers feature gate. | [optional] -**host_aliases** | [**list[V1HostAlias]**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1HostAlias.md) | HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. This is only valid for non-hostNetwork pods. | [optional] +**ephemeral_containers** | [**list[V1EphemeralContainer]**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1EphemeralContainer.md) | List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource. | [optional] +**host_aliases** | [**list[V1HostAlias]**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1HostAlias.md) | HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. | [optional] **host_ipc** | **bool** | Use the host's ipc namespace. Optional: Default to false. | [optional] **host_network** | **bool** | Host networking requested for this pod. Use the host's network namespace. If this option is set, the ports that will be used must be specified. Default to false. | [optional] **host_pid** | **bool** | Use the host's pid namespace. Optional: Default to false. | [optional] **host_users** | **bool** | Use the host's user namespace. Optional: Default to true. If set to true or not present, the pod will be run in the host user namespace, useful for when the pod needs a feature only available to the host user namespace, such as loading a kernel module with CAP_SYS_MODULE. When set to false, a new userns is created for the pod. Setting false is useful for mitigating container breakout vulnerabilities even allowing users to run their containers as root without actually having root privileges on the host. This field is alpha-level and is only honored by servers that enable the UserNamespacesSupport feature. | [optional] **hostname** | **str** | Specifies the hostname of the Pod If not specified, the pod's hostname will be set to a system-defined value. | [optional] -**image_pull_secrets** | [**list[V1LocalObjectReference]**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1LocalObjectReference.md) | ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. For example, in the case of docker, only DockerConfig type secrets are honored. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod | [optional] -**init_containers** | [**list[V1Container]**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1Container.md) | List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ | [optional] +**image_pull_secrets** | [**list[V1LocalObjectReference]**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1LocalObjectReference.md) | ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod | [optional] +**init_containers** | [**list[V1Container]**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1Container.md) | List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ | [optional] **labels** | **dict(str, str)** | Labels that will be added to the component pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ | [optional] **logger** | [**V1beta1LoggerSpec**](V1beta1LoggerSpec.md) | | [optional] **max_replicas** | **int** | Maximum number of replicas for autoscaling. | [optional] **min_replicas** | **int** | Minimum number of replicas, defaults to 1 but can be set to 0 to enable scale-to-zero. | [optional] -**node_name** | **str** | NodeName is a request to schedule this pod onto a specific node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits resource requirements. | [optional] +**node_name** | **str** | NodeName indicates in which node this pod is scheduled. If empty, this pod is a candidate for scheduling by the scheduler defined in schedulerName. Once this field is set, the kubelet for this node becomes responsible for the lifecycle of this pod. This field should not be used to express a desire for the pod to be scheduled on a specific node. https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodename | [optional] **node_selector** | **dict(str, str)** | NodeSelector is a selector which must be true for the pod to fit on a node. Selector which must match a node's labels for the pod to be scheduled on that node. More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ | [optional] **os** | [**V1PodOS**](V1PodOS.md) | | [optional] -**overhead** | [**dict(str, ResourceQuantity)**](ResourceQuantity.md) | Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md This field is beta-level as of Kubernetes v1.18, and is only honored by servers that enable the PodOverhead feature. | [optional] -**preemption_policy** | **str** | PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset. This field is beta-level, gated by the NonPreemptingPriority feature-gate. | [optional] +**overhead** | [**dict(str, ResourceQuantity)**](ResourceQuantity.md) | Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md | [optional] +**preemption_policy** | **str** | PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset. | [optional] **priority** | **int** | The priority value. Various system components use this field to find the priority of the pod. When Priority Admission Controller is enabled, it prevents users from setting this field. The admission controller populates this field from PriorityClassName. The higher the value, the higher the priority. | [optional] **priority_class_name** | **str** | If specified, indicates the pod's priority. \"system-node-critical\" and \"system-cluster-critical\" are two special keywords which indicate the highest priorities with the former being the highest priority. Any other name must be defined by creating a PriorityClass object with that name. If not specified, the pod priority will be default or zero if there is no default. | [optional] **readiness_gates** | [**list[V1PodReadinessGate]**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1PodReadinessGate.md) | If specified, all readiness gates will be evaluated for pod readiness. A pod is ready when all its containers are ready AND all conditions specified in the readiness gates have status equal to \"True\" More info: https://git.k8s.io/enhancements/keps/sig-network/580-pod-readiness-gates | [optional] **resource_claims** | [**list[V1PodResourceClaim]**](V1PodResourceClaim.md) | ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name. This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. This field is immutable. | [optional] -**restart_policy** | **str** | Restart policy for all containers within the pod. One of Always, OnFailure, Never. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy | [optional] -**runtime_class_name** | **str** | RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class This is a beta feature as of Kubernetes v1.14. | [optional] +**resources** | [**V1ResourceRequirements**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1ResourceRequirements.md) | | [optional] +**restart_policy** | **str** | Restart policy for all containers within the pod. One of Always, OnFailure, Never. In some contexts, only a subset of those values may be permitted. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy | [optional] +**runtime_class_name** | **str** | RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class | [optional] **scale_metric** | **str** | ScaleMetric defines the scaling metric type watched by autoscaler possible values are concurrency, rps, cpu, memory. concurrency, rps are supported via Knative Pod Autoscaler(https://knative.dev/docs/serving/autoscaling/autoscaling-metrics). | [optional] **scale_target** | **int** | ScaleTarget specifies the integer target value of the metric type the Autoscaler watches for. concurrency and rps targets are supported by Knative Pod Autoscaler (https://knative.dev/docs/serving/autoscaling/autoscaling-targets/). | [optional] **scheduler_name** | **str** | If specified, the pod will be dispatched by specified scheduler. If not specified, the pod will be dispatched by default scheduler. | [optional] -**scheduling_gates** | [**list[V1PodSchedulingGate]**](V1PodSchedulingGate.md) | SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod. SchedulingGates can only be set at pod creation time, and be removed only afterwards. This is a beta feature enabled by the PodSchedulingReadiness feature gate. | [optional] +**scheduling_gates** | [**list[V1PodSchedulingGate]**](V1PodSchedulingGate.md) | SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod. SchedulingGates can only be set at pod creation time, and be removed only afterwards. | [optional] **security_context** | [**V1PodSecurityContext**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1PodSecurityContext.md) | | [optional] -**service_account** | **str** | DeprecatedServiceAccount is a depreciated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead. | [optional] +**service_account** | **str** | DeprecatedServiceAccount is a deprecated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead. | [optional] **service_account_name** | **str** | ServiceAccountName is the name of the ServiceAccount to use to run this pod. More info: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/ | [optional] **set_hostname_as_fqdn** | **bool** | If true the pod's hostname will be configured as the pod's FQDN, rather than the leaf name (the default). In Linux containers, this means setting the FQDN in the hostname field of the kernel (the nodename field of struct utsname). In Windows containers, this means setting the registry value of hostname for the registry key HKEY_LOCAL_MACHINE\\\\SYSTEM\\\\CurrentControlSet\\\\Services\\\\Tcpip\\\\Parameters to FQDN. If a pod does not have FQDN, this has no effect. Default to false. | [optional] **share_process_namespace** | **bool** | Share a single process namespace between all of the containers in a pod. When this is set containers will be able to view and signal processes from other containers in the same pod, and the first process in each container will not be assigned PID 1. HostPID and ShareProcessNamespace cannot both be set. Optional: Default to false. | [optional] diff --git a/python/kserve/docs/V1beta1InferenceServicesConfig.md b/python/kserve/docs/V1beta1InferenceServicesConfig.md index 3901fa327d1..c417c76f06d 100644 --- a/python/kserve/docs/V1beta1InferenceServicesConfig.md +++ b/python/kserve/docs/V1beta1InferenceServicesConfig.md @@ -4,6 +4,9 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- **explainers** | [**V1beta1ExplainersConfig**](V1beta1ExplainersConfig.md) | | +**resource** | [**V1beta1ResourceConfig**](V1beta1ResourceConfig.md) | | [optional] +**service_annotation_disallowed_list** | **list[str]** | ServiceAnnotationDisallowedList is a list of annotations that are not allowed to be propagated to Knative revisions | [optional] +**service_label_disallowed_list** | **list[str]** | ServiceLabelDisallowedList is a list of labels that are not allowed to be propagated to Knative revisions | [optional] [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/python/kserve/docs/V1beta1IngressConfig.md b/python/kserve/docs/V1beta1IngressConfig.md index c54e90e7547..ff864d6b572 100644 --- a/python/kserve/docs/V1beta1IngressConfig.md +++ b/python/kserve/docs/V1beta1IngressConfig.md @@ -7,10 +7,12 @@ Name | Type | Description | Notes **disable_ingress_creation** | **bool** | | [optional] **disable_istio_virtual_host** | **bool** | | [optional] **domain_template** | **str** | | [optional] +**enable_gateway_api** | **bool** | | [optional] **ingress_class_name** | **str** | | [optional] **ingress_domain** | **str** | | [optional] **ingress_gateway** | **str** | | [optional] **knative_local_gateway_service** | **str** | | [optional] +**kserve_ingress_gateway** | **str** | | [optional] **local_gateway** | **str** | | [optional] **local_gateway_service** | **str** | | [optional] **path_template** | **str** | | [optional] diff --git a/python/kserve/docs/V1beta1LocalModelConfig.md b/python/kserve/docs/V1beta1LocalModelConfig.md index bc3df9c3d39..254e1a07efc 100644 --- a/python/kserve/docs/V1beta1LocalModelConfig.md +++ b/python/kserve/docs/V1beta1LocalModelConfig.md @@ -4,6 +4,7 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- **default_job_image** | **str** | | [optional] +**disable_volume_management** | **bool** | | [optional] **enabled** | **bool** | | [default to False] **fs_group** | **int** | | [optional] **job_namespace** | **str** | | [default to ''] diff --git a/python/kserve/docs/V1beta1PodSpec.md b/python/kserve/docs/V1beta1PodSpec.md index 6e1438ea773..70ff57fda4f 100644 --- a/python/kserve/docs/V1beta1PodSpec.md +++ b/python/kserve/docs/V1beta1PodSpec.md @@ -11,30 +11,31 @@ Name | Type | Description | Notes **dns_config** | [**V1PodDNSConfig**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1PodDNSConfig.md) | | [optional] **dns_policy** | **str** | Set DNS policy for the pod. Defaults to \"ClusterFirst\". Valid values are 'ClusterFirstWithHostNet', 'ClusterFirst', 'Default' or 'None'. DNS parameters given in DNSConfig will be merged with the policy selected with DNSPolicy. To have DNS options set along with hostNetwork, you have to specify DNS policy explicitly to 'ClusterFirstWithHostNet'. | [optional] **enable_service_links** | **bool** | EnableServiceLinks indicates whether information about services should be injected into pod's environment variables, matching the syntax of Docker links. Optional: Defaults to true. | [optional] -**ephemeral_containers** | [**list[V1EphemeralContainer]**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1EphemeralContainer.md) | List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource. This field is beta-level and available on clusters that haven't disabled the EphemeralContainers feature gate. | [optional] -**host_aliases** | [**list[V1HostAlias]**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1HostAlias.md) | HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. This is only valid for non-hostNetwork pods. | [optional] +**ephemeral_containers** | [**list[V1EphemeralContainer]**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1EphemeralContainer.md) | List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource. | [optional] +**host_aliases** | [**list[V1HostAlias]**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1HostAlias.md) | HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. | [optional] **host_ipc** | **bool** | Use the host's ipc namespace. Optional: Default to false. | [optional] **host_network** | **bool** | Host networking requested for this pod. Use the host's network namespace. If this option is set, the ports that will be used must be specified. Default to false. | [optional] **host_pid** | **bool** | Use the host's pid namespace. Optional: Default to false. | [optional] **host_users** | **bool** | Use the host's user namespace. Optional: Default to true. If set to true or not present, the pod will be run in the host user namespace, useful for when the pod needs a feature only available to the host user namespace, such as loading a kernel module with CAP_SYS_MODULE. When set to false, a new userns is created for the pod. Setting false is useful for mitigating container breakout vulnerabilities even allowing users to run their containers as root without actually having root privileges on the host. This field is alpha-level and is only honored by servers that enable the UserNamespacesSupport feature. | [optional] **hostname** | **str** | Specifies the hostname of the Pod If not specified, the pod's hostname will be set to a system-defined value. | [optional] -**image_pull_secrets** | [**list[V1LocalObjectReference]**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1LocalObjectReference.md) | ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. For example, in the case of docker, only DockerConfig type secrets are honored. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod | [optional] -**init_containers** | [**list[V1Container]**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1Container.md) | List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ | [optional] -**node_name** | **str** | NodeName is a request to schedule this pod onto a specific node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits resource requirements. | [optional] +**image_pull_secrets** | [**list[V1LocalObjectReference]**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1LocalObjectReference.md) | ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod | [optional] +**init_containers** | [**list[V1Container]**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1Container.md) | List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ | [optional] +**node_name** | **str** | NodeName indicates in which node this pod is scheduled. If empty, this pod is a candidate for scheduling by the scheduler defined in schedulerName. Once this field is set, the kubelet for this node becomes responsible for the lifecycle of this pod. This field should not be used to express a desire for the pod to be scheduled on a specific node. https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodename | [optional] **node_selector** | **dict(str, str)** | NodeSelector is a selector which must be true for the pod to fit on a node. Selector which must match a node's labels for the pod to be scheduled on that node. More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ | [optional] **os** | [**V1PodOS**](V1PodOS.md) | | [optional] -**overhead** | [**dict(str, ResourceQuantity)**](ResourceQuantity.md) | Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md This field is beta-level as of Kubernetes v1.18, and is only honored by servers that enable the PodOverhead feature. | [optional] -**preemption_policy** | **str** | PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset. This field is beta-level, gated by the NonPreemptingPriority feature-gate. | [optional] +**overhead** | [**dict(str, ResourceQuantity)**](ResourceQuantity.md) | Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md | [optional] +**preemption_policy** | **str** | PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset. | [optional] **priority** | **int** | The priority value. Various system components use this field to find the priority of the pod. When Priority Admission Controller is enabled, it prevents users from setting this field. The admission controller populates this field from PriorityClassName. The higher the value, the higher the priority. | [optional] **priority_class_name** | **str** | If specified, indicates the pod's priority. \"system-node-critical\" and \"system-cluster-critical\" are two special keywords which indicate the highest priorities with the former being the highest priority. Any other name must be defined by creating a PriorityClass object with that name. If not specified, the pod priority will be default or zero if there is no default. | [optional] **readiness_gates** | [**list[V1PodReadinessGate]**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1PodReadinessGate.md) | If specified, all readiness gates will be evaluated for pod readiness. A pod is ready when all its containers are ready AND all conditions specified in the readiness gates have status equal to \"True\" More info: https://git.k8s.io/enhancements/keps/sig-network/580-pod-readiness-gates | [optional] **resource_claims** | [**list[V1PodResourceClaim]**](V1PodResourceClaim.md) | ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name. This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. This field is immutable. | [optional] -**restart_policy** | **str** | Restart policy for all containers within the pod. One of Always, OnFailure, Never. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy | [optional] -**runtime_class_name** | **str** | RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class This is a beta feature as of Kubernetes v1.14. | [optional] +**resources** | [**V1ResourceRequirements**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1ResourceRequirements.md) | | [optional] +**restart_policy** | **str** | Restart policy for all containers within the pod. One of Always, OnFailure, Never. In some contexts, only a subset of those values may be permitted. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy | [optional] +**runtime_class_name** | **str** | RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class | [optional] **scheduler_name** | **str** | If specified, the pod will be dispatched by specified scheduler. If not specified, the pod will be dispatched by default scheduler. | [optional] -**scheduling_gates** | [**list[V1PodSchedulingGate]**](V1PodSchedulingGate.md) | SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod. SchedulingGates can only be set at pod creation time, and be removed only afterwards. This is a beta feature enabled by the PodSchedulingReadiness feature gate. | [optional] +**scheduling_gates** | [**list[V1PodSchedulingGate]**](V1PodSchedulingGate.md) | SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod. SchedulingGates can only be set at pod creation time, and be removed only afterwards. | [optional] **security_context** | [**V1PodSecurityContext**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1PodSecurityContext.md) | | [optional] -**service_account** | **str** | DeprecatedServiceAccount is a depreciated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead. | [optional] +**service_account** | **str** | DeprecatedServiceAccount is a deprecated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead. | [optional] **service_account_name** | **str** | ServiceAccountName is the name of the ServiceAccount to use to run this pod. More info: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/ | [optional] **set_hostname_as_fqdn** | **bool** | If true the pod's hostname will be configured as the pod's FQDN, rather than the leaf name (the default). In Linux containers, this means setting the FQDN in the hostname field of the kernel (the nodename field of struct utsname). In Windows containers, this means setting the registry value of hostname for the registry key HKEY_LOCAL_MACHINE\\\\SYSTEM\\\\CurrentControlSet\\\\Services\\\\Tcpip\\\\Parameters to FQDN. If a pod does not have FQDN, this has no effect. Default to false. | [optional] **share_process_namespace** | **bool** | Share a single process namespace between all of the containers in a pod. When this is set containers will be able to view and signal processes from other containers in the same pod, and the first process in each container will not be assigned PID 1. HostPID and ShareProcessNamespace cannot both be set. Optional: Default to false. | [optional] diff --git a/python/kserve/docs/V1beta1PredictorSpec.md b/python/kserve/docs/V1beta1PredictorSpec.md index 216e88b55b2..387d37862a0 100644 --- a/python/kserve/docs/V1beta1PredictorSpec.md +++ b/python/kserve/docs/V1beta1PredictorSpec.md @@ -16,43 +16,44 @@ Name | Type | Description | Notes **dns_config** | [**V1PodDNSConfig**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1PodDNSConfig.md) | | [optional] **dns_policy** | **str** | Set DNS policy for the pod. Defaults to \"ClusterFirst\". Valid values are 'ClusterFirstWithHostNet', 'ClusterFirst', 'Default' or 'None'. DNS parameters given in DNSConfig will be merged with the policy selected with DNSPolicy. To have DNS options set along with hostNetwork, you have to specify DNS policy explicitly to 'ClusterFirstWithHostNet'. | [optional] **enable_service_links** | **bool** | EnableServiceLinks indicates whether information about services should be injected into pod's environment variables, matching the syntax of Docker links. Optional: Defaults to true. | [optional] -**ephemeral_containers** | [**list[V1EphemeralContainer]**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1EphemeralContainer.md) | List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource. This field is beta-level and available on clusters that haven't disabled the EphemeralContainers feature gate. | [optional] -**host_aliases** | [**list[V1HostAlias]**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1HostAlias.md) | HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. This is only valid for non-hostNetwork pods. | [optional] +**ephemeral_containers** | [**list[V1EphemeralContainer]**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1EphemeralContainer.md) | List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource. | [optional] +**host_aliases** | [**list[V1HostAlias]**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1HostAlias.md) | HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. | [optional] **host_ipc** | **bool** | Use the host's ipc namespace. Optional: Default to false. | [optional] **host_network** | **bool** | Host networking requested for this pod. Use the host's network namespace. If this option is set, the ports that will be used must be specified. Default to false. | [optional] **host_pid** | **bool** | Use the host's pid namespace. Optional: Default to false. | [optional] **host_users** | **bool** | Use the host's user namespace. Optional: Default to true. If set to true or not present, the pod will be run in the host user namespace, useful for when the pod needs a feature only available to the host user namespace, such as loading a kernel module with CAP_SYS_MODULE. When set to false, a new userns is created for the pod. Setting false is useful for mitigating container breakout vulnerabilities even allowing users to run their containers as root without actually having root privileges on the host. This field is alpha-level and is only honored by servers that enable the UserNamespacesSupport feature. | [optional] **hostname** | **str** | Specifies the hostname of the Pod If not specified, the pod's hostname will be set to a system-defined value. | [optional] **huggingface** | [**V1beta1HuggingFaceRuntimeSpec**](V1beta1HuggingFaceRuntimeSpec.md) | | [optional] -**image_pull_secrets** | [**list[V1LocalObjectReference]**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1LocalObjectReference.md) | ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. For example, in the case of docker, only DockerConfig type secrets are honored. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod | [optional] -**init_containers** | [**list[V1Container]**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1Container.md) | List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ | [optional] +**image_pull_secrets** | [**list[V1LocalObjectReference]**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1LocalObjectReference.md) | ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod | [optional] +**init_containers** | [**list[V1Container]**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1Container.md) | List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ | [optional] **labels** | **dict(str, str)** | Labels that will be added to the component pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ | [optional] **lightgbm** | [**V1beta1LightGBMSpec**](V1beta1LightGBMSpec.md) | | [optional] **logger** | [**V1beta1LoggerSpec**](V1beta1LoggerSpec.md) | | [optional] **max_replicas** | **int** | Maximum number of replicas for autoscaling. | [optional] **min_replicas** | **int** | Minimum number of replicas, defaults to 1 but can be set to 0 to enable scale-to-zero. | [optional] **model** | [**V1beta1ModelSpec**](V1beta1ModelSpec.md) | | [optional] -**node_name** | **str** | NodeName is a request to schedule this pod onto a specific node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits resource requirements. | [optional] +**node_name** | **str** | NodeName indicates in which node this pod is scheduled. If empty, this pod is a candidate for scheduling by the scheduler defined in schedulerName. Once this field is set, the kubelet for this node becomes responsible for the lifecycle of this pod. This field should not be used to express a desire for the pod to be scheduled on a specific node. https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodename | [optional] **node_selector** | **dict(str, str)** | NodeSelector is a selector which must be true for the pod to fit on a node. Selector which must match a node's labels for the pod to be scheduled on that node. More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ | [optional] **onnx** | [**V1beta1ONNXRuntimeSpec**](V1beta1ONNXRuntimeSpec.md) | | [optional] **os** | [**V1PodOS**](V1PodOS.md) | | [optional] -**overhead** | [**dict(str, ResourceQuantity)**](ResourceQuantity.md) | Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md This field is beta-level as of Kubernetes v1.18, and is only honored by servers that enable the PodOverhead feature. | [optional] +**overhead** | [**dict(str, ResourceQuantity)**](ResourceQuantity.md) | Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md | [optional] **paddle** | [**V1beta1PaddleServerSpec**](V1beta1PaddleServerSpec.md) | | [optional] **pmml** | [**V1beta1PMMLSpec**](V1beta1PMMLSpec.md) | | [optional] -**preemption_policy** | **str** | PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset. This field is beta-level, gated by the NonPreemptingPriority feature-gate. | [optional] +**preemption_policy** | **str** | PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset. | [optional] **priority** | **int** | The priority value. Various system components use this field to find the priority of the pod. When Priority Admission Controller is enabled, it prevents users from setting this field. The admission controller populates this field from PriorityClassName. The higher the value, the higher the priority. | [optional] **priority_class_name** | **str** | If specified, indicates the pod's priority. \"system-node-critical\" and \"system-cluster-critical\" are two special keywords which indicate the highest priorities with the former being the highest priority. Any other name must be defined by creating a PriorityClass object with that name. If not specified, the pod priority will be default or zero if there is no default. | [optional] **pytorch** | [**V1beta1TorchServeSpec**](V1beta1TorchServeSpec.md) | | [optional] **readiness_gates** | [**list[V1PodReadinessGate]**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1PodReadinessGate.md) | If specified, all readiness gates will be evaluated for pod readiness. A pod is ready when all its containers are ready AND all conditions specified in the readiness gates have status equal to \"True\" More info: https://git.k8s.io/enhancements/keps/sig-network/580-pod-readiness-gates | [optional] **resource_claims** | [**list[V1PodResourceClaim]**](V1PodResourceClaim.md) | ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name. This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. This field is immutable. | [optional] -**restart_policy** | **str** | Restart policy for all containers within the pod. One of Always, OnFailure, Never. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy | [optional] -**runtime_class_name** | **str** | RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class This is a beta feature as of Kubernetes v1.14. | [optional] +**resources** | [**V1ResourceRequirements**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1ResourceRequirements.md) | | [optional] +**restart_policy** | **str** | Restart policy for all containers within the pod. One of Always, OnFailure, Never. In some contexts, only a subset of those values may be permitted. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy | [optional] +**runtime_class_name** | **str** | RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class | [optional] **scale_metric** | **str** | ScaleMetric defines the scaling metric type watched by autoscaler possible values are concurrency, rps, cpu, memory. concurrency, rps are supported via Knative Pod Autoscaler(https://knative.dev/docs/serving/autoscaling/autoscaling-metrics). | [optional] **scale_target** | **int** | ScaleTarget specifies the integer target value of the metric type the Autoscaler watches for. concurrency and rps targets are supported by Knative Pod Autoscaler (https://knative.dev/docs/serving/autoscaling/autoscaling-targets/). | [optional] **scheduler_name** | **str** | If specified, the pod will be dispatched by specified scheduler. If not specified, the pod will be dispatched by default scheduler. | [optional] -**scheduling_gates** | [**list[V1PodSchedulingGate]**](V1PodSchedulingGate.md) | SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod. SchedulingGates can only be set at pod creation time, and be removed only afterwards. This is a beta feature enabled by the PodSchedulingReadiness feature gate. | [optional] +**scheduling_gates** | [**list[V1PodSchedulingGate]**](V1PodSchedulingGate.md) | SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod. SchedulingGates can only be set at pod creation time, and be removed only afterwards. | [optional] **security_context** | [**V1PodSecurityContext**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1PodSecurityContext.md) | | [optional] -**service_account** | **str** | DeprecatedServiceAccount is a depreciated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead. | [optional] +**service_account** | **str** | DeprecatedServiceAccount is a deprecated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead. | [optional] **service_account_name** | **str** | ServiceAccountName is the name of the ServiceAccount to use to run this pod. More info: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/ | [optional] **set_hostname_as_fqdn** | **bool** | If true the pod's hostname will be configured as the pod's FQDN, rather than the leaf name (the default). In Linux containers, this means setting the FQDN in the hostname field of the kernel (the nodename field of struct utsname). In Windows containers, this means setting the registry value of hostname for the registry key HKEY_LOCAL_MACHINE\\\\SYSTEM\\\\CurrentControlSet\\\\Services\\\\Tcpip\\\\Parameters to FQDN. If a pod does not have FQDN, this has no effect. Default to false. | [optional] **share_process_namespace** | **bool** | Share a single process namespace between all of the containers in a pod. When this is set containers will be able to view and signal processes from other containers in the same pod, and the first process in each container will not be assigned PID 1. HostPID and ShareProcessNamespace cannot both be set. Optional: Default to false. | [optional] diff --git a/python/kserve/docs/V1beta1ResourceConfig.md b/python/kserve/docs/V1beta1ResourceConfig.md new file mode 100644 index 00000000000..c4582371c67 --- /dev/null +++ b/python/kserve/docs/V1beta1ResourceConfig.md @@ -0,0 +1,13 @@ +# V1beta1ResourceConfig + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**cpu_limit** | **str** | | [optional] +**cpu_request** | **str** | | [optional] +**memory_limit** | **str** | | [optional] +**memory_request** | **str** | | [optional] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/python/kserve/docs/V1beta1TransformerSpec.md b/python/kserve/docs/V1beta1TransformerSpec.md index 490f156a64d..090790950a3 100644 --- a/python/kserve/docs/V1beta1TransformerSpec.md +++ b/python/kserve/docs/V1beta1TransformerSpec.md @@ -16,36 +16,37 @@ Name | Type | Description | Notes **dns_config** | [**V1PodDNSConfig**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1PodDNSConfig.md) | | [optional] **dns_policy** | **str** | Set DNS policy for the pod. Defaults to \"ClusterFirst\". Valid values are 'ClusterFirstWithHostNet', 'ClusterFirst', 'Default' or 'None'. DNS parameters given in DNSConfig will be merged with the policy selected with DNSPolicy. To have DNS options set along with hostNetwork, you have to specify DNS policy explicitly to 'ClusterFirstWithHostNet'. | [optional] **enable_service_links** | **bool** | EnableServiceLinks indicates whether information about services should be injected into pod's environment variables, matching the syntax of Docker links. Optional: Defaults to true. | [optional] -**ephemeral_containers** | [**list[V1EphemeralContainer]**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1EphemeralContainer.md) | List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource. This field is beta-level and available on clusters that haven't disabled the EphemeralContainers feature gate. | [optional] -**host_aliases** | [**list[V1HostAlias]**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1HostAlias.md) | HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. This is only valid for non-hostNetwork pods. | [optional] +**ephemeral_containers** | [**list[V1EphemeralContainer]**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1EphemeralContainer.md) | List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource. | [optional] +**host_aliases** | [**list[V1HostAlias]**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1HostAlias.md) | HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. | [optional] **host_ipc** | **bool** | Use the host's ipc namespace. Optional: Default to false. | [optional] **host_network** | **bool** | Host networking requested for this pod. Use the host's network namespace. If this option is set, the ports that will be used must be specified. Default to false. | [optional] **host_pid** | **bool** | Use the host's pid namespace. Optional: Default to false. | [optional] **host_users** | **bool** | Use the host's user namespace. Optional: Default to true. If set to true or not present, the pod will be run in the host user namespace, useful for when the pod needs a feature only available to the host user namespace, such as loading a kernel module with CAP_SYS_MODULE. When set to false, a new userns is created for the pod. Setting false is useful for mitigating container breakout vulnerabilities even allowing users to run their containers as root without actually having root privileges on the host. This field is alpha-level and is only honored by servers that enable the UserNamespacesSupport feature. | [optional] **hostname** | **str** | Specifies the hostname of the Pod If not specified, the pod's hostname will be set to a system-defined value. | [optional] -**image_pull_secrets** | [**list[V1LocalObjectReference]**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1LocalObjectReference.md) | ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. For example, in the case of docker, only DockerConfig type secrets are honored. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod | [optional] -**init_containers** | [**list[V1Container]**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1Container.md) | List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ | [optional] +**image_pull_secrets** | [**list[V1LocalObjectReference]**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1LocalObjectReference.md) | ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod | [optional] +**init_containers** | [**list[V1Container]**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1Container.md) | List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ | [optional] **labels** | **dict(str, str)** | Labels that will be added to the component pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ | [optional] **logger** | [**V1beta1LoggerSpec**](V1beta1LoggerSpec.md) | | [optional] **max_replicas** | **int** | Maximum number of replicas for autoscaling. | [optional] **min_replicas** | **int** | Minimum number of replicas, defaults to 1 but can be set to 0 to enable scale-to-zero. | [optional] -**node_name** | **str** | NodeName is a request to schedule this pod onto a specific node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits resource requirements. | [optional] +**node_name** | **str** | NodeName indicates in which node this pod is scheduled. If empty, this pod is a candidate for scheduling by the scheduler defined in schedulerName. Once this field is set, the kubelet for this node becomes responsible for the lifecycle of this pod. This field should not be used to express a desire for the pod to be scheduled on a specific node. https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodename | [optional] **node_selector** | **dict(str, str)** | NodeSelector is a selector which must be true for the pod to fit on a node. Selector which must match a node's labels for the pod to be scheduled on that node. More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ | [optional] **os** | [**V1PodOS**](V1PodOS.md) | | [optional] -**overhead** | [**dict(str, ResourceQuantity)**](ResourceQuantity.md) | Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md This field is beta-level as of Kubernetes v1.18, and is only honored by servers that enable the PodOverhead feature. | [optional] -**preemption_policy** | **str** | PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset. This field is beta-level, gated by the NonPreemptingPriority feature-gate. | [optional] +**overhead** | [**dict(str, ResourceQuantity)**](ResourceQuantity.md) | Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md | [optional] +**preemption_policy** | **str** | PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset. | [optional] **priority** | **int** | The priority value. Various system components use this field to find the priority of the pod. When Priority Admission Controller is enabled, it prevents users from setting this field. The admission controller populates this field from PriorityClassName. The higher the value, the higher the priority. | [optional] **priority_class_name** | **str** | If specified, indicates the pod's priority. \"system-node-critical\" and \"system-cluster-critical\" are two special keywords which indicate the highest priorities with the former being the highest priority. Any other name must be defined by creating a PriorityClass object with that name. If not specified, the pod priority will be default or zero if there is no default. | [optional] **readiness_gates** | [**list[V1PodReadinessGate]**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1PodReadinessGate.md) | If specified, all readiness gates will be evaluated for pod readiness. A pod is ready when all its containers are ready AND all conditions specified in the readiness gates have status equal to \"True\" More info: https://git.k8s.io/enhancements/keps/sig-network/580-pod-readiness-gates | [optional] **resource_claims** | [**list[V1PodResourceClaim]**](V1PodResourceClaim.md) | ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name. This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. This field is immutable. | [optional] -**restart_policy** | **str** | Restart policy for all containers within the pod. One of Always, OnFailure, Never. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy | [optional] -**runtime_class_name** | **str** | RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class This is a beta feature as of Kubernetes v1.14. | [optional] +**resources** | [**V1ResourceRequirements**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1ResourceRequirements.md) | | [optional] +**restart_policy** | **str** | Restart policy for all containers within the pod. One of Always, OnFailure, Never. In some contexts, only a subset of those values may be permitted. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy | [optional] +**runtime_class_name** | **str** | RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class | [optional] **scale_metric** | **str** | ScaleMetric defines the scaling metric type watched by autoscaler possible values are concurrency, rps, cpu, memory. concurrency, rps are supported via Knative Pod Autoscaler(https://knative.dev/docs/serving/autoscaling/autoscaling-metrics). | [optional] **scale_target** | **int** | ScaleTarget specifies the integer target value of the metric type the Autoscaler watches for. concurrency and rps targets are supported by Knative Pod Autoscaler (https://knative.dev/docs/serving/autoscaling/autoscaling-targets/). | [optional] **scheduler_name** | **str** | If specified, the pod will be dispatched by specified scheduler. If not specified, the pod will be dispatched by default scheduler. | [optional] -**scheduling_gates** | [**list[V1PodSchedulingGate]**](V1PodSchedulingGate.md) | SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod. SchedulingGates can only be set at pod creation time, and be removed only afterwards. This is a beta feature enabled by the PodSchedulingReadiness feature gate. | [optional] +**scheduling_gates** | [**list[V1PodSchedulingGate]**](V1PodSchedulingGate.md) | SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod. SchedulingGates can only be set at pod creation time, and be removed only afterwards. | [optional] **security_context** | [**V1PodSecurityContext**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1PodSecurityContext.md) | | [optional] -**service_account** | **str** | DeprecatedServiceAccount is a depreciated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead. | [optional] +**service_account** | **str** | DeprecatedServiceAccount is a deprecated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead. | [optional] **service_account_name** | **str** | ServiceAccountName is the name of the ServiceAccount to use to run this pod. More info: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/ | [optional] **set_hostname_as_fqdn** | **bool** | If true the pod's hostname will be configured as the pod's FQDN, rather than the leaf name (the default). In Linux containers, this means setting the FQDN in the hostname field of the kernel (the nodename field of struct utsname). In Windows containers, this means setting the registry value of hostname for the registry key HKEY_LOCAL_MACHINE\\\\SYSTEM\\\\CurrentControlSet\\\\Services\\\\Tcpip\\\\Parameters to FQDN. If a pod does not have FQDN, this has no effect. Default to false. | [optional] **share_process_namespace** | **bool** | Share a single process namespace between all of the containers in a pod. When this is set containers will be able to view and signal processes from other containers in the same pod, and the first process in each container will not be assigned PID 1. HostPID and ShareProcessNamespace cannot both be set. Optional: Default to false. | [optional] diff --git a/python/kserve/docs/V1beta1WorkerSpec.md b/python/kserve/docs/V1beta1WorkerSpec.md index cd341cd7f20..587b3ea8cd2 100644 --- a/python/kserve/docs/V1beta1WorkerSpec.md +++ b/python/kserve/docs/V1beta1WorkerSpec.md @@ -10,31 +10,32 @@ Name | Type | Description | Notes **dns_config** | [**V1PodDNSConfig**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1PodDNSConfig.md) | | [optional] **dns_policy** | **str** | Set DNS policy for the pod. Defaults to \"ClusterFirst\". Valid values are 'ClusterFirstWithHostNet', 'ClusterFirst', 'Default' or 'None'. DNS parameters given in DNSConfig will be merged with the policy selected with DNSPolicy. To have DNS options set along with hostNetwork, you have to specify DNS policy explicitly to 'ClusterFirstWithHostNet'. | [optional] **enable_service_links** | **bool** | EnableServiceLinks indicates whether information about services should be injected into pod's environment variables, matching the syntax of Docker links. Optional: Defaults to true. | [optional] -**ephemeral_containers** | [**list[V1EphemeralContainer]**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1EphemeralContainer.md) | List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource. This field is beta-level and available on clusters that haven't disabled the EphemeralContainers feature gate. | [optional] -**host_aliases** | [**list[V1HostAlias]**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1HostAlias.md) | HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. This is only valid for non-hostNetwork pods. | [optional] +**ephemeral_containers** | [**list[V1EphemeralContainer]**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1EphemeralContainer.md) | List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource. | [optional] +**host_aliases** | [**list[V1HostAlias]**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1HostAlias.md) | HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. | [optional] **host_ipc** | **bool** | Use the host's ipc namespace. Optional: Default to false. | [optional] **host_network** | **bool** | Host networking requested for this pod. Use the host's network namespace. If this option is set, the ports that will be used must be specified. Default to false. | [optional] **host_pid** | **bool** | Use the host's pid namespace. Optional: Default to false. | [optional] **host_users** | **bool** | Use the host's user namespace. Optional: Default to true. If set to true or not present, the pod will be run in the host user namespace, useful for when the pod needs a feature only available to the host user namespace, such as loading a kernel module with CAP_SYS_MODULE. When set to false, a new userns is created for the pod. Setting false is useful for mitigating container breakout vulnerabilities even allowing users to run their containers as root without actually having root privileges on the host. This field is alpha-level and is only honored by servers that enable the UserNamespacesSupport feature. | [optional] **hostname** | **str** | Specifies the hostname of the Pod If not specified, the pod's hostname will be set to a system-defined value. | [optional] -**image_pull_secrets** | [**list[V1LocalObjectReference]**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1LocalObjectReference.md) | ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. For example, in the case of docker, only DockerConfig type secrets are honored. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod | [optional] -**init_containers** | [**list[V1Container]**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1Container.md) | List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ | [optional] -**node_name** | **str** | NodeName is a request to schedule this pod onto a specific node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits resource requirements. | [optional] +**image_pull_secrets** | [**list[V1LocalObjectReference]**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1LocalObjectReference.md) | ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod | [optional] +**init_containers** | [**list[V1Container]**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1Container.md) | List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ | [optional] +**node_name** | **str** | NodeName indicates in which node this pod is scheduled. If empty, this pod is a candidate for scheduling by the scheduler defined in schedulerName. Once this field is set, the kubelet for this node becomes responsible for the lifecycle of this pod. This field should not be used to express a desire for the pod to be scheduled on a specific node. https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodename | [optional] **node_selector** | **dict(str, str)** | NodeSelector is a selector which must be true for the pod to fit on a node. Selector which must match a node's labels for the pod to be scheduled on that node. More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ | [optional] **os** | [**V1PodOS**](V1PodOS.md) | | [optional] -**overhead** | [**dict(str, ResourceQuantity)**](ResourceQuantity.md) | Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md This field is beta-level as of Kubernetes v1.18, and is only honored by servers that enable the PodOverhead feature. | [optional] +**overhead** | [**dict(str, ResourceQuantity)**](ResourceQuantity.md) | Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md | [optional] **pipeline_parallel_size** | **int** | PipelineParallelSize defines the number of parallel workers. It also represents the number of replicas in the worker set, where each worker set serves as a scaling unit. | [optional] -**preemption_policy** | **str** | PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset. This field is beta-level, gated by the NonPreemptingPriority feature-gate. | [optional] +**preemption_policy** | **str** | PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset. | [optional] **priority** | **int** | The priority value. Various system components use this field to find the priority of the pod. When Priority Admission Controller is enabled, it prevents users from setting this field. The admission controller populates this field from PriorityClassName. The higher the value, the higher the priority. | [optional] **priority_class_name** | **str** | If specified, indicates the pod's priority. \"system-node-critical\" and \"system-cluster-critical\" are two special keywords which indicate the highest priorities with the former being the highest priority. Any other name must be defined by creating a PriorityClass object with that name. If not specified, the pod priority will be default or zero if there is no default. | [optional] **readiness_gates** | [**list[V1PodReadinessGate]**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1PodReadinessGate.md) | If specified, all readiness gates will be evaluated for pod readiness. A pod is ready when all its containers are ready AND all conditions specified in the readiness gates have status equal to \"True\" More info: https://git.k8s.io/enhancements/keps/sig-network/580-pod-readiness-gates | [optional] **resource_claims** | [**list[V1PodResourceClaim]**](V1PodResourceClaim.md) | ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name. This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. This field is immutable. | [optional] -**restart_policy** | **str** | Restart policy for all containers within the pod. One of Always, OnFailure, Never. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy | [optional] -**runtime_class_name** | **str** | RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class This is a beta feature as of Kubernetes v1.14. | [optional] +**resources** | [**V1ResourceRequirements**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1ResourceRequirements.md) | | [optional] +**restart_policy** | **str** | Restart policy for all containers within the pod. One of Always, OnFailure, Never. In some contexts, only a subset of those values may be permitted. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy | [optional] +**runtime_class_name** | **str** | RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class | [optional] **scheduler_name** | **str** | If specified, the pod will be dispatched by specified scheduler. If not specified, the pod will be dispatched by default scheduler. | [optional] -**scheduling_gates** | [**list[V1PodSchedulingGate]**](V1PodSchedulingGate.md) | SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod. SchedulingGates can only be set at pod creation time, and be removed only afterwards. This is a beta feature enabled by the PodSchedulingReadiness feature gate. | [optional] +**scheduling_gates** | [**list[V1PodSchedulingGate]**](V1PodSchedulingGate.md) | SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod. SchedulingGates can only be set at pod creation time, and be removed only afterwards. | [optional] **security_context** | [**V1PodSecurityContext**](https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1PodSecurityContext.md) | | [optional] -**service_account** | **str** | DeprecatedServiceAccount is a depreciated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead. | [optional] +**service_account** | **str** | DeprecatedServiceAccount is a deprecated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead. | [optional] **service_account_name** | **str** | ServiceAccountName is the name of the ServiceAccount to use to run this pod. More info: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/ | [optional] **set_hostname_as_fqdn** | **bool** | If true the pod's hostname will be configured as the pod's FQDN, rather than the leaf name (the default). In Linux containers, this means setting the FQDN in the hostname field of the kernel (the nodename field of struct utsname). In Windows containers, this means setting the registry value of hostname for the registry key HKEY_LOCAL_MACHINE\\\\SYSTEM\\\\CurrentControlSet\\\\Services\\\\Tcpip\\\\Parameters to FQDN. If a pod does not have FQDN, this has no effect. Default to false. | [optional] **share_process_namespace** | **bool** | Share a single process namespace between all of the containers in a pod. When this is set containers will be able to view and signal processes from other containers in the same pod, and the first process in each container will not be assigned PID 1. HostPID and ShareProcessNamespace cannot both be set. Optional: Default to false. | [optional] diff --git a/python/kserve/kserve/api/kserve_client.py b/python/kserve/kserve/api/kserve_client.py index a36a17e3c3a..5f2a2620dfd 100644 --- a/python/kserve/kserve/api/kserve_client.py +++ b/python/kserve/kserve/api/kserve_client.py @@ -13,11 +13,14 @@ # limitations under the License. import time +from typing import List from urllib.parse import urlparse import requests from kubernetes import client, config +from ..models.v1alpha1_local_model_cache import V1alpha1LocalModelCache +from ..models.v1alpha1_local_model_node_group import V1alpha1LocalModelNodeGroup from .creds_utils import set_gcs_credentials, set_s3_credentials, set_azure_credentials from .watch import isvc_watch from ..constants import constants @@ -141,7 +144,7 @@ def create( constants.KSERVE_GROUP, version, namespace, - constants.KSERVE_PLURAL, + constants.KSERVE_PLURAL_INFERENCESERVICE, inferenceservice, ) except client.rest.ApiException as e: @@ -192,7 +195,7 @@ def get( constants.KSERVE_GROUP, version, namespace, - constants.KSERVE_PLURAL, + constants.KSERVE_PLURAL_INFERENCESERVICE, name, ) except client.rest.ApiException as e: @@ -210,7 +213,7 @@ def get( constants.KSERVE_GROUP, version, namespace, - constants.KSERVE_PLURAL, + constants.KSERVE_PLURAL_INFERENCESERVICE, ) except client.rest.ApiException as e: raise RuntimeError( @@ -241,7 +244,7 @@ def patch( constants.KSERVE_GROUP, version, namespace, - constants.KSERVE_PLURAL, + constants.KSERVE_PLURAL_INFERENCESERVICE, name, inferenceservice, ) @@ -291,7 +294,7 @@ def replace( constants.KSERVE_GROUP, version, namespace, - constants.KSERVE_PLURAL, + constants.KSERVE_PLURAL_INFERENCESERVICE, name, inferenceservice, ) @@ -328,7 +331,7 @@ def delete(self, name, namespace=None, version=constants.KSERVE_V1BETA1_VERSION) constants.KSERVE_GROUP, version, namespace, - constants.KSERVE_PLURAL, + constants.KSERVE_PLURAL_INFERENCESERVICE, name, ) except client.rest.ApiException as e: @@ -646,3 +649,186 @@ def wait_ig_ready( name, current_ig ) ) + + def create_local_model_node_group( + self, localmodelnodegroup: V1alpha1LocalModelNodeGroup + ): + """ + Create a local model node group + + :param localmodelnodegroup: local model node group object + :return: created local model node group object + """ + version = localmodelnodegroup.api_version.split("/")[1] + + try: + output = self.api_instance.create_cluster_custom_object( + constants.KSERVE_GROUP, + version, + constants.KSERVE_PLURAL_LOCALMODELNODEGROUP, + localmodelnodegroup, + ) + except client.rest.ApiException as e: + raise RuntimeError( + f"Exception when calling CustomObjectsApi->create_cluster_custom_object:{e}%s\n" + ) from e + return output + + def get_local_model_node_group( + self, name: str, version: str = constants.KSERVE_V1ALPHA1_VERSION + ) -> object: + """ + Get the local model node group + + :param name: existing local model node group name + :param version: api group version. Default to v1alpha + :return: local model node group object + """ + try: + return self.api_instance.get_cluster_custom_object( + constants.KSERVE_GROUP, + version, + constants.KSERVE_PLURAL_LOCALMODELNODEGROUP, + name, + ) + except client.rest.ApiException as e: + raise RuntimeError( + f"Exception when calling CustomObjectsApi->get_cluster_custom_object:{e}%s\n" + ) from e + + def delete_local_model_node_group( + self, name: str, version: str = constants.KSERVE_V1ALPHA1_VERSION + ): + """ + Delete the local model node group + + :param name: local model node group name + :param version: api group version. Default to v1alpha + """ + try: + self.api_instance.delete_cluster_custom_object( + constants.KSERVE_GROUP, + version, + constants.KSERVE_PLURAL_LOCALMODELNODEGROUP, + name, + ) + except client.rest.ApiException as e: + raise RuntimeError( + f"Exception when calling CustomObjectsApi->delete_cluster_custom_object:{e}%s\n" + ) from e + + def create_local_model_cache( + self, localmodelcache: V1alpha1LocalModelCache + ) -> object: + """ + Create a local model cache + + :param localmodelcache: local model cache object + :return: created local model cache object + """ + version = localmodelcache.api_version.split("/")[1] + + try: + output = self.api_instance.create_cluster_custom_object( + constants.KSERVE_GROUP, + version, + constants.KSERVE_PLURAL_LOCALMODELCACHE, + localmodelcache, + ) + except client.rest.ApiException as e: + raise RuntimeError( + f"Exception when calling CustomObjectsApi->create_cluster_custom_object:{e}%s\n" + ) from e + return output + + def get_local_model_cache( + self, name: str, version: str = constants.KSERVE_V1ALPHA1_VERSION + ) -> object: + """ + Get the local model cache + + :param name: existing local model cache name + :param version: api group version. Default to v1alpha1 + :return: local model cache object + """ + try: + return self.api_instance.get_cluster_custom_object( + constants.KSERVE_GROUP, + version, + constants.KSERVE_PLURAL_LOCALMODELCACHE, + name, + ) + except client.rest.ApiException as e: + raise RuntimeError( + f"Exception when calling CustomObjectsApi->get_cluster_custom_object:{e}%s\n" + ) from e + + def delete_local_model_cache( + self, name: str, version: str = constants.KSERVE_V1ALPHA1_VERSION + ): + """ + Delete the local model cache + + :param name: local model cache name + :param version: api group version. Default to v1alpha1 + """ + try: + self.api_instance.delete_cluster_custom_object( + constants.KSERVE_GROUP, + version, + constants.KSERVE_PLURAL_LOCALMODELCACHE, + name, + ) + except client.rest.ApiException as e: + raise RuntimeError( + f"Exception when calling CustomObjectsApi->delete_cluster_custom_object:{e}%s\n" + ) from e + + def is_local_model_cache_ready( + self, + name: str, + nodes: List[str], + version: str = constants.KSERVE_V1ALPHA1_VERSION, + ) -> bool: + """ + Verify if the model is successfully cached on the specified node. + + :param name: local model cache name + :param node_name: name of the node to check if the model is cached + :param version: api group version + :return: true if the model is successfully cached, else false. + """ + local_model_cache = self.get_local_model_cache(name, version=version) + node_status = local_model_cache.get("status", {}).get("nodeStatus", {}) + for node in nodes: + if node_status.get(node, "") != "NodeDownloaded": + return False + return True + + def wait_local_model_cache_ready( + self, + name: str, + nodes: List[str], + version: str = constants.KSERVE_V1ALPHA1_VERSION, + timeout_seconds: int = 600, + polling_interval: int = 10, + ): + """ + Wait for model to be cached locally for specified nodes until timeout. + + :param name: local model cache name + :param nodes: list of node names to check if the model is cached + :param version: api group version + :param timeout_seconds: timeout seconds for waiting, default to 600s. + :param polling_interval: The time interval to poll status + :return: + """ + for _ in range(round(timeout_seconds / polling_interval)): + time.sleep(polling_interval) + if self.is_local_model_cache_ready(name, nodes, version=version): + return + + current_local_model_cache = self.get_local_model_cache(name, version=version) + raise RuntimeError( + f"Timeout while caching the model. The current state of LocalModelCache is: {current_local_model_cache}" + ) diff --git a/python/kserve/kserve/api/watch.py b/python/kserve/kserve/api/watch.py index 6af64e8360b..c73c513dfb5 100644 --- a/python/kserve/kserve/api/watch.py +++ b/python/kserve/kserve/api/watch.py @@ -36,7 +36,7 @@ def isvc_watch(name=None, namespace=None, timeout_seconds=600, generation=0): constants.KSERVE_GROUP, constants.KSERVE_V1BETA1_VERSION, namespace, - constants.KSERVE_PLURAL, + constants.KSERVE_PLURAL_INFERENCESERVICE, timeout_seconds=timeout_seconds, ) diff --git a/python/kserve/kserve/constants/constants.py b/python/kserve/kserve/constants/constants.py index 93c0df857ad..b4ebd77a86e 100644 --- a/python/kserve/kserve/constants/constants.py +++ b/python/kserve/kserve/constants/constants.py @@ -17,12 +17,18 @@ # KServe K8S constants KSERVE_GROUP = "serving.kserve.io" -KSERVE_KIND = "InferenceService" -KSERVE_PLURAL = "inferenceservices" +KSERVE_KIND_INFERENCESERVICE = "InferenceService" +KSERVE_PLURAL_INFERENCESERVICE = "inferenceservices" KSERVE_KIND_TRAINEDMODEL = "TrainedModel" KSERVE_PLURAL_TRAINEDMODEL = "trainedmodels" KSERVE_KIND_INFERENCEGRAPH = "InferenceGraph" KSERVE_PLURAL_INFERENCEGRAPH = "inferencegraphs" +KSERVE_KIND_LOCALMODELNODEGROUP = "LocalModelNodeGroup" +KSERVE_PLURAL_LOCALMODELNODEGROUP = "localmodelnodegroups" +KSERVE_KIND_LOCALMODELCACHE = "LocalModelCache" +KSERVE_PLURAL_LOCALMODELCACHE = "localmodelcaches" +KSERVE_KIND_LOCALMODELNODE = "LocalModelNode" +KSERVE_PLURAL_LOCALMODELNODE = "localmodelnodes" KSERVE_V1BETA1_VERSION = "v1beta1" KSERVE_V1ALPHA1_VERSION = "v1alpha1" diff --git a/python/kserve/kserve/inference_client.py b/python/kserve/kserve/inference_client.py index e2378cc965d..3d9b6d06a6d 100644 --- a/python/kserve/kserve/inference_client.py +++ b/python/kserve/kserve/inference_client.py @@ -426,7 +426,7 @@ def _construct_url( relative_url = "/" + relative_url return base_url.join(base_url.path + relative_url) - def _consturct_http_status_error( + def _construct_http_status_error( self, response: httpx.Response ) -> httpx.HTTPStatusError: message = ( @@ -506,7 +506,7 @@ async def infer( "response code: %s, content: %s", response.status_code, response.text ) if not response.is_success: - raise self._consturct_http_status_error(response) + raise self._construct_http_status_error(response) if response_headers is not None: response_headers.update(response.headers) # If inference graph result, return it as dict @@ -561,7 +561,7 @@ async def explain( "response code: %s, content: %s", response.status_code, response.text ) if not response.is_success: - raise self._consturct_http_status_error(response) + raise self._construct_http_status_error(response) return orjson.loads(response.content) async def is_server_ready( @@ -593,7 +593,7 @@ async def is_server_ready( "response code: %s, content: %s", response.status_code, response.text ) if not response.is_success: - raise self._consturct_http_status_error(response) + raise self._construct_http_status_error(response) return response.json().get("ready") async def is_server_live( @@ -627,7 +627,7 @@ async def is_server_live( "response code: %s, content: %s", response.status_code, response.text ) if not response.is_success: - raise self._consturct_http_status_error(response) + raise self._construct_http_status_error(response) if is_v1(self._config.protocol): is_live = response.json().get("status").lower() == "alive" elif is_v2(self._config.protocol): @@ -678,7 +678,7 @@ async def is_model_ready( return False # Raise for other status codes if not response.is_success: - raise self._consturct_http_status_error(response) + raise self._construct_http_status_error(response) return response.json().get("ready") async def close(self): diff --git a/python/kserve/kserve/model_server.py b/python/kserve/kserve/model_server.py index 9265e14c46e..e2a325ebd52 100644 --- a/python/kserve/kserve/model_server.py +++ b/python/kserve/kserve/model_server.py @@ -36,7 +36,7 @@ from .protocol.dataplane import DataPlane from .protocol.grpc.server import GRPCServer from .protocol.model_repository_extension import ModelRepositoryExtension -from .protocol.rest.server import UvicornServer +from .protocol.rest.server import RESTServer from .utils import utils from .utils.inference_client_factory import InferenceClientFactory @@ -237,15 +237,6 @@ def __init__( self.model_repository_extension = ModelRepositoryExtension( model_registry=self.registered_models ) - self._grpc_server = None - self._rest_server = None - if self.enable_grpc: - self._grpc_server = GRPCServer( - grpc_port, - self.dataplane, - self.model_repository_extension, - kwargs=vars(args), - ) if args.configure_logging: # If the logger does not have any handlers, then the logger is not configured. # For backward compatibility, we configure the logger here. @@ -264,86 +255,77 @@ def __init__( self.dataplane = DataPlane( model_registry=self.registered_models, predictor_config=predictor_config ) - - async def _serve_rest(self): - logger.info(f"Starting uvicorn with {self.workers} workers") - loop = asyncio.get_event_loop() - if sys.platform not in ["win32", "win64"]: - sig_list = [signal.SIGINT, signal.SIGTERM, signal.SIGQUIT] - else: - sig_list = [signal.SIGINT, signal.SIGTERM] - - for sig in sig_list: - loop.add_signal_handler( - sig, lambda s=sig: asyncio.create_task(self.stop(sig=s)) - ) - if self._custom_exception_handler is None: - loop.set_exception_handler(self.default_exception_handler) - else: - loop.set_exception_handler(self._custom_exception_handler) - self._rest_server = UvicornServer( + self._rest_server = self._rest_server = RESTServer( app, - self.http_port, self.dataplane, self.model_repository_extension, + self.http_port, # By setting log_config to None we tell Uvicorn not to configure logging as it is already # configured by kserve. log_config=None, access_log_format=self.access_log_format, workers=self.workers, ) - await self._rest_server.run() - - def start(self, models: List[BaseKServeModel]) -> None: - """Start the model server with a set of registered models. + self._grpc_server = None + if self.enable_grpc: + self._grpc_server = GRPCServer( + grpc_port, + self.dataplane, + self.model_repository_extension, + kwargs=vars(args), + ) - Args: - models: a list of models to register to the model server. - """ - if isinstance(models, list): - at_least_one_model_ready = False - for model in models: - if isinstance(model, BaseKServeModel): - if model.ready: - at_least_one_model_ready = True - self.register_model(model) - # pass whether to log request latency into the model - model.enable_latency_logging = self.enable_latency_logging - model.start() - else: - raise RuntimeError("Model type should be 'BaseKServeModel'") - if not at_least_one_model_ready and models: - raise NoModelReady(models) + def setup_event_loop(self): + loop = asyncio.get_event_loop() + if self._custom_exception_handler is None: + loop.set_exception_handler(self.default_exception_handler) else: - raise RuntimeError("Unknown model collection type") + loop.set_exception_handler(self._custom_exception_handler) if self.max_asyncio_workers is None: # formula as suggest in https://bugs.python.org/issue35279 self.max_asyncio_workers = min(32, utils.cpu_count() + 4) logger.info(f"Setting max asyncio worker threads as {self.max_asyncio_workers}") - asyncio.get_event_loop().set_default_executor( + loop.set_default_executor( concurrent.futures.ThreadPoolExecutor(max_workers=self.max_asyncio_workers) ) - async def servers_task(): - servers = [self._serve_rest()] - if self.enable_grpc: - servers.append(self._grpc_server.start(self.max_threads)) - await asyncio.gather(*servers) + def register_signal_handler(self): + if sys.platform == "win32": + sig_list = [signal.SIGINT, signal.SIGTERM, signal.SIGBREAK] + else: + sig_list = [signal.SIGINT, signal.SIGTERM, signal.SIGQUIT] + + for sig in sig_list: + signal.signal( + sig, lambda sig, frame: asyncio.create_task(self.stop(sig=sig)) + ) + + async def _servers_task(self): + servers = [self._rest_server.start()] + if self.enable_grpc: + servers.append(self._grpc_server.start(self.max_threads)) + await asyncio.gather(*servers) + + def start(self, models: List[BaseKServeModel]) -> None: + """Start the model server with a set of registered models. - asyncio.run(servers_task()) + Args: + models: a list of models to register to the model server. + """ + self._check_atleast_one_model_is_ready(models) + self.setup_event_loop() + self.register_signal_handler() + asyncio.run(self._servers_task()) - async def stop(self, sig: Optional[int] = None): + async def stop(self, sig: int): """Stop the instances of REST and gRPC model servers. Args: - sig: The signal to stop the server. Default: ``None``. + sig: The signal to stop the server. """ await InferenceClientFactory().close() logger.info("Stopping the model server") - if self._rest_server: - logger.info("Stopping the rest server") - await self._rest_server.stop() if self._grpc_server: logger.info("Stopping the grpc server") await self._grpc_server.stop(sig) @@ -387,3 +369,21 @@ def register_model(self, model: BaseKServeModel): raise Exception("Failed to register model, model.name must be provided.") self.registered_models.update(model) logger.info("Registering model: %s", model.name) + + def _check_atleast_one_model_is_ready(self, models: List[BaseKServeModel]): + if isinstance(models, list): + at_least_one_model_ready = False + for model in models: + if isinstance(model, BaseKServeModel): + if model.ready: + at_least_one_model_ready = True + self.register_model(model) + # pass whether to log request latency into the model + model.enable_latency_logging = self.enable_latency_logging + model.start() + else: + raise RuntimeError("Model type should be 'BaseKServeModel'") + if not at_least_one_model_ready and models: + raise NoModelReady(models) + else: + raise RuntimeError("Unknown model collection type") diff --git a/python/kserve/kserve/models/__init__.py b/python/kserve/kserve/models/__init__.py index d46daa25249..452fe71cddf 100644 --- a/python/kserve/kserve/models/__init__.py +++ b/python/kserve/kserve/models/__init__.py @@ -95,6 +95,7 @@ from kserve.models.v1beta1_pod_spec import V1beta1PodSpec from kserve.models.v1beta1_predictor_extension_spec import V1beta1PredictorExtensionSpec from kserve.models.v1beta1_predictor_spec import V1beta1PredictorSpec +from kserve.models.v1beta1_resource_config import V1beta1ResourceConfig from kserve.models.v1beta1_sk_learn_spec import V1beta1SKLearnSpec from kserve.models.v1beta1_security_config import V1beta1SecurityConfig from kserve.models.v1beta1_service_config import V1beta1ServiceConfig diff --git a/python/kserve/kserve/models/v1beta1_custom_explainer.py b/python/kserve/kserve/models/v1beta1_custom_explainer.py index e280e7207d7..655e6b8b408 100644 --- a/python/kserve/kserve/models/v1beta1_custom_explainer.py +++ b/python/kserve/kserve/models/v1beta1_custom_explainer.py @@ -72,6 +72,7 @@ class V1beta1CustomExplainer(object): 'priority_class_name': 'str', 'readiness_gates': 'list[V1PodReadinessGate]', 'resource_claims': 'list[V1PodResourceClaim]', + 'resources': 'V1ResourceRequirements', 'restart_policy': 'str', 'runtime_class_name': 'str', 'scheduler_name': 'str', @@ -114,6 +115,7 @@ class V1beta1CustomExplainer(object): 'priority_class_name': 'priorityClassName', 'readiness_gates': 'readinessGates', 'resource_claims': 'resourceClaims', + 'resources': 'resources', 'restart_policy': 'restartPolicy', 'runtime_class_name': 'runtimeClassName', 'scheduler_name': 'schedulerName', @@ -130,7 +132,7 @@ class V1beta1CustomExplainer(object): 'volumes': 'volumes' } - def __init__(self, active_deadline_seconds=None, affinity=None, automount_service_account_token=None, containers=None, dns_config=None, dns_policy=None, enable_service_links=None, ephemeral_containers=None, host_aliases=None, host_ipc=None, host_network=None, host_pid=None, host_users=None, hostname=None, image_pull_secrets=None, init_containers=None, node_name=None, node_selector=None, os=None, overhead=None, preemption_policy=None, priority=None, priority_class_name=None, readiness_gates=None, resource_claims=None, restart_policy=None, runtime_class_name=None, scheduler_name=None, scheduling_gates=None, security_context=None, service_account=None, service_account_name=None, set_hostname_as_fqdn=None, share_process_namespace=None, subdomain=None, termination_grace_period_seconds=None, tolerations=None, topology_spread_constraints=None, volumes=None, local_vars_configuration=None): # noqa: E501 + def __init__(self, active_deadline_seconds=None, affinity=None, automount_service_account_token=None, containers=None, dns_config=None, dns_policy=None, enable_service_links=None, ephemeral_containers=None, host_aliases=None, host_ipc=None, host_network=None, host_pid=None, host_users=None, hostname=None, image_pull_secrets=None, init_containers=None, node_name=None, node_selector=None, os=None, overhead=None, preemption_policy=None, priority=None, priority_class_name=None, readiness_gates=None, resource_claims=None, resources=None, restart_policy=None, runtime_class_name=None, scheduler_name=None, scheduling_gates=None, security_context=None, service_account=None, service_account_name=None, set_hostname_as_fqdn=None, share_process_namespace=None, subdomain=None, termination_grace_period_seconds=None, tolerations=None, topology_spread_constraints=None, volumes=None, local_vars_configuration=None): # noqa: E501 """V1beta1CustomExplainer - a model defined in OpenAPI""" # noqa: E501 if local_vars_configuration is None: local_vars_configuration = Configuration() @@ -161,6 +163,7 @@ def __init__(self, active_deadline_seconds=None, affinity=None, automount_servic self._priority_class_name = None self._readiness_gates = None self._resource_claims = None + self._resources = None self._restart_policy = None self._runtime_class_name = None self._scheduler_name = None @@ -226,6 +229,8 @@ def __init__(self, active_deadline_seconds=None, affinity=None, automount_servic self.readiness_gates = readiness_gates if resource_claims is not None: self.resource_claims = resource_claims + if resources is not None: + self.resources = resources if restart_policy is not None: self.restart_policy = restart_policy if runtime_class_name is not None: @@ -625,7 +630,7 @@ def init_containers(self, init_containers): def node_name(self): """Gets the node_name of this V1beta1CustomExplainer. # noqa: E501 - NodeName is a request to schedule this pod onto a specific node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits resource requirements. # noqa: E501 + NodeName indicates in which node this pod is scheduled. If empty, this pod is a candidate for scheduling by the scheduler defined in schedulerName. Once this field is set, the kubelet for this node becomes responsible for the lifecycle of this pod. This field should not be used to express a desire for the pod to be scheduled on a specific node. https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodename # noqa: E501 :return: The node_name of this V1beta1CustomExplainer. # noqa: E501 :rtype: str @@ -636,7 +641,7 @@ def node_name(self): def node_name(self, node_name): """Sets the node_name of this V1beta1CustomExplainer. - NodeName is a request to schedule this pod onto a specific node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits resource requirements. # noqa: E501 + NodeName indicates in which node this pod is scheduled. If empty, this pod is a candidate for scheduling by the scheduler defined in schedulerName. Once this field is set, the kubelet for this node becomes responsible for the lifecycle of this pod. This field should not be used to express a desire for the pod to be scheduled on a specific node. https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodename # noqa: E501 :param node_name: The node_name of this V1beta1CustomExplainer. # noqa: E501 :type: str @@ -826,6 +831,27 @@ def resource_claims(self, resource_claims): self._resource_claims = resource_claims + @property + def resources(self): + """Gets the resources of this V1beta1CustomExplainer. # noqa: E501 + + + :return: The resources of this V1beta1CustomExplainer. # noqa: E501 + :rtype: V1ResourceRequirements + """ + return self._resources + + @resources.setter + def resources(self, resources): + """Sets the resources of this V1beta1CustomExplainer. + + + :param resources: The resources of this V1beta1CustomExplainer. # noqa: E501 + :type: V1ResourceRequirements + """ + + self._resources = resources + @property def restart_policy(self): """Gets the restart_policy of this V1beta1CustomExplainer. # noqa: E501 diff --git a/python/kserve/kserve/models/v1beta1_custom_predictor.py b/python/kserve/kserve/models/v1beta1_custom_predictor.py index 403cb4d8247..f17c9cfae27 100644 --- a/python/kserve/kserve/models/v1beta1_custom_predictor.py +++ b/python/kserve/kserve/models/v1beta1_custom_predictor.py @@ -72,6 +72,7 @@ class V1beta1CustomPredictor(object): 'priority_class_name': 'str', 'readiness_gates': 'list[V1PodReadinessGate]', 'resource_claims': 'list[V1PodResourceClaim]', + 'resources': 'V1ResourceRequirements', 'restart_policy': 'str', 'runtime_class_name': 'str', 'scheduler_name': 'str', @@ -114,6 +115,7 @@ class V1beta1CustomPredictor(object): 'priority_class_name': 'priorityClassName', 'readiness_gates': 'readinessGates', 'resource_claims': 'resourceClaims', + 'resources': 'resources', 'restart_policy': 'restartPolicy', 'runtime_class_name': 'runtimeClassName', 'scheduler_name': 'schedulerName', @@ -130,7 +132,7 @@ class V1beta1CustomPredictor(object): 'volumes': 'volumes' } - def __init__(self, active_deadline_seconds=None, affinity=None, automount_service_account_token=None, containers=None, dns_config=None, dns_policy=None, enable_service_links=None, ephemeral_containers=None, host_aliases=None, host_ipc=None, host_network=None, host_pid=None, host_users=None, hostname=None, image_pull_secrets=None, init_containers=None, node_name=None, node_selector=None, os=None, overhead=None, preemption_policy=None, priority=None, priority_class_name=None, readiness_gates=None, resource_claims=None, restart_policy=None, runtime_class_name=None, scheduler_name=None, scheduling_gates=None, security_context=None, service_account=None, service_account_name=None, set_hostname_as_fqdn=None, share_process_namespace=None, subdomain=None, termination_grace_period_seconds=None, tolerations=None, topology_spread_constraints=None, volumes=None, local_vars_configuration=None): # noqa: E501 + def __init__(self, active_deadline_seconds=None, affinity=None, automount_service_account_token=None, containers=None, dns_config=None, dns_policy=None, enable_service_links=None, ephemeral_containers=None, host_aliases=None, host_ipc=None, host_network=None, host_pid=None, host_users=None, hostname=None, image_pull_secrets=None, init_containers=None, node_name=None, node_selector=None, os=None, overhead=None, preemption_policy=None, priority=None, priority_class_name=None, readiness_gates=None, resource_claims=None, resources=None, restart_policy=None, runtime_class_name=None, scheduler_name=None, scheduling_gates=None, security_context=None, service_account=None, service_account_name=None, set_hostname_as_fqdn=None, share_process_namespace=None, subdomain=None, termination_grace_period_seconds=None, tolerations=None, topology_spread_constraints=None, volumes=None, local_vars_configuration=None): # noqa: E501 """V1beta1CustomPredictor - a model defined in OpenAPI""" # noqa: E501 if local_vars_configuration is None: local_vars_configuration = Configuration() @@ -161,6 +163,7 @@ def __init__(self, active_deadline_seconds=None, affinity=None, automount_servic self._priority_class_name = None self._readiness_gates = None self._resource_claims = None + self._resources = None self._restart_policy = None self._runtime_class_name = None self._scheduler_name = None @@ -226,6 +229,8 @@ def __init__(self, active_deadline_seconds=None, affinity=None, automount_servic self.readiness_gates = readiness_gates if resource_claims is not None: self.resource_claims = resource_claims + if resources is not None: + self.resources = resources if restart_policy is not None: self.restart_policy = restart_policy if runtime_class_name is not None: @@ -625,7 +630,7 @@ def init_containers(self, init_containers): def node_name(self): """Gets the node_name of this V1beta1CustomPredictor. # noqa: E501 - NodeName is a request to schedule this pod onto a specific node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits resource requirements. # noqa: E501 + NodeName indicates in which node this pod is scheduled. If empty, this pod is a candidate for scheduling by the scheduler defined in schedulerName. Once this field is set, the kubelet for this node becomes responsible for the lifecycle of this pod. This field should not be used to express a desire for the pod to be scheduled on a specific node. https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodename # noqa: E501 :return: The node_name of this V1beta1CustomPredictor. # noqa: E501 :rtype: str @@ -636,7 +641,7 @@ def node_name(self): def node_name(self, node_name): """Sets the node_name of this V1beta1CustomPredictor. - NodeName is a request to schedule this pod onto a specific node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits resource requirements. # noqa: E501 + NodeName indicates in which node this pod is scheduled. If empty, this pod is a candidate for scheduling by the scheduler defined in schedulerName. Once this field is set, the kubelet for this node becomes responsible for the lifecycle of this pod. This field should not be used to express a desire for the pod to be scheduled on a specific node. https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodename # noqa: E501 :param node_name: The node_name of this V1beta1CustomPredictor. # noqa: E501 :type: str @@ -826,6 +831,27 @@ def resource_claims(self, resource_claims): self._resource_claims = resource_claims + @property + def resources(self): + """Gets the resources of this V1beta1CustomPredictor. # noqa: E501 + + + :return: The resources of this V1beta1CustomPredictor. # noqa: E501 + :rtype: V1ResourceRequirements + """ + return self._resources + + @resources.setter + def resources(self, resources): + """Sets the resources of this V1beta1CustomPredictor. + + + :param resources: The resources of this V1beta1CustomPredictor. # noqa: E501 + :type: V1ResourceRequirements + """ + + self._resources = resources + @property def restart_policy(self): """Gets the restart_policy of this V1beta1CustomPredictor. # noqa: E501 diff --git a/python/kserve/kserve/models/v1beta1_custom_transformer.py b/python/kserve/kserve/models/v1beta1_custom_transformer.py index 06312e1d7c9..a9e8473863e 100644 --- a/python/kserve/kserve/models/v1beta1_custom_transformer.py +++ b/python/kserve/kserve/models/v1beta1_custom_transformer.py @@ -72,6 +72,7 @@ class V1beta1CustomTransformer(object): 'priority_class_name': 'str', 'readiness_gates': 'list[V1PodReadinessGate]', 'resource_claims': 'list[V1PodResourceClaim]', + 'resources': 'V1ResourceRequirements', 'restart_policy': 'str', 'runtime_class_name': 'str', 'scheduler_name': 'str', @@ -114,6 +115,7 @@ class V1beta1CustomTransformer(object): 'priority_class_name': 'priorityClassName', 'readiness_gates': 'readinessGates', 'resource_claims': 'resourceClaims', + 'resources': 'resources', 'restart_policy': 'restartPolicy', 'runtime_class_name': 'runtimeClassName', 'scheduler_name': 'schedulerName', @@ -130,7 +132,7 @@ class V1beta1CustomTransformer(object): 'volumes': 'volumes' } - def __init__(self, active_deadline_seconds=None, affinity=None, automount_service_account_token=None, containers=None, dns_config=None, dns_policy=None, enable_service_links=None, ephemeral_containers=None, host_aliases=None, host_ipc=None, host_network=None, host_pid=None, host_users=None, hostname=None, image_pull_secrets=None, init_containers=None, node_name=None, node_selector=None, os=None, overhead=None, preemption_policy=None, priority=None, priority_class_name=None, readiness_gates=None, resource_claims=None, restart_policy=None, runtime_class_name=None, scheduler_name=None, scheduling_gates=None, security_context=None, service_account=None, service_account_name=None, set_hostname_as_fqdn=None, share_process_namespace=None, subdomain=None, termination_grace_period_seconds=None, tolerations=None, topology_spread_constraints=None, volumes=None, local_vars_configuration=None): # noqa: E501 + def __init__(self, active_deadline_seconds=None, affinity=None, automount_service_account_token=None, containers=None, dns_config=None, dns_policy=None, enable_service_links=None, ephemeral_containers=None, host_aliases=None, host_ipc=None, host_network=None, host_pid=None, host_users=None, hostname=None, image_pull_secrets=None, init_containers=None, node_name=None, node_selector=None, os=None, overhead=None, preemption_policy=None, priority=None, priority_class_name=None, readiness_gates=None, resource_claims=None, resources=None, restart_policy=None, runtime_class_name=None, scheduler_name=None, scheduling_gates=None, security_context=None, service_account=None, service_account_name=None, set_hostname_as_fqdn=None, share_process_namespace=None, subdomain=None, termination_grace_period_seconds=None, tolerations=None, topology_spread_constraints=None, volumes=None, local_vars_configuration=None): # noqa: E501 """V1beta1CustomTransformer - a model defined in OpenAPI""" # noqa: E501 if local_vars_configuration is None: local_vars_configuration = Configuration() @@ -161,6 +163,7 @@ def __init__(self, active_deadline_seconds=None, affinity=None, automount_servic self._priority_class_name = None self._readiness_gates = None self._resource_claims = None + self._resources = None self._restart_policy = None self._runtime_class_name = None self._scheduler_name = None @@ -226,6 +229,8 @@ def __init__(self, active_deadline_seconds=None, affinity=None, automount_servic self.readiness_gates = readiness_gates if resource_claims is not None: self.resource_claims = resource_claims + if resources is not None: + self.resources = resources if restart_policy is not None: self.restart_policy = restart_policy if runtime_class_name is not None: @@ -625,7 +630,7 @@ def init_containers(self, init_containers): def node_name(self): """Gets the node_name of this V1beta1CustomTransformer. # noqa: E501 - NodeName is a request to schedule this pod onto a specific node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits resource requirements. # noqa: E501 + NodeName indicates in which node this pod is scheduled. If empty, this pod is a candidate for scheduling by the scheduler defined in schedulerName. Once this field is set, the kubelet for this node becomes responsible for the lifecycle of this pod. This field should not be used to express a desire for the pod to be scheduled on a specific node. https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodename # noqa: E501 :return: The node_name of this V1beta1CustomTransformer. # noqa: E501 :rtype: str @@ -636,7 +641,7 @@ def node_name(self): def node_name(self, node_name): """Sets the node_name of this V1beta1CustomTransformer. - NodeName is a request to schedule this pod onto a specific node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits resource requirements. # noqa: E501 + NodeName indicates in which node this pod is scheduled. If empty, this pod is a candidate for scheduling by the scheduler defined in schedulerName. Once this field is set, the kubelet for this node becomes responsible for the lifecycle of this pod. This field should not be used to express a desire for the pod to be scheduled on a specific node. https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodename # noqa: E501 :param node_name: The node_name of this V1beta1CustomTransformer. # noqa: E501 :type: str @@ -826,6 +831,27 @@ def resource_claims(self, resource_claims): self._resource_claims = resource_claims + @property + def resources(self): + """Gets the resources of this V1beta1CustomTransformer. # noqa: E501 + + + :return: The resources of this V1beta1CustomTransformer. # noqa: E501 + :rtype: V1ResourceRequirements + """ + return self._resources + + @resources.setter + def resources(self, resources): + """Sets the resources of this V1beta1CustomTransformer. + + + :param resources: The resources of this V1beta1CustomTransformer. # noqa: E501 + :type: V1ResourceRequirements + """ + + self._resources = resources + @property def restart_policy(self): """Gets the restart_policy of this V1beta1CustomTransformer. # noqa: E501 diff --git a/python/kserve/kserve/models/v1beta1_explainer_spec.py b/python/kserve/kserve/models/v1beta1_explainer_spec.py index 1ca69b17ff5..d04ed0497fd 100644 --- a/python/kserve/kserve/models/v1beta1_explainer_spec.py +++ b/python/kserve/kserve/models/v1beta1_explainer_spec.py @@ -82,6 +82,7 @@ class V1beta1ExplainerSpec(object): 'priority_class_name': 'str', 'readiness_gates': 'list[V1PodReadinessGate]', 'resource_claims': 'list[V1PodResourceClaim]', + 'resources': 'V1ResourceRequirements', 'restart_policy': 'str', 'runtime_class_name': 'str', 'scale_metric': 'str', @@ -137,6 +138,7 @@ class V1beta1ExplainerSpec(object): 'priority_class_name': 'priorityClassName', 'readiness_gates': 'readinessGates', 'resource_claims': 'resourceClaims', + 'resources': 'resources', 'restart_policy': 'restartPolicy', 'runtime_class_name': 'runtimeClassName', 'scale_metric': 'scaleMetric', @@ -156,7 +158,7 @@ class V1beta1ExplainerSpec(object): 'volumes': 'volumes' } - def __init__(self, active_deadline_seconds=None, affinity=None, annotations=None, art=None, automount_service_account_token=None, batcher=None, canary_traffic_percent=None, container_concurrency=None, containers=None, deployment_strategy=None, dns_config=None, dns_policy=None, enable_service_links=None, ephemeral_containers=None, host_aliases=None, host_ipc=None, host_network=None, host_pid=None, host_users=None, hostname=None, image_pull_secrets=None, init_containers=None, labels=None, logger=None, max_replicas=None, min_replicas=None, node_name=None, node_selector=None, os=None, overhead=None, preemption_policy=None, priority=None, priority_class_name=None, readiness_gates=None, resource_claims=None, restart_policy=None, runtime_class_name=None, scale_metric=None, scale_target=None, scheduler_name=None, scheduling_gates=None, security_context=None, service_account=None, service_account_name=None, set_hostname_as_fqdn=None, share_process_namespace=None, subdomain=None, termination_grace_period_seconds=None, timeout=None, tolerations=None, topology_spread_constraints=None, volumes=None, local_vars_configuration=None): # noqa: E501 + def __init__(self, active_deadline_seconds=None, affinity=None, annotations=None, art=None, automount_service_account_token=None, batcher=None, canary_traffic_percent=None, container_concurrency=None, containers=None, deployment_strategy=None, dns_config=None, dns_policy=None, enable_service_links=None, ephemeral_containers=None, host_aliases=None, host_ipc=None, host_network=None, host_pid=None, host_users=None, hostname=None, image_pull_secrets=None, init_containers=None, labels=None, logger=None, max_replicas=None, min_replicas=None, node_name=None, node_selector=None, os=None, overhead=None, preemption_policy=None, priority=None, priority_class_name=None, readiness_gates=None, resource_claims=None, resources=None, restart_policy=None, runtime_class_name=None, scale_metric=None, scale_target=None, scheduler_name=None, scheduling_gates=None, security_context=None, service_account=None, service_account_name=None, set_hostname_as_fqdn=None, share_process_namespace=None, subdomain=None, termination_grace_period_seconds=None, timeout=None, tolerations=None, topology_spread_constraints=None, volumes=None, local_vars_configuration=None): # noqa: E501 """V1beta1ExplainerSpec - a model defined in OpenAPI""" # noqa: E501 if local_vars_configuration is None: local_vars_configuration = Configuration() @@ -197,6 +199,7 @@ def __init__(self, active_deadline_seconds=None, affinity=None, annotations=None self._priority_class_name = None self._readiness_gates = None self._resource_claims = None + self._resources = None self._restart_policy = None self._runtime_class_name = None self._scale_metric = None @@ -286,6 +289,8 @@ def __init__(self, active_deadline_seconds=None, affinity=None, annotations=None self.readiness_gates = readiness_gates if resource_claims is not None: self.resource_claims = resource_claims + if resources is not None: + self.resources = resources if restart_policy is not None: self.restart_policy = restart_policy if runtime_class_name is not None: @@ -614,7 +619,7 @@ def enable_service_links(self, enable_service_links): def ephemeral_containers(self): """Gets the ephemeral_containers of this V1beta1ExplainerSpec. # noqa: E501 - List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource. This field is beta-level and available on clusters that haven't disabled the EphemeralContainers feature gate. # noqa: E501 + List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource. # noqa: E501 :return: The ephemeral_containers of this V1beta1ExplainerSpec. # noqa: E501 :rtype: list[V1EphemeralContainer] @@ -625,7 +630,7 @@ def ephemeral_containers(self): def ephemeral_containers(self, ephemeral_containers): """Sets the ephemeral_containers of this V1beta1ExplainerSpec. - List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource. This field is beta-level and available on clusters that haven't disabled the EphemeralContainers feature gate. # noqa: E501 + List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource. # noqa: E501 :param ephemeral_containers: The ephemeral_containers of this V1beta1ExplainerSpec. # noqa: E501 :type: list[V1EphemeralContainer] @@ -637,7 +642,7 @@ def ephemeral_containers(self, ephemeral_containers): def host_aliases(self): """Gets the host_aliases of this V1beta1ExplainerSpec. # noqa: E501 - HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. This is only valid for non-hostNetwork pods. # noqa: E501 + HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. # noqa: E501 :return: The host_aliases of this V1beta1ExplainerSpec. # noqa: E501 :rtype: list[V1HostAlias] @@ -648,7 +653,7 @@ def host_aliases(self): def host_aliases(self, host_aliases): """Sets the host_aliases of this V1beta1ExplainerSpec. - HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. This is only valid for non-hostNetwork pods. # noqa: E501 + HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. # noqa: E501 :param host_aliases: The host_aliases of this V1beta1ExplainerSpec. # noqa: E501 :type: list[V1HostAlias] @@ -775,7 +780,7 @@ def hostname(self, hostname): def image_pull_secrets(self): """Gets the image_pull_secrets of this V1beta1ExplainerSpec. # noqa: E501 - ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. For example, in the case of docker, only DockerConfig type secrets are honored. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod # noqa: E501 + ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod # noqa: E501 :return: The image_pull_secrets of this V1beta1ExplainerSpec. # noqa: E501 :rtype: list[V1LocalObjectReference] @@ -786,7 +791,7 @@ def image_pull_secrets(self): def image_pull_secrets(self, image_pull_secrets): """Sets the image_pull_secrets of this V1beta1ExplainerSpec. - ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. For example, in the case of docker, only DockerConfig type secrets are honored. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod # noqa: E501 + ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod # noqa: E501 :param image_pull_secrets: The image_pull_secrets of this V1beta1ExplainerSpec. # noqa: E501 :type: list[V1LocalObjectReference] @@ -798,7 +803,7 @@ def image_pull_secrets(self, image_pull_secrets): def init_containers(self): """Gets the init_containers of this V1beta1ExplainerSpec. # noqa: E501 - List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ # noqa: E501 + List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ # noqa: E501 :return: The init_containers of this V1beta1ExplainerSpec. # noqa: E501 :rtype: list[V1Container] @@ -809,7 +814,7 @@ def init_containers(self): def init_containers(self, init_containers): """Sets the init_containers of this V1beta1ExplainerSpec. - List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ # noqa: E501 + List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ # noqa: E501 :param init_containers: The init_containers of this V1beta1ExplainerSpec. # noqa: E501 :type: list[V1Container] @@ -911,7 +916,7 @@ def min_replicas(self, min_replicas): def node_name(self): """Gets the node_name of this V1beta1ExplainerSpec. # noqa: E501 - NodeName is a request to schedule this pod onto a specific node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits resource requirements. # noqa: E501 + NodeName indicates in which node this pod is scheduled. If empty, this pod is a candidate for scheduling by the scheduler defined in schedulerName. Once this field is set, the kubelet for this node becomes responsible for the lifecycle of this pod. This field should not be used to express a desire for the pod to be scheduled on a specific node. https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodename # noqa: E501 :return: The node_name of this V1beta1ExplainerSpec. # noqa: E501 :rtype: str @@ -922,7 +927,7 @@ def node_name(self): def node_name(self, node_name): """Sets the node_name of this V1beta1ExplainerSpec. - NodeName is a request to schedule this pod onto a specific node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits resource requirements. # noqa: E501 + NodeName indicates in which node this pod is scheduled. If empty, this pod is a candidate for scheduling by the scheduler defined in schedulerName. Once this field is set, the kubelet for this node becomes responsible for the lifecycle of this pod. This field should not be used to express a desire for the pod to be scheduled on a specific node. https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodename # noqa: E501 :param node_name: The node_name of this V1beta1ExplainerSpec. # noqa: E501 :type: str @@ -978,7 +983,7 @@ def os(self, os): def overhead(self): """Gets the overhead of this V1beta1ExplainerSpec. # noqa: E501 - Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md This field is beta-level as of Kubernetes v1.18, and is only honored by servers that enable the PodOverhead feature. # noqa: E501 + Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md # noqa: E501 :return: The overhead of this V1beta1ExplainerSpec. # noqa: E501 :rtype: dict(str, ResourceQuantity) @@ -989,7 +994,7 @@ def overhead(self): def overhead(self, overhead): """Sets the overhead of this V1beta1ExplainerSpec. - Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md This field is beta-level as of Kubernetes v1.18, and is only honored by servers that enable the PodOverhead feature. # noqa: E501 + Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md # noqa: E501 :param overhead: The overhead of this V1beta1ExplainerSpec. # noqa: E501 :type: dict(str, ResourceQuantity) @@ -1001,7 +1006,7 @@ def overhead(self, overhead): def preemption_policy(self): """Gets the preemption_policy of this V1beta1ExplainerSpec. # noqa: E501 - PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset. This field is beta-level, gated by the NonPreemptingPriority feature-gate. # noqa: E501 + PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset. # noqa: E501 :return: The preemption_policy of this V1beta1ExplainerSpec. # noqa: E501 :rtype: str @@ -1012,7 +1017,7 @@ def preemption_policy(self): def preemption_policy(self, preemption_policy): """Sets the preemption_policy of this V1beta1ExplainerSpec. - PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset. This field is beta-level, gated by the NonPreemptingPriority feature-gate. # noqa: E501 + PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset. # noqa: E501 :param preemption_policy: The preemption_policy of this V1beta1ExplainerSpec. # noqa: E501 :type: str @@ -1112,11 +1117,32 @@ def resource_claims(self, resource_claims): self._resource_claims = resource_claims + @property + def resources(self): + """Gets the resources of this V1beta1ExplainerSpec. # noqa: E501 + + + :return: The resources of this V1beta1ExplainerSpec. # noqa: E501 + :rtype: V1ResourceRequirements + """ + return self._resources + + @resources.setter + def resources(self, resources): + """Sets the resources of this V1beta1ExplainerSpec. + + + :param resources: The resources of this V1beta1ExplainerSpec. # noqa: E501 + :type: V1ResourceRequirements + """ + + self._resources = resources + @property def restart_policy(self): """Gets the restart_policy of this V1beta1ExplainerSpec. # noqa: E501 - Restart policy for all containers within the pod. One of Always, OnFailure, Never. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy # noqa: E501 + Restart policy for all containers within the pod. One of Always, OnFailure, Never. In some contexts, only a subset of those values may be permitted. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy # noqa: E501 :return: The restart_policy of this V1beta1ExplainerSpec. # noqa: E501 :rtype: str @@ -1127,7 +1153,7 @@ def restart_policy(self): def restart_policy(self, restart_policy): """Sets the restart_policy of this V1beta1ExplainerSpec. - Restart policy for all containers within the pod. One of Always, OnFailure, Never. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy # noqa: E501 + Restart policy for all containers within the pod. One of Always, OnFailure, Never. In some contexts, only a subset of those values may be permitted. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy # noqa: E501 :param restart_policy: The restart_policy of this V1beta1ExplainerSpec. # noqa: E501 :type: str @@ -1139,7 +1165,7 @@ def restart_policy(self, restart_policy): def runtime_class_name(self): """Gets the runtime_class_name of this V1beta1ExplainerSpec. # noqa: E501 - RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class This is a beta feature as of Kubernetes v1.14. # noqa: E501 + RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class # noqa: E501 :return: The runtime_class_name of this V1beta1ExplainerSpec. # noqa: E501 :rtype: str @@ -1150,7 +1176,7 @@ def runtime_class_name(self): def runtime_class_name(self, runtime_class_name): """Sets the runtime_class_name of this V1beta1ExplainerSpec. - RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class This is a beta feature as of Kubernetes v1.14. # noqa: E501 + RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class # noqa: E501 :param runtime_class_name: The runtime_class_name of this V1beta1ExplainerSpec. # noqa: E501 :type: str @@ -1231,7 +1257,7 @@ def scheduler_name(self, scheduler_name): def scheduling_gates(self): """Gets the scheduling_gates of this V1beta1ExplainerSpec. # noqa: E501 - SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod. SchedulingGates can only be set at pod creation time, and be removed only afterwards. This is a beta feature enabled by the PodSchedulingReadiness feature gate. # noqa: E501 + SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod. SchedulingGates can only be set at pod creation time, and be removed only afterwards. # noqa: E501 :return: The scheduling_gates of this V1beta1ExplainerSpec. # noqa: E501 :rtype: list[V1PodSchedulingGate] @@ -1242,7 +1268,7 @@ def scheduling_gates(self): def scheduling_gates(self, scheduling_gates): """Sets the scheduling_gates of this V1beta1ExplainerSpec. - SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod. SchedulingGates can only be set at pod creation time, and be removed only afterwards. This is a beta feature enabled by the PodSchedulingReadiness feature gate. # noqa: E501 + SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod. SchedulingGates can only be set at pod creation time, and be removed only afterwards. # noqa: E501 :param scheduling_gates: The scheduling_gates of this V1beta1ExplainerSpec. # noqa: E501 :type: list[V1PodSchedulingGate] @@ -1275,7 +1301,7 @@ def security_context(self, security_context): def service_account(self): """Gets the service_account of this V1beta1ExplainerSpec. # noqa: E501 - DeprecatedServiceAccount is a depreciated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead. # noqa: E501 + DeprecatedServiceAccount is a deprecated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead. # noqa: E501 :return: The service_account of this V1beta1ExplainerSpec. # noqa: E501 :rtype: str @@ -1286,7 +1312,7 @@ def service_account(self): def service_account(self, service_account): """Sets the service_account of this V1beta1ExplainerSpec. - DeprecatedServiceAccount is a depreciated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead. # noqa: E501 + DeprecatedServiceAccount is a deprecated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead. # noqa: E501 :param service_account: The service_account of this V1beta1ExplainerSpec. # noqa: E501 :type: str diff --git a/python/kserve/kserve/models/v1beta1_inference_services_config.py b/python/kserve/kserve/models/v1beta1_inference_services_config.py index 54cba14b5c2..bcf31f2d5c8 100644 --- a/python/kserve/kserve/models/v1beta1_inference_services_config.py +++ b/python/kserve/kserve/models/v1beta1_inference_services_config.py @@ -47,23 +47,38 @@ class V1beta1InferenceServicesConfig(object): and the value is json key in definition. """ openapi_types = { - 'explainers': 'V1beta1ExplainersConfig' + 'explainers': 'V1beta1ExplainersConfig', + 'resource': 'V1beta1ResourceConfig', + 'service_annotation_disallowed_list': 'list[str]', + 'service_label_disallowed_list': 'list[str]' } attribute_map = { - 'explainers': 'explainers' + 'explainers': 'explainers', + 'resource': 'resource', + 'service_annotation_disallowed_list': 'serviceAnnotationDisallowedList', + 'service_label_disallowed_list': 'serviceLabelDisallowedList' } - def __init__(self, explainers=None, local_vars_configuration=None): # noqa: E501 + def __init__(self, explainers=None, resource=None, service_annotation_disallowed_list=None, service_label_disallowed_list=None, local_vars_configuration=None): # noqa: E501 """V1beta1InferenceServicesConfig - a model defined in OpenAPI""" # noqa: E501 if local_vars_configuration is None: local_vars_configuration = Configuration() self.local_vars_configuration = local_vars_configuration self._explainers = None + self._resource = None + self._service_annotation_disallowed_list = None + self._service_label_disallowed_list = None self.discriminator = None self.explainers = explainers + if resource is not None: + self.resource = resource + if service_annotation_disallowed_list is not None: + self.service_annotation_disallowed_list = service_annotation_disallowed_list + if service_label_disallowed_list is not None: + self.service_label_disallowed_list = service_label_disallowed_list @property def explainers(self): @@ -88,6 +103,73 @@ def explainers(self, explainers): self._explainers = explainers + @property + def resource(self): + """Gets the resource of this V1beta1InferenceServicesConfig. # noqa: E501 + + + :return: The resource of this V1beta1InferenceServicesConfig. # noqa: E501 + :rtype: V1beta1ResourceConfig + """ + return self._resource + + @resource.setter + def resource(self, resource): + """Sets the resource of this V1beta1InferenceServicesConfig. + + + :param resource: The resource of this V1beta1InferenceServicesConfig. # noqa: E501 + :type: V1beta1ResourceConfig + """ + + self._resource = resource + + @property + def service_annotation_disallowed_list(self): + """Gets the service_annotation_disallowed_list of this V1beta1InferenceServicesConfig. # noqa: E501 + + ServiceAnnotationDisallowedList is a list of annotations that are not allowed to be propagated to Knative revisions # noqa: E501 + + :return: The service_annotation_disallowed_list of this V1beta1InferenceServicesConfig. # noqa: E501 + :rtype: list[str] + """ + return self._service_annotation_disallowed_list + + @service_annotation_disallowed_list.setter + def service_annotation_disallowed_list(self, service_annotation_disallowed_list): + """Sets the service_annotation_disallowed_list of this V1beta1InferenceServicesConfig. + + ServiceAnnotationDisallowedList is a list of annotations that are not allowed to be propagated to Knative revisions # noqa: E501 + + :param service_annotation_disallowed_list: The service_annotation_disallowed_list of this V1beta1InferenceServicesConfig. # noqa: E501 + :type: list[str] + """ + + self._service_annotation_disallowed_list = service_annotation_disallowed_list + + @property + def service_label_disallowed_list(self): + """Gets the service_label_disallowed_list of this V1beta1InferenceServicesConfig. # noqa: E501 + + ServiceLabelDisallowedList is a list of labels that are not allowed to be propagated to Knative revisions # noqa: E501 + + :return: The service_label_disallowed_list of this V1beta1InferenceServicesConfig. # noqa: E501 + :rtype: list[str] + """ + return self._service_label_disallowed_list + + @service_label_disallowed_list.setter + def service_label_disallowed_list(self, service_label_disallowed_list): + """Sets the service_label_disallowed_list of this V1beta1InferenceServicesConfig. + + ServiceLabelDisallowedList is a list of labels that are not allowed to be propagated to Knative revisions # noqa: E501 + + :param service_label_disallowed_list: The service_label_disallowed_list of this V1beta1InferenceServicesConfig. # noqa: E501 + :type: list[str] + """ + + self._service_label_disallowed_list = service_label_disallowed_list + def to_dict(self): """Returns the model properties as a dict""" result = {} diff --git a/python/kserve/kserve/models/v1beta1_ingress_config.py b/python/kserve/kserve/models/v1beta1_ingress_config.py index 29888830ac9..4f7049721fc 100644 --- a/python/kserve/kserve/models/v1beta1_ingress_config.py +++ b/python/kserve/kserve/models/v1beta1_ingress_config.py @@ -51,10 +51,12 @@ class V1beta1IngressConfig(object): 'disable_ingress_creation': 'bool', 'disable_istio_virtual_host': 'bool', 'domain_template': 'str', + 'enable_gateway_api': 'bool', 'ingress_class_name': 'str', 'ingress_domain': 'str', 'ingress_gateway': 'str', 'knative_local_gateway_service': 'str', + 'kserve_ingress_gateway': 'str', 'local_gateway': 'str', 'local_gateway_service': 'str', 'path_template': 'str', @@ -66,17 +68,19 @@ class V1beta1IngressConfig(object): 'disable_ingress_creation': 'disableIngressCreation', 'disable_istio_virtual_host': 'disableIstioVirtualHost', 'domain_template': 'domainTemplate', + 'enable_gateway_api': 'enableGatewayApi', 'ingress_class_name': 'ingressClassName', 'ingress_domain': 'ingressDomain', 'ingress_gateway': 'ingressGateway', 'knative_local_gateway_service': 'knativeLocalGatewayService', + 'kserve_ingress_gateway': 'kserveIngressGateway', 'local_gateway': 'localGateway', 'local_gateway_service': 'localGatewayService', 'path_template': 'pathTemplate', 'url_scheme': 'urlScheme' } - def __init__(self, additional_ingress_domains=None, disable_ingress_creation=None, disable_istio_virtual_host=None, domain_template=None, ingress_class_name=None, ingress_domain=None, ingress_gateway=None, knative_local_gateway_service=None, local_gateway=None, local_gateway_service=None, path_template=None, url_scheme=None, local_vars_configuration=None): # noqa: E501 + def __init__(self, additional_ingress_domains=None, disable_ingress_creation=None, disable_istio_virtual_host=None, domain_template=None, enable_gateway_api=None, ingress_class_name=None, ingress_domain=None, ingress_gateway=None, knative_local_gateway_service=None, kserve_ingress_gateway=None, local_gateway=None, local_gateway_service=None, path_template=None, url_scheme=None, local_vars_configuration=None): # noqa: E501 """V1beta1IngressConfig - a model defined in OpenAPI""" # noqa: E501 if local_vars_configuration is None: local_vars_configuration = Configuration() @@ -86,10 +90,12 @@ def __init__(self, additional_ingress_domains=None, disable_ingress_creation=Non self._disable_ingress_creation = None self._disable_istio_virtual_host = None self._domain_template = None + self._enable_gateway_api = None self._ingress_class_name = None self._ingress_domain = None self._ingress_gateway = None self._knative_local_gateway_service = None + self._kserve_ingress_gateway = None self._local_gateway = None self._local_gateway_service = None self._path_template = None @@ -104,6 +110,8 @@ def __init__(self, additional_ingress_domains=None, disable_ingress_creation=Non self.disable_istio_virtual_host = disable_istio_virtual_host if domain_template is not None: self.domain_template = domain_template + if enable_gateway_api is not None: + self.enable_gateway_api = enable_gateway_api if ingress_class_name is not None: self.ingress_class_name = ingress_class_name if ingress_domain is not None: @@ -112,6 +120,8 @@ def __init__(self, additional_ingress_domains=None, disable_ingress_creation=Non self.ingress_gateway = ingress_gateway if knative_local_gateway_service is not None: self.knative_local_gateway_service = knative_local_gateway_service + if kserve_ingress_gateway is not None: + self.kserve_ingress_gateway = kserve_ingress_gateway if local_gateway is not None: self.local_gateway = local_gateway if local_gateway_service is not None: @@ -205,6 +215,27 @@ def domain_template(self, domain_template): self._domain_template = domain_template + @property + def enable_gateway_api(self): + """Gets the enable_gateway_api of this V1beta1IngressConfig. # noqa: E501 + + + :return: The enable_gateway_api of this V1beta1IngressConfig. # noqa: E501 + :rtype: bool + """ + return self._enable_gateway_api + + @enable_gateway_api.setter + def enable_gateway_api(self, enable_gateway_api): + """Sets the enable_gateway_api of this V1beta1IngressConfig. + + + :param enable_gateway_api: The enable_gateway_api of this V1beta1IngressConfig. # noqa: E501 + :type: bool + """ + + self._enable_gateway_api = enable_gateway_api + @property def ingress_class_name(self): """Gets the ingress_class_name of this V1beta1IngressConfig. # noqa: E501 @@ -289,6 +320,27 @@ def knative_local_gateway_service(self, knative_local_gateway_service): self._knative_local_gateway_service = knative_local_gateway_service + @property + def kserve_ingress_gateway(self): + """Gets the kserve_ingress_gateway of this V1beta1IngressConfig. # noqa: E501 + + + :return: The kserve_ingress_gateway of this V1beta1IngressConfig. # noqa: E501 + :rtype: str + """ + return self._kserve_ingress_gateway + + @kserve_ingress_gateway.setter + def kserve_ingress_gateway(self, kserve_ingress_gateway): + """Sets the kserve_ingress_gateway of this V1beta1IngressConfig. + + + :param kserve_ingress_gateway: The kserve_ingress_gateway of this V1beta1IngressConfig. # noqa: E501 + :type: str + """ + + self._kserve_ingress_gateway = kserve_ingress_gateway + @property def local_gateway(self): """Gets the local_gateway of this V1beta1IngressConfig. # noqa: E501 diff --git a/python/kserve/kserve/models/v1beta1_local_model_config.py b/python/kserve/kserve/models/v1beta1_local_model_config.py index f6b55844e9a..d1bcb0d5d48 100644 --- a/python/kserve/kserve/models/v1beta1_local_model_config.py +++ b/python/kserve/kserve/models/v1beta1_local_model_config.py @@ -48,6 +48,7 @@ class V1beta1LocalModelConfig(object): """ openapi_types = { 'default_job_image': 'str', + 'disable_volume_management': 'bool', 'enabled': 'bool', 'fs_group': 'int', 'job_namespace': 'str', @@ -57,6 +58,7 @@ class V1beta1LocalModelConfig(object): attribute_map = { 'default_job_image': 'defaultJobImage', + 'disable_volume_management': 'disableVolumeManagement', 'enabled': 'enabled', 'fs_group': 'fsGroup', 'job_namespace': 'jobNamespace', @@ -64,13 +66,14 @@ class V1beta1LocalModelConfig(object): 'reconcilation_frequency_in_secs': 'reconcilationFrequencyInSecs' } - def __init__(self, default_job_image=None, enabled=False, fs_group=None, job_namespace='', job_ttl_seconds_after_finished=None, reconcilation_frequency_in_secs=None, local_vars_configuration=None): # noqa: E501 + def __init__(self, default_job_image=None, disable_volume_management=None, enabled=False, fs_group=None, job_namespace='', job_ttl_seconds_after_finished=None, reconcilation_frequency_in_secs=None, local_vars_configuration=None): # noqa: E501 """V1beta1LocalModelConfig - a model defined in OpenAPI""" # noqa: E501 if local_vars_configuration is None: local_vars_configuration = Configuration() self.local_vars_configuration = local_vars_configuration self._default_job_image = None + self._disable_volume_management = None self._enabled = None self._fs_group = None self._job_namespace = None @@ -80,6 +83,8 @@ def __init__(self, default_job_image=None, enabled=False, fs_group=None, job_nam if default_job_image is not None: self.default_job_image = default_job_image + if disable_volume_management is not None: + self.disable_volume_management = disable_volume_management self.enabled = enabled if fs_group is not None: self.fs_group = fs_group @@ -110,6 +115,27 @@ def default_job_image(self, default_job_image): self._default_job_image = default_job_image + @property + def disable_volume_management(self): + """Gets the disable_volume_management of this V1beta1LocalModelConfig. # noqa: E501 + + + :return: The disable_volume_management of this V1beta1LocalModelConfig. # noqa: E501 + :rtype: bool + """ + return self._disable_volume_management + + @disable_volume_management.setter + def disable_volume_management(self, disable_volume_management): + """Sets the disable_volume_management of this V1beta1LocalModelConfig. + + + :param disable_volume_management: The disable_volume_management of this V1beta1LocalModelConfig. # noqa: E501 + :type: bool + """ + + self._disable_volume_management = disable_volume_management + @property def enabled(self): """Gets the enabled of this V1beta1LocalModelConfig. # noqa: E501 diff --git a/python/kserve/kserve/models/v1beta1_pod_spec.py b/python/kserve/kserve/models/v1beta1_pod_spec.py index 3a076746f60..760fb42e224 100644 --- a/python/kserve/kserve/models/v1beta1_pod_spec.py +++ b/python/kserve/kserve/models/v1beta1_pod_spec.py @@ -72,6 +72,7 @@ class V1beta1PodSpec(object): 'priority_class_name': 'str', 'readiness_gates': 'list[V1PodReadinessGate]', 'resource_claims': 'list[V1PodResourceClaim]', + 'resources': 'V1ResourceRequirements', 'restart_policy': 'str', 'runtime_class_name': 'str', 'scheduler_name': 'str', @@ -114,6 +115,7 @@ class V1beta1PodSpec(object): 'priority_class_name': 'priorityClassName', 'readiness_gates': 'readinessGates', 'resource_claims': 'resourceClaims', + 'resources': 'resources', 'restart_policy': 'restartPolicy', 'runtime_class_name': 'runtimeClassName', 'scheduler_name': 'schedulerName', @@ -130,7 +132,7 @@ class V1beta1PodSpec(object): 'volumes': 'volumes' } - def __init__(self, active_deadline_seconds=None, affinity=None, automount_service_account_token=None, containers=None, dns_config=None, dns_policy=None, enable_service_links=None, ephemeral_containers=None, host_aliases=None, host_ipc=None, host_network=None, host_pid=None, host_users=None, hostname=None, image_pull_secrets=None, init_containers=None, node_name=None, node_selector=None, os=None, overhead=None, preemption_policy=None, priority=None, priority_class_name=None, readiness_gates=None, resource_claims=None, restart_policy=None, runtime_class_name=None, scheduler_name=None, scheduling_gates=None, security_context=None, service_account=None, service_account_name=None, set_hostname_as_fqdn=None, share_process_namespace=None, subdomain=None, termination_grace_period_seconds=None, tolerations=None, topology_spread_constraints=None, volumes=None, local_vars_configuration=None): # noqa: E501 + def __init__(self, active_deadline_seconds=None, affinity=None, automount_service_account_token=None, containers=None, dns_config=None, dns_policy=None, enable_service_links=None, ephemeral_containers=None, host_aliases=None, host_ipc=None, host_network=None, host_pid=None, host_users=None, hostname=None, image_pull_secrets=None, init_containers=None, node_name=None, node_selector=None, os=None, overhead=None, preemption_policy=None, priority=None, priority_class_name=None, readiness_gates=None, resource_claims=None, resources=None, restart_policy=None, runtime_class_name=None, scheduler_name=None, scheduling_gates=None, security_context=None, service_account=None, service_account_name=None, set_hostname_as_fqdn=None, share_process_namespace=None, subdomain=None, termination_grace_period_seconds=None, tolerations=None, topology_spread_constraints=None, volumes=None, local_vars_configuration=None): # noqa: E501 """V1beta1PodSpec - a model defined in OpenAPI""" # noqa: E501 if local_vars_configuration is None: local_vars_configuration = Configuration() @@ -161,6 +163,7 @@ def __init__(self, active_deadline_seconds=None, affinity=None, automount_servic self._priority_class_name = None self._readiness_gates = None self._resource_claims = None + self._resources = None self._restart_policy = None self._runtime_class_name = None self._scheduler_name = None @@ -227,6 +230,8 @@ def __init__(self, active_deadline_seconds=None, affinity=None, automount_servic self.readiness_gates = readiness_gates if resource_claims is not None: self.resource_claims = resource_claims + if resources is not None: + self.resources = resources if restart_policy is not None: self.restart_policy = restart_policy if runtime_class_name is not None: @@ -417,7 +422,7 @@ def enable_service_links(self, enable_service_links): def ephemeral_containers(self): """Gets the ephemeral_containers of this V1beta1PodSpec. # noqa: E501 - List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource. This field is beta-level and available on clusters that haven't disabled the EphemeralContainers feature gate. # noqa: E501 + List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource. # noqa: E501 :return: The ephemeral_containers of this V1beta1PodSpec. # noqa: E501 :rtype: list[V1EphemeralContainer] @@ -428,7 +433,7 @@ def ephemeral_containers(self): def ephemeral_containers(self, ephemeral_containers): """Sets the ephemeral_containers of this V1beta1PodSpec. - List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource. This field is beta-level and available on clusters that haven't disabled the EphemeralContainers feature gate. # noqa: E501 + List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource. # noqa: E501 :param ephemeral_containers: The ephemeral_containers of this V1beta1PodSpec. # noqa: E501 :type: list[V1EphemeralContainer] @@ -440,7 +445,7 @@ def ephemeral_containers(self, ephemeral_containers): def host_aliases(self): """Gets the host_aliases of this V1beta1PodSpec. # noqa: E501 - HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. This is only valid for non-hostNetwork pods. # noqa: E501 + HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. # noqa: E501 :return: The host_aliases of this V1beta1PodSpec. # noqa: E501 :rtype: list[V1HostAlias] @@ -451,7 +456,7 @@ def host_aliases(self): def host_aliases(self, host_aliases): """Sets the host_aliases of this V1beta1PodSpec. - HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. This is only valid for non-hostNetwork pods. # noqa: E501 + HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. # noqa: E501 :param host_aliases: The host_aliases of this V1beta1PodSpec. # noqa: E501 :type: list[V1HostAlias] @@ -578,7 +583,7 @@ def hostname(self, hostname): def image_pull_secrets(self): """Gets the image_pull_secrets of this V1beta1PodSpec. # noqa: E501 - ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. For example, in the case of docker, only DockerConfig type secrets are honored. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod # noqa: E501 + ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod # noqa: E501 :return: The image_pull_secrets of this V1beta1PodSpec. # noqa: E501 :rtype: list[V1LocalObjectReference] @@ -589,7 +594,7 @@ def image_pull_secrets(self): def image_pull_secrets(self, image_pull_secrets): """Sets the image_pull_secrets of this V1beta1PodSpec. - ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. For example, in the case of docker, only DockerConfig type secrets are honored. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod # noqa: E501 + ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod # noqa: E501 :param image_pull_secrets: The image_pull_secrets of this V1beta1PodSpec. # noqa: E501 :type: list[V1LocalObjectReference] @@ -601,7 +606,7 @@ def image_pull_secrets(self, image_pull_secrets): def init_containers(self): """Gets the init_containers of this V1beta1PodSpec. # noqa: E501 - List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ # noqa: E501 + List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ # noqa: E501 :return: The init_containers of this V1beta1PodSpec. # noqa: E501 :rtype: list[V1Container] @@ -612,7 +617,7 @@ def init_containers(self): def init_containers(self, init_containers): """Sets the init_containers of this V1beta1PodSpec. - List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ # noqa: E501 + List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ # noqa: E501 :param init_containers: The init_containers of this V1beta1PodSpec. # noqa: E501 :type: list[V1Container] @@ -624,7 +629,7 @@ def init_containers(self, init_containers): def node_name(self): """Gets the node_name of this V1beta1PodSpec. # noqa: E501 - NodeName is a request to schedule this pod onto a specific node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits resource requirements. # noqa: E501 + NodeName indicates in which node this pod is scheduled. If empty, this pod is a candidate for scheduling by the scheduler defined in schedulerName. Once this field is set, the kubelet for this node becomes responsible for the lifecycle of this pod. This field should not be used to express a desire for the pod to be scheduled on a specific node. https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodename # noqa: E501 :return: The node_name of this V1beta1PodSpec. # noqa: E501 :rtype: str @@ -635,7 +640,7 @@ def node_name(self): def node_name(self, node_name): """Sets the node_name of this V1beta1PodSpec. - NodeName is a request to schedule this pod onto a specific node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits resource requirements. # noqa: E501 + NodeName indicates in which node this pod is scheduled. If empty, this pod is a candidate for scheduling by the scheduler defined in schedulerName. Once this field is set, the kubelet for this node becomes responsible for the lifecycle of this pod. This field should not be used to express a desire for the pod to be scheduled on a specific node. https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodename # noqa: E501 :param node_name: The node_name of this V1beta1PodSpec. # noqa: E501 :type: str @@ -691,7 +696,7 @@ def os(self, os): def overhead(self): """Gets the overhead of this V1beta1PodSpec. # noqa: E501 - Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md This field is beta-level as of Kubernetes v1.18, and is only honored by servers that enable the PodOverhead feature. # noqa: E501 + Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md # noqa: E501 :return: The overhead of this V1beta1PodSpec. # noqa: E501 :rtype: dict(str, ResourceQuantity) @@ -702,7 +707,7 @@ def overhead(self): def overhead(self, overhead): """Sets the overhead of this V1beta1PodSpec. - Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md This field is beta-level as of Kubernetes v1.18, and is only honored by servers that enable the PodOverhead feature. # noqa: E501 + Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md # noqa: E501 :param overhead: The overhead of this V1beta1PodSpec. # noqa: E501 :type: dict(str, ResourceQuantity) @@ -714,7 +719,7 @@ def overhead(self, overhead): def preemption_policy(self): """Gets the preemption_policy of this V1beta1PodSpec. # noqa: E501 - PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset. This field is beta-level, gated by the NonPreemptingPriority feature-gate. # noqa: E501 + PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset. # noqa: E501 :return: The preemption_policy of this V1beta1PodSpec. # noqa: E501 :rtype: str @@ -725,7 +730,7 @@ def preemption_policy(self): def preemption_policy(self, preemption_policy): """Sets the preemption_policy of this V1beta1PodSpec. - PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset. This field is beta-level, gated by the NonPreemptingPriority feature-gate. # noqa: E501 + PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset. # noqa: E501 :param preemption_policy: The preemption_policy of this V1beta1PodSpec. # noqa: E501 :type: str @@ -825,11 +830,32 @@ def resource_claims(self, resource_claims): self._resource_claims = resource_claims + @property + def resources(self): + """Gets the resources of this V1beta1PodSpec. # noqa: E501 + + + :return: The resources of this V1beta1PodSpec. # noqa: E501 + :rtype: V1ResourceRequirements + """ + return self._resources + + @resources.setter + def resources(self, resources): + """Sets the resources of this V1beta1PodSpec. + + + :param resources: The resources of this V1beta1PodSpec. # noqa: E501 + :type: V1ResourceRequirements + """ + + self._resources = resources + @property def restart_policy(self): """Gets the restart_policy of this V1beta1PodSpec. # noqa: E501 - Restart policy for all containers within the pod. One of Always, OnFailure, Never. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy # noqa: E501 + Restart policy for all containers within the pod. One of Always, OnFailure, Never. In some contexts, only a subset of those values may be permitted. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy # noqa: E501 :return: The restart_policy of this V1beta1PodSpec. # noqa: E501 :rtype: str @@ -840,7 +866,7 @@ def restart_policy(self): def restart_policy(self, restart_policy): """Sets the restart_policy of this V1beta1PodSpec. - Restart policy for all containers within the pod. One of Always, OnFailure, Never. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy # noqa: E501 + Restart policy for all containers within the pod. One of Always, OnFailure, Never. In some contexts, only a subset of those values may be permitted. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy # noqa: E501 :param restart_policy: The restart_policy of this V1beta1PodSpec. # noqa: E501 :type: str @@ -852,7 +878,7 @@ def restart_policy(self, restart_policy): def runtime_class_name(self): """Gets the runtime_class_name of this V1beta1PodSpec. # noqa: E501 - RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class This is a beta feature as of Kubernetes v1.14. # noqa: E501 + RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class # noqa: E501 :return: The runtime_class_name of this V1beta1PodSpec. # noqa: E501 :rtype: str @@ -863,7 +889,7 @@ def runtime_class_name(self): def runtime_class_name(self, runtime_class_name): """Sets the runtime_class_name of this V1beta1PodSpec. - RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class This is a beta feature as of Kubernetes v1.14. # noqa: E501 + RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class # noqa: E501 :param runtime_class_name: The runtime_class_name of this V1beta1PodSpec. # noqa: E501 :type: str @@ -898,7 +924,7 @@ def scheduler_name(self, scheduler_name): def scheduling_gates(self): """Gets the scheduling_gates of this V1beta1PodSpec. # noqa: E501 - SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod. SchedulingGates can only be set at pod creation time, and be removed only afterwards. This is a beta feature enabled by the PodSchedulingReadiness feature gate. # noqa: E501 + SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod. SchedulingGates can only be set at pod creation time, and be removed only afterwards. # noqa: E501 :return: The scheduling_gates of this V1beta1PodSpec. # noqa: E501 :rtype: list[V1PodSchedulingGate] @@ -909,7 +935,7 @@ def scheduling_gates(self): def scheduling_gates(self, scheduling_gates): """Sets the scheduling_gates of this V1beta1PodSpec. - SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod. SchedulingGates can only be set at pod creation time, and be removed only afterwards. This is a beta feature enabled by the PodSchedulingReadiness feature gate. # noqa: E501 + SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod. SchedulingGates can only be set at pod creation time, and be removed only afterwards. # noqa: E501 :param scheduling_gates: The scheduling_gates of this V1beta1PodSpec. # noqa: E501 :type: list[V1PodSchedulingGate] @@ -942,7 +968,7 @@ def security_context(self, security_context): def service_account(self): """Gets the service_account of this V1beta1PodSpec. # noqa: E501 - DeprecatedServiceAccount is a depreciated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead. # noqa: E501 + DeprecatedServiceAccount is a deprecated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead. # noqa: E501 :return: The service_account of this V1beta1PodSpec. # noqa: E501 :rtype: str @@ -953,7 +979,7 @@ def service_account(self): def service_account(self, service_account): """Sets the service_account of this V1beta1PodSpec. - DeprecatedServiceAccount is a depreciated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead. # noqa: E501 + DeprecatedServiceAccount is a deprecated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead. # noqa: E501 :param service_account: The service_account of this V1beta1PodSpec. # noqa: E501 :type: str diff --git a/python/kserve/kserve/models/v1beta1_predictor_spec.py b/python/kserve/kserve/models/v1beta1_predictor_spec.py index 37c5e08638d..f1e3cbc94aa 100644 --- a/python/kserve/kserve/models/v1beta1_predictor_spec.py +++ b/python/kserve/kserve/models/v1beta1_predictor_spec.py @@ -88,6 +88,7 @@ class V1beta1PredictorSpec(object): 'pytorch': 'V1beta1TorchServeSpec', 'readiness_gates': 'list[V1PodReadinessGate]', 'resource_claims': 'list[V1PodResourceClaim]', + 'resources': 'V1ResourceRequirements', 'restart_policy': 'str', 'runtime_class_name': 'str', 'scale_metric': 'str', @@ -154,6 +155,7 @@ class V1beta1PredictorSpec(object): 'pytorch': 'pytorch', 'readiness_gates': 'readinessGates', 'resource_claims': 'resourceClaims', + 'resources': 'resources', 'restart_policy': 'restartPolicy', 'runtime_class_name': 'runtimeClassName', 'scale_metric': 'scaleMetric', @@ -178,7 +180,7 @@ class V1beta1PredictorSpec(object): 'xgboost': 'xgboost' } - def __init__(self, active_deadline_seconds=None, affinity=None, annotations=None, automount_service_account_token=None, batcher=None, canary_traffic_percent=None, container_concurrency=None, containers=None, deployment_strategy=None, dns_config=None, dns_policy=None, enable_service_links=None, ephemeral_containers=None, host_aliases=None, host_ipc=None, host_network=None, host_pid=None, host_users=None, hostname=None, huggingface=None, image_pull_secrets=None, init_containers=None, labels=None, lightgbm=None, logger=None, max_replicas=None, min_replicas=None, model=None, node_name=None, node_selector=None, onnx=None, os=None, overhead=None, paddle=None, pmml=None, preemption_policy=None, priority=None, priority_class_name=None, pytorch=None, readiness_gates=None, resource_claims=None, restart_policy=None, runtime_class_name=None, scale_metric=None, scale_target=None, scheduler_name=None, scheduling_gates=None, security_context=None, service_account=None, service_account_name=None, set_hostname_as_fqdn=None, share_process_namespace=None, sklearn=None, subdomain=None, tensorflow=None, termination_grace_period_seconds=None, timeout=None, tolerations=None, topology_spread_constraints=None, triton=None, volumes=None, worker_spec=None, xgboost=None, local_vars_configuration=None): # noqa: E501 + def __init__(self, active_deadline_seconds=None, affinity=None, annotations=None, automount_service_account_token=None, batcher=None, canary_traffic_percent=None, container_concurrency=None, containers=None, deployment_strategy=None, dns_config=None, dns_policy=None, enable_service_links=None, ephemeral_containers=None, host_aliases=None, host_ipc=None, host_network=None, host_pid=None, host_users=None, hostname=None, huggingface=None, image_pull_secrets=None, init_containers=None, labels=None, lightgbm=None, logger=None, max_replicas=None, min_replicas=None, model=None, node_name=None, node_selector=None, onnx=None, os=None, overhead=None, paddle=None, pmml=None, preemption_policy=None, priority=None, priority_class_name=None, pytorch=None, readiness_gates=None, resource_claims=None, resources=None, restart_policy=None, runtime_class_name=None, scale_metric=None, scale_target=None, scheduler_name=None, scheduling_gates=None, security_context=None, service_account=None, service_account_name=None, set_hostname_as_fqdn=None, share_process_namespace=None, sklearn=None, subdomain=None, tensorflow=None, termination_grace_period_seconds=None, timeout=None, tolerations=None, topology_spread_constraints=None, triton=None, volumes=None, worker_spec=None, xgboost=None, local_vars_configuration=None): # noqa: E501 """V1beta1PredictorSpec - a model defined in OpenAPI""" # noqa: E501 if local_vars_configuration is None: local_vars_configuration = Configuration() @@ -225,6 +227,7 @@ def __init__(self, active_deadline_seconds=None, affinity=None, annotations=None self._pytorch = None self._readiness_gates = None self._resource_claims = None + self._resources = None self._restart_policy = None self._runtime_class_name = None self._scale_metric = None @@ -331,6 +334,8 @@ def __init__(self, active_deadline_seconds=None, affinity=None, annotations=None self.readiness_gates = readiness_gates if resource_claims is not None: self.resource_claims = resource_claims + if resources is not None: + self.resources = resources if restart_policy is not None: self.restart_policy = restart_policy if runtime_class_name is not None: @@ -648,7 +653,7 @@ def enable_service_links(self, enable_service_links): def ephemeral_containers(self): """Gets the ephemeral_containers of this V1beta1PredictorSpec. # noqa: E501 - List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource. This field is beta-level and available on clusters that haven't disabled the EphemeralContainers feature gate. # noqa: E501 + List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource. # noqa: E501 :return: The ephemeral_containers of this V1beta1PredictorSpec. # noqa: E501 :rtype: list[V1EphemeralContainer] @@ -659,7 +664,7 @@ def ephemeral_containers(self): def ephemeral_containers(self, ephemeral_containers): """Sets the ephemeral_containers of this V1beta1PredictorSpec. - List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource. This field is beta-level and available on clusters that haven't disabled the EphemeralContainers feature gate. # noqa: E501 + List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource. # noqa: E501 :param ephemeral_containers: The ephemeral_containers of this V1beta1PredictorSpec. # noqa: E501 :type: list[V1EphemeralContainer] @@ -671,7 +676,7 @@ def ephemeral_containers(self, ephemeral_containers): def host_aliases(self): """Gets the host_aliases of this V1beta1PredictorSpec. # noqa: E501 - HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. This is only valid for non-hostNetwork pods. # noqa: E501 + HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. # noqa: E501 :return: The host_aliases of this V1beta1PredictorSpec. # noqa: E501 :rtype: list[V1HostAlias] @@ -682,7 +687,7 @@ def host_aliases(self): def host_aliases(self, host_aliases): """Sets the host_aliases of this V1beta1PredictorSpec. - HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. This is only valid for non-hostNetwork pods. # noqa: E501 + HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. # noqa: E501 :param host_aliases: The host_aliases of this V1beta1PredictorSpec. # noqa: E501 :type: list[V1HostAlias] @@ -830,7 +835,7 @@ def huggingface(self, huggingface): def image_pull_secrets(self): """Gets the image_pull_secrets of this V1beta1PredictorSpec. # noqa: E501 - ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. For example, in the case of docker, only DockerConfig type secrets are honored. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod # noqa: E501 + ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod # noqa: E501 :return: The image_pull_secrets of this V1beta1PredictorSpec. # noqa: E501 :rtype: list[V1LocalObjectReference] @@ -841,7 +846,7 @@ def image_pull_secrets(self): def image_pull_secrets(self, image_pull_secrets): """Sets the image_pull_secrets of this V1beta1PredictorSpec. - ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. For example, in the case of docker, only DockerConfig type secrets are honored. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod # noqa: E501 + ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod # noqa: E501 :param image_pull_secrets: The image_pull_secrets of this V1beta1PredictorSpec. # noqa: E501 :type: list[V1LocalObjectReference] @@ -853,7 +858,7 @@ def image_pull_secrets(self, image_pull_secrets): def init_containers(self): """Gets the init_containers of this V1beta1PredictorSpec. # noqa: E501 - List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ # noqa: E501 + List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ # noqa: E501 :return: The init_containers of this V1beta1PredictorSpec. # noqa: E501 :rtype: list[V1Container] @@ -864,7 +869,7 @@ def init_containers(self): def init_containers(self, init_containers): """Sets the init_containers of this V1beta1PredictorSpec. - List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ # noqa: E501 + List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ # noqa: E501 :param init_containers: The init_containers of this V1beta1PredictorSpec. # noqa: E501 :type: list[V1Container] @@ -1008,7 +1013,7 @@ def model(self, model): def node_name(self): """Gets the node_name of this V1beta1PredictorSpec. # noqa: E501 - NodeName is a request to schedule this pod onto a specific node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits resource requirements. # noqa: E501 + NodeName indicates in which node this pod is scheduled. If empty, this pod is a candidate for scheduling by the scheduler defined in schedulerName. Once this field is set, the kubelet for this node becomes responsible for the lifecycle of this pod. This field should not be used to express a desire for the pod to be scheduled on a specific node. https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodename # noqa: E501 :return: The node_name of this V1beta1PredictorSpec. # noqa: E501 :rtype: str @@ -1019,7 +1024,7 @@ def node_name(self): def node_name(self, node_name): """Sets the node_name of this V1beta1PredictorSpec. - NodeName is a request to schedule this pod onto a specific node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits resource requirements. # noqa: E501 + NodeName indicates in which node this pod is scheduled. If empty, this pod is a candidate for scheduling by the scheduler defined in schedulerName. Once this field is set, the kubelet for this node becomes responsible for the lifecycle of this pod. This field should not be used to express a desire for the pod to be scheduled on a specific node. https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodename # noqa: E501 :param node_name: The node_name of this V1beta1PredictorSpec. # noqa: E501 :type: str @@ -1096,7 +1101,7 @@ def os(self, os): def overhead(self): """Gets the overhead of this V1beta1PredictorSpec. # noqa: E501 - Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md This field is beta-level as of Kubernetes v1.18, and is only honored by servers that enable the PodOverhead feature. # noqa: E501 + Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md # noqa: E501 :return: The overhead of this V1beta1PredictorSpec. # noqa: E501 :rtype: dict(str, ResourceQuantity) @@ -1107,7 +1112,7 @@ def overhead(self): def overhead(self, overhead): """Sets the overhead of this V1beta1PredictorSpec. - Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md This field is beta-level as of Kubernetes v1.18, and is only honored by servers that enable the PodOverhead feature. # noqa: E501 + Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md # noqa: E501 :param overhead: The overhead of this V1beta1PredictorSpec. # noqa: E501 :type: dict(str, ResourceQuantity) @@ -1161,7 +1166,7 @@ def pmml(self, pmml): def preemption_policy(self): """Gets the preemption_policy of this V1beta1PredictorSpec. # noqa: E501 - PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset. This field is beta-level, gated by the NonPreemptingPriority feature-gate. # noqa: E501 + PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset. # noqa: E501 :return: The preemption_policy of this V1beta1PredictorSpec. # noqa: E501 :rtype: str @@ -1172,7 +1177,7 @@ def preemption_policy(self): def preemption_policy(self, preemption_policy): """Sets the preemption_policy of this V1beta1PredictorSpec. - PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset. This field is beta-level, gated by the NonPreemptingPriority feature-gate. # noqa: E501 + PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset. # noqa: E501 :param preemption_policy: The preemption_policy of this V1beta1PredictorSpec. # noqa: E501 :type: str @@ -1293,11 +1298,32 @@ def resource_claims(self, resource_claims): self._resource_claims = resource_claims + @property + def resources(self): + """Gets the resources of this V1beta1PredictorSpec. # noqa: E501 + + + :return: The resources of this V1beta1PredictorSpec. # noqa: E501 + :rtype: V1ResourceRequirements + """ + return self._resources + + @resources.setter + def resources(self, resources): + """Sets the resources of this V1beta1PredictorSpec. + + + :param resources: The resources of this V1beta1PredictorSpec. # noqa: E501 + :type: V1ResourceRequirements + """ + + self._resources = resources + @property def restart_policy(self): """Gets the restart_policy of this V1beta1PredictorSpec. # noqa: E501 - Restart policy for all containers within the pod. One of Always, OnFailure, Never. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy # noqa: E501 + Restart policy for all containers within the pod. One of Always, OnFailure, Never. In some contexts, only a subset of those values may be permitted. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy # noqa: E501 :return: The restart_policy of this V1beta1PredictorSpec. # noqa: E501 :rtype: str @@ -1308,7 +1334,7 @@ def restart_policy(self): def restart_policy(self, restart_policy): """Sets the restart_policy of this V1beta1PredictorSpec. - Restart policy for all containers within the pod. One of Always, OnFailure, Never. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy # noqa: E501 + Restart policy for all containers within the pod. One of Always, OnFailure, Never. In some contexts, only a subset of those values may be permitted. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy # noqa: E501 :param restart_policy: The restart_policy of this V1beta1PredictorSpec. # noqa: E501 :type: str @@ -1320,7 +1346,7 @@ def restart_policy(self, restart_policy): def runtime_class_name(self): """Gets the runtime_class_name of this V1beta1PredictorSpec. # noqa: E501 - RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class This is a beta feature as of Kubernetes v1.14. # noqa: E501 + RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class # noqa: E501 :return: The runtime_class_name of this V1beta1PredictorSpec. # noqa: E501 :rtype: str @@ -1331,7 +1357,7 @@ def runtime_class_name(self): def runtime_class_name(self, runtime_class_name): """Sets the runtime_class_name of this V1beta1PredictorSpec. - RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class This is a beta feature as of Kubernetes v1.14. # noqa: E501 + RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class # noqa: E501 :param runtime_class_name: The runtime_class_name of this V1beta1PredictorSpec. # noqa: E501 :type: str @@ -1412,7 +1438,7 @@ def scheduler_name(self, scheduler_name): def scheduling_gates(self): """Gets the scheduling_gates of this V1beta1PredictorSpec. # noqa: E501 - SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod. SchedulingGates can only be set at pod creation time, and be removed only afterwards. This is a beta feature enabled by the PodSchedulingReadiness feature gate. # noqa: E501 + SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod. SchedulingGates can only be set at pod creation time, and be removed only afterwards. # noqa: E501 :return: The scheduling_gates of this V1beta1PredictorSpec. # noqa: E501 :rtype: list[V1PodSchedulingGate] @@ -1423,7 +1449,7 @@ def scheduling_gates(self): def scheduling_gates(self, scheduling_gates): """Sets the scheduling_gates of this V1beta1PredictorSpec. - SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod. SchedulingGates can only be set at pod creation time, and be removed only afterwards. This is a beta feature enabled by the PodSchedulingReadiness feature gate. # noqa: E501 + SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod. SchedulingGates can only be set at pod creation time, and be removed only afterwards. # noqa: E501 :param scheduling_gates: The scheduling_gates of this V1beta1PredictorSpec. # noqa: E501 :type: list[V1PodSchedulingGate] @@ -1456,7 +1482,7 @@ def security_context(self, security_context): def service_account(self): """Gets the service_account of this V1beta1PredictorSpec. # noqa: E501 - DeprecatedServiceAccount is a depreciated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead. # noqa: E501 + DeprecatedServiceAccount is a deprecated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead. # noqa: E501 :return: The service_account of this V1beta1PredictorSpec. # noqa: E501 :rtype: str @@ -1467,7 +1493,7 @@ def service_account(self): def service_account(self, service_account): """Sets the service_account of this V1beta1PredictorSpec. - DeprecatedServiceAccount is a depreciated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead. # noqa: E501 + DeprecatedServiceAccount is a deprecated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead. # noqa: E501 :param service_account: The service_account of this V1beta1PredictorSpec. # noqa: E501 :type: str diff --git a/python/kserve/kserve/models/v1beta1_resource_config.py b/python/kserve/kserve/models/v1beta1_resource_config.py new file mode 100644 index 00000000000..3e2456ccfc4 --- /dev/null +++ b/python/kserve/kserve/models/v1beta1_resource_config.py @@ -0,0 +1,212 @@ +# Copyright 2023 The KServe Authors. +# +# Licensed 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. + +# coding: utf-8 + +""" + KServe + + Python SDK for KServe # noqa: E501 + + The version of the OpenAPI document: v0.1 + Generated by: https://openapi-generator.tech +""" + + +import pprint +import re # noqa: F401 + +import six + +from kserve.configuration import Configuration + + +class V1beta1ResourceConfig(object): + """NOTE: This class is auto generated by OpenAPI Generator. + Ref: https://openapi-generator.tech + + Do not edit the class manually. + """ + + """ + Attributes: + openapi_types (dict): The key is attribute name + and the value is attribute type. + attribute_map (dict): The key is attribute name + and the value is json key in definition. + """ + openapi_types = { + 'cpu_limit': 'str', + 'cpu_request': 'str', + 'memory_limit': 'str', + 'memory_request': 'str' + } + + attribute_map = { + 'cpu_limit': 'cpuLimit', + 'cpu_request': 'cpuRequest', + 'memory_limit': 'memoryLimit', + 'memory_request': 'memoryRequest' + } + + def __init__(self, cpu_limit=None, cpu_request=None, memory_limit=None, memory_request=None, local_vars_configuration=None): # noqa: E501 + """V1beta1ResourceConfig - a model defined in OpenAPI""" # noqa: E501 + if local_vars_configuration is None: + local_vars_configuration = Configuration() + self.local_vars_configuration = local_vars_configuration + + self._cpu_limit = None + self._cpu_request = None + self._memory_limit = None + self._memory_request = None + self.discriminator = None + + if cpu_limit is not None: + self.cpu_limit = cpu_limit + if cpu_request is not None: + self.cpu_request = cpu_request + if memory_limit is not None: + self.memory_limit = memory_limit + if memory_request is not None: + self.memory_request = memory_request + + @property + def cpu_limit(self): + """Gets the cpu_limit of this V1beta1ResourceConfig. # noqa: E501 + + + :return: The cpu_limit of this V1beta1ResourceConfig. # noqa: E501 + :rtype: str + """ + return self._cpu_limit + + @cpu_limit.setter + def cpu_limit(self, cpu_limit): + """Sets the cpu_limit of this V1beta1ResourceConfig. + + + :param cpu_limit: The cpu_limit of this V1beta1ResourceConfig. # noqa: E501 + :type: str + """ + + self._cpu_limit = cpu_limit + + @property + def cpu_request(self): + """Gets the cpu_request of this V1beta1ResourceConfig. # noqa: E501 + + + :return: The cpu_request of this V1beta1ResourceConfig. # noqa: E501 + :rtype: str + """ + return self._cpu_request + + @cpu_request.setter + def cpu_request(self, cpu_request): + """Sets the cpu_request of this V1beta1ResourceConfig. + + + :param cpu_request: The cpu_request of this V1beta1ResourceConfig. # noqa: E501 + :type: str + """ + + self._cpu_request = cpu_request + + @property + def memory_limit(self): + """Gets the memory_limit of this V1beta1ResourceConfig. # noqa: E501 + + + :return: The memory_limit of this V1beta1ResourceConfig. # noqa: E501 + :rtype: str + """ + return self._memory_limit + + @memory_limit.setter + def memory_limit(self, memory_limit): + """Sets the memory_limit of this V1beta1ResourceConfig. + + + :param memory_limit: The memory_limit of this V1beta1ResourceConfig. # noqa: E501 + :type: str + """ + + self._memory_limit = memory_limit + + @property + def memory_request(self): + """Gets the memory_request of this V1beta1ResourceConfig. # noqa: E501 + + + :return: The memory_request of this V1beta1ResourceConfig. # noqa: E501 + :rtype: str + """ + return self._memory_request + + @memory_request.setter + def memory_request(self, memory_request): + """Sets the memory_request of this V1beta1ResourceConfig. + + + :param memory_request: The memory_request of this V1beta1ResourceConfig. # noqa: E501 + :type: str + """ + + self._memory_request = memory_request + + def to_dict(self): + """Returns the model properties as a dict""" + result = {} + + for attr, _ in six.iteritems(self.openapi_types): + value = getattr(self, attr) + if isinstance(value, list): + result[attr] = list(map( + lambda x: x.to_dict() if hasattr(x, "to_dict") else x, + value + )) + elif hasattr(value, "to_dict"): + result[attr] = value.to_dict() + elif isinstance(value, dict): + result[attr] = dict(map( + lambda item: (item[0], item[1].to_dict()) + if hasattr(item[1], "to_dict") else item, + value.items() + )) + else: + result[attr] = value + + return result + + def to_str(self): + """Returns the string representation of the model""" + return pprint.pformat(self.to_dict()) + + def __repr__(self): + """For `print` and `pprint`""" + return self.to_str() + + def __eq__(self, other): + """Returns true if both objects are equal""" + if not isinstance(other, V1beta1ResourceConfig): + return False + + return self.to_dict() == other.to_dict() + + def __ne__(self, other): + """Returns true if both objects are not equal""" + if not isinstance(other, V1beta1ResourceConfig): + return True + + return self.to_dict() != other.to_dict() diff --git a/python/kserve/kserve/models/v1beta1_transformer_spec.py b/python/kserve/kserve/models/v1beta1_transformer_spec.py index 7e9cc817068..22026d5d09a 100644 --- a/python/kserve/kserve/models/v1beta1_transformer_spec.py +++ b/python/kserve/kserve/models/v1beta1_transformer_spec.py @@ -81,6 +81,7 @@ class V1beta1TransformerSpec(object): 'priority_class_name': 'str', 'readiness_gates': 'list[V1PodReadinessGate]', 'resource_claims': 'list[V1PodResourceClaim]', + 'resources': 'V1ResourceRequirements', 'restart_policy': 'str', 'runtime_class_name': 'str', 'scale_metric': 'str', @@ -135,6 +136,7 @@ class V1beta1TransformerSpec(object): 'priority_class_name': 'priorityClassName', 'readiness_gates': 'readinessGates', 'resource_claims': 'resourceClaims', + 'resources': 'resources', 'restart_policy': 'restartPolicy', 'runtime_class_name': 'runtimeClassName', 'scale_metric': 'scaleMetric', @@ -154,7 +156,7 @@ class V1beta1TransformerSpec(object): 'volumes': 'volumes' } - def __init__(self, active_deadline_seconds=None, affinity=None, annotations=None, automount_service_account_token=None, batcher=None, canary_traffic_percent=None, container_concurrency=None, containers=None, deployment_strategy=None, dns_config=None, dns_policy=None, enable_service_links=None, ephemeral_containers=None, host_aliases=None, host_ipc=None, host_network=None, host_pid=None, host_users=None, hostname=None, image_pull_secrets=None, init_containers=None, labels=None, logger=None, max_replicas=None, min_replicas=None, node_name=None, node_selector=None, os=None, overhead=None, preemption_policy=None, priority=None, priority_class_name=None, readiness_gates=None, resource_claims=None, restart_policy=None, runtime_class_name=None, scale_metric=None, scale_target=None, scheduler_name=None, scheduling_gates=None, security_context=None, service_account=None, service_account_name=None, set_hostname_as_fqdn=None, share_process_namespace=None, subdomain=None, termination_grace_period_seconds=None, timeout=None, tolerations=None, topology_spread_constraints=None, volumes=None, local_vars_configuration=None): # noqa: E501 + def __init__(self, active_deadline_seconds=None, affinity=None, annotations=None, automount_service_account_token=None, batcher=None, canary_traffic_percent=None, container_concurrency=None, containers=None, deployment_strategy=None, dns_config=None, dns_policy=None, enable_service_links=None, ephemeral_containers=None, host_aliases=None, host_ipc=None, host_network=None, host_pid=None, host_users=None, hostname=None, image_pull_secrets=None, init_containers=None, labels=None, logger=None, max_replicas=None, min_replicas=None, node_name=None, node_selector=None, os=None, overhead=None, preemption_policy=None, priority=None, priority_class_name=None, readiness_gates=None, resource_claims=None, resources=None, restart_policy=None, runtime_class_name=None, scale_metric=None, scale_target=None, scheduler_name=None, scheduling_gates=None, security_context=None, service_account=None, service_account_name=None, set_hostname_as_fqdn=None, share_process_namespace=None, subdomain=None, termination_grace_period_seconds=None, timeout=None, tolerations=None, topology_spread_constraints=None, volumes=None, local_vars_configuration=None): # noqa: E501 """V1beta1TransformerSpec - a model defined in OpenAPI""" # noqa: E501 if local_vars_configuration is None: local_vars_configuration = Configuration() @@ -194,6 +196,7 @@ def __init__(self, active_deadline_seconds=None, affinity=None, annotations=None self._priority_class_name = None self._readiness_gates = None self._resource_claims = None + self._resources = None self._restart_policy = None self._runtime_class_name = None self._scale_metric = None @@ -281,6 +284,8 @@ def __init__(self, active_deadline_seconds=None, affinity=None, annotations=None self.readiness_gates = readiness_gates if resource_claims is not None: self.resource_claims = resource_claims + if resources is not None: + self.resources = resources if restart_policy is not None: self.restart_policy = restart_policy if runtime_class_name is not None: @@ -588,7 +593,7 @@ def enable_service_links(self, enable_service_links): def ephemeral_containers(self): """Gets the ephemeral_containers of this V1beta1TransformerSpec. # noqa: E501 - List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource. This field is beta-level and available on clusters that haven't disabled the EphemeralContainers feature gate. # noqa: E501 + List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource. # noqa: E501 :return: The ephemeral_containers of this V1beta1TransformerSpec. # noqa: E501 :rtype: list[V1EphemeralContainer] @@ -599,7 +604,7 @@ def ephemeral_containers(self): def ephemeral_containers(self, ephemeral_containers): """Sets the ephemeral_containers of this V1beta1TransformerSpec. - List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource. This field is beta-level and available on clusters that haven't disabled the EphemeralContainers feature gate. # noqa: E501 + List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource. # noqa: E501 :param ephemeral_containers: The ephemeral_containers of this V1beta1TransformerSpec. # noqa: E501 :type: list[V1EphemeralContainer] @@ -611,7 +616,7 @@ def ephemeral_containers(self, ephemeral_containers): def host_aliases(self): """Gets the host_aliases of this V1beta1TransformerSpec. # noqa: E501 - HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. This is only valid for non-hostNetwork pods. # noqa: E501 + HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. # noqa: E501 :return: The host_aliases of this V1beta1TransformerSpec. # noqa: E501 :rtype: list[V1HostAlias] @@ -622,7 +627,7 @@ def host_aliases(self): def host_aliases(self, host_aliases): """Sets the host_aliases of this V1beta1TransformerSpec. - HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. This is only valid for non-hostNetwork pods. # noqa: E501 + HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. # noqa: E501 :param host_aliases: The host_aliases of this V1beta1TransformerSpec. # noqa: E501 :type: list[V1HostAlias] @@ -749,7 +754,7 @@ def hostname(self, hostname): def image_pull_secrets(self): """Gets the image_pull_secrets of this V1beta1TransformerSpec. # noqa: E501 - ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. For example, in the case of docker, only DockerConfig type secrets are honored. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod # noqa: E501 + ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod # noqa: E501 :return: The image_pull_secrets of this V1beta1TransformerSpec. # noqa: E501 :rtype: list[V1LocalObjectReference] @@ -760,7 +765,7 @@ def image_pull_secrets(self): def image_pull_secrets(self, image_pull_secrets): """Sets the image_pull_secrets of this V1beta1TransformerSpec. - ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. For example, in the case of docker, only DockerConfig type secrets are honored. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod # noqa: E501 + ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod # noqa: E501 :param image_pull_secrets: The image_pull_secrets of this V1beta1TransformerSpec. # noqa: E501 :type: list[V1LocalObjectReference] @@ -772,7 +777,7 @@ def image_pull_secrets(self, image_pull_secrets): def init_containers(self): """Gets the init_containers of this V1beta1TransformerSpec. # noqa: E501 - List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ # noqa: E501 + List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ # noqa: E501 :return: The init_containers of this V1beta1TransformerSpec. # noqa: E501 :rtype: list[V1Container] @@ -783,7 +788,7 @@ def init_containers(self): def init_containers(self, init_containers): """Sets the init_containers of this V1beta1TransformerSpec. - List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ # noqa: E501 + List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ # noqa: E501 :param init_containers: The init_containers of this V1beta1TransformerSpec. # noqa: E501 :type: list[V1Container] @@ -885,7 +890,7 @@ def min_replicas(self, min_replicas): def node_name(self): """Gets the node_name of this V1beta1TransformerSpec. # noqa: E501 - NodeName is a request to schedule this pod onto a specific node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits resource requirements. # noqa: E501 + NodeName indicates in which node this pod is scheduled. If empty, this pod is a candidate for scheduling by the scheduler defined in schedulerName. Once this field is set, the kubelet for this node becomes responsible for the lifecycle of this pod. This field should not be used to express a desire for the pod to be scheduled on a specific node. https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodename # noqa: E501 :return: The node_name of this V1beta1TransformerSpec. # noqa: E501 :rtype: str @@ -896,7 +901,7 @@ def node_name(self): def node_name(self, node_name): """Sets the node_name of this V1beta1TransformerSpec. - NodeName is a request to schedule this pod onto a specific node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits resource requirements. # noqa: E501 + NodeName indicates in which node this pod is scheduled. If empty, this pod is a candidate for scheduling by the scheduler defined in schedulerName. Once this field is set, the kubelet for this node becomes responsible for the lifecycle of this pod. This field should not be used to express a desire for the pod to be scheduled on a specific node. https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodename # noqa: E501 :param node_name: The node_name of this V1beta1TransformerSpec. # noqa: E501 :type: str @@ -952,7 +957,7 @@ def os(self, os): def overhead(self): """Gets the overhead of this V1beta1TransformerSpec. # noqa: E501 - Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md This field is beta-level as of Kubernetes v1.18, and is only honored by servers that enable the PodOverhead feature. # noqa: E501 + Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md # noqa: E501 :return: The overhead of this V1beta1TransformerSpec. # noqa: E501 :rtype: dict(str, ResourceQuantity) @@ -963,7 +968,7 @@ def overhead(self): def overhead(self, overhead): """Sets the overhead of this V1beta1TransformerSpec. - Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md This field is beta-level as of Kubernetes v1.18, and is only honored by servers that enable the PodOverhead feature. # noqa: E501 + Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md # noqa: E501 :param overhead: The overhead of this V1beta1TransformerSpec. # noqa: E501 :type: dict(str, ResourceQuantity) @@ -975,7 +980,7 @@ def overhead(self, overhead): def preemption_policy(self): """Gets the preemption_policy of this V1beta1TransformerSpec. # noqa: E501 - PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset. This field is beta-level, gated by the NonPreemptingPriority feature-gate. # noqa: E501 + PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset. # noqa: E501 :return: The preemption_policy of this V1beta1TransformerSpec. # noqa: E501 :rtype: str @@ -986,7 +991,7 @@ def preemption_policy(self): def preemption_policy(self, preemption_policy): """Sets the preemption_policy of this V1beta1TransformerSpec. - PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset. This field is beta-level, gated by the NonPreemptingPriority feature-gate. # noqa: E501 + PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset. # noqa: E501 :param preemption_policy: The preemption_policy of this V1beta1TransformerSpec. # noqa: E501 :type: str @@ -1086,11 +1091,32 @@ def resource_claims(self, resource_claims): self._resource_claims = resource_claims + @property + def resources(self): + """Gets the resources of this V1beta1TransformerSpec. # noqa: E501 + + + :return: The resources of this V1beta1TransformerSpec. # noqa: E501 + :rtype: V1ResourceRequirements + """ + return self._resources + + @resources.setter + def resources(self, resources): + """Sets the resources of this V1beta1TransformerSpec. + + + :param resources: The resources of this V1beta1TransformerSpec. # noqa: E501 + :type: V1ResourceRequirements + """ + + self._resources = resources + @property def restart_policy(self): """Gets the restart_policy of this V1beta1TransformerSpec. # noqa: E501 - Restart policy for all containers within the pod. One of Always, OnFailure, Never. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy # noqa: E501 + Restart policy for all containers within the pod. One of Always, OnFailure, Never. In some contexts, only a subset of those values may be permitted. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy # noqa: E501 :return: The restart_policy of this V1beta1TransformerSpec. # noqa: E501 :rtype: str @@ -1101,7 +1127,7 @@ def restart_policy(self): def restart_policy(self, restart_policy): """Sets the restart_policy of this V1beta1TransformerSpec. - Restart policy for all containers within the pod. One of Always, OnFailure, Never. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy # noqa: E501 + Restart policy for all containers within the pod. One of Always, OnFailure, Never. In some contexts, only a subset of those values may be permitted. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy # noqa: E501 :param restart_policy: The restart_policy of this V1beta1TransformerSpec. # noqa: E501 :type: str @@ -1113,7 +1139,7 @@ def restart_policy(self, restart_policy): def runtime_class_name(self): """Gets the runtime_class_name of this V1beta1TransformerSpec. # noqa: E501 - RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class This is a beta feature as of Kubernetes v1.14. # noqa: E501 + RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class # noqa: E501 :return: The runtime_class_name of this V1beta1TransformerSpec. # noqa: E501 :rtype: str @@ -1124,7 +1150,7 @@ def runtime_class_name(self): def runtime_class_name(self, runtime_class_name): """Sets the runtime_class_name of this V1beta1TransformerSpec. - RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class This is a beta feature as of Kubernetes v1.14. # noqa: E501 + RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class # noqa: E501 :param runtime_class_name: The runtime_class_name of this V1beta1TransformerSpec. # noqa: E501 :type: str @@ -1205,7 +1231,7 @@ def scheduler_name(self, scheduler_name): def scheduling_gates(self): """Gets the scheduling_gates of this V1beta1TransformerSpec. # noqa: E501 - SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod. SchedulingGates can only be set at pod creation time, and be removed only afterwards. This is a beta feature enabled by the PodSchedulingReadiness feature gate. # noqa: E501 + SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod. SchedulingGates can only be set at pod creation time, and be removed only afterwards. # noqa: E501 :return: The scheduling_gates of this V1beta1TransformerSpec. # noqa: E501 :rtype: list[V1PodSchedulingGate] @@ -1216,7 +1242,7 @@ def scheduling_gates(self): def scheduling_gates(self, scheduling_gates): """Sets the scheduling_gates of this V1beta1TransformerSpec. - SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod. SchedulingGates can only be set at pod creation time, and be removed only afterwards. This is a beta feature enabled by the PodSchedulingReadiness feature gate. # noqa: E501 + SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod. SchedulingGates can only be set at pod creation time, and be removed only afterwards. # noqa: E501 :param scheduling_gates: The scheduling_gates of this V1beta1TransformerSpec. # noqa: E501 :type: list[V1PodSchedulingGate] @@ -1249,7 +1275,7 @@ def security_context(self, security_context): def service_account(self): """Gets the service_account of this V1beta1TransformerSpec. # noqa: E501 - DeprecatedServiceAccount is a depreciated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead. # noqa: E501 + DeprecatedServiceAccount is a deprecated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead. # noqa: E501 :return: The service_account of this V1beta1TransformerSpec. # noqa: E501 :rtype: str @@ -1260,7 +1286,7 @@ def service_account(self): def service_account(self, service_account): """Sets the service_account of this V1beta1TransformerSpec. - DeprecatedServiceAccount is a depreciated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead. # noqa: E501 + DeprecatedServiceAccount is a deprecated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead. # noqa: E501 :param service_account: The service_account of this V1beta1TransformerSpec. # noqa: E501 :type: str diff --git a/python/kserve/kserve/models/v1beta1_worker_spec.py b/python/kserve/kserve/models/v1beta1_worker_spec.py index e21defa6612..5ce25fa0aa7 100644 --- a/python/kserve/kserve/models/v1beta1_worker_spec.py +++ b/python/kserve/kserve/models/v1beta1_worker_spec.py @@ -73,6 +73,7 @@ class V1beta1WorkerSpec(object): 'priority_class_name': 'str', 'readiness_gates': 'list[V1PodReadinessGate]', 'resource_claims': 'list[V1PodResourceClaim]', + 'resources': 'V1ResourceRequirements', 'restart_policy': 'str', 'runtime_class_name': 'str', 'scheduler_name': 'str', @@ -117,6 +118,7 @@ class V1beta1WorkerSpec(object): 'priority_class_name': 'priorityClassName', 'readiness_gates': 'readinessGates', 'resource_claims': 'resourceClaims', + 'resources': 'resources', 'restart_policy': 'restartPolicy', 'runtime_class_name': 'runtimeClassName', 'scheduler_name': 'schedulerName', @@ -134,7 +136,7 @@ class V1beta1WorkerSpec(object): 'volumes': 'volumes' } - def __init__(self, active_deadline_seconds=None, affinity=None, automount_service_account_token=None, containers=None, dns_config=None, dns_policy=None, enable_service_links=None, ephemeral_containers=None, host_aliases=None, host_ipc=None, host_network=None, host_pid=None, host_users=None, hostname=None, image_pull_secrets=None, init_containers=None, node_name=None, node_selector=None, os=None, overhead=None, pipeline_parallel_size=None, preemption_policy=None, priority=None, priority_class_name=None, readiness_gates=None, resource_claims=None, restart_policy=None, runtime_class_name=None, scheduler_name=None, scheduling_gates=None, security_context=None, service_account=None, service_account_name=None, set_hostname_as_fqdn=None, share_process_namespace=None, subdomain=None, tensor_parallel_size=None, termination_grace_period_seconds=None, tolerations=None, topology_spread_constraints=None, volumes=None, local_vars_configuration=None): # noqa: E501 + def __init__(self, active_deadline_seconds=None, affinity=None, automount_service_account_token=None, containers=None, dns_config=None, dns_policy=None, enable_service_links=None, ephemeral_containers=None, host_aliases=None, host_ipc=None, host_network=None, host_pid=None, host_users=None, hostname=None, image_pull_secrets=None, init_containers=None, node_name=None, node_selector=None, os=None, overhead=None, pipeline_parallel_size=None, preemption_policy=None, priority=None, priority_class_name=None, readiness_gates=None, resource_claims=None, resources=None, restart_policy=None, runtime_class_name=None, scheduler_name=None, scheduling_gates=None, security_context=None, service_account=None, service_account_name=None, set_hostname_as_fqdn=None, share_process_namespace=None, subdomain=None, tensor_parallel_size=None, termination_grace_period_seconds=None, tolerations=None, topology_spread_constraints=None, volumes=None, local_vars_configuration=None): # noqa: E501 """V1beta1WorkerSpec - a model defined in OpenAPI""" # noqa: E501 if local_vars_configuration is None: local_vars_configuration = Configuration() @@ -166,6 +168,7 @@ def __init__(self, active_deadline_seconds=None, affinity=None, automount_servic self._priority_class_name = None self._readiness_gates = None self._resource_claims = None + self._resources = None self._restart_policy = None self._runtime_class_name = None self._scheduler_name = None @@ -235,6 +238,8 @@ def __init__(self, active_deadline_seconds=None, affinity=None, automount_servic self.readiness_gates = readiness_gates if resource_claims is not None: self.resource_claims = resource_claims + if resources is not None: + self.resources = resources if restart_policy is not None: self.restart_policy = restart_policy if runtime_class_name is not None: @@ -427,7 +432,7 @@ def enable_service_links(self, enable_service_links): def ephemeral_containers(self): """Gets the ephemeral_containers of this V1beta1WorkerSpec. # noqa: E501 - List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource. This field is beta-level and available on clusters that haven't disabled the EphemeralContainers feature gate. # noqa: E501 + List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource. # noqa: E501 :return: The ephemeral_containers of this V1beta1WorkerSpec. # noqa: E501 :rtype: list[V1EphemeralContainer] @@ -438,7 +443,7 @@ def ephemeral_containers(self): def ephemeral_containers(self, ephemeral_containers): """Sets the ephemeral_containers of this V1beta1WorkerSpec. - List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource. This field is beta-level and available on clusters that haven't disabled the EphemeralContainers feature gate. # noqa: E501 + List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource. # noqa: E501 :param ephemeral_containers: The ephemeral_containers of this V1beta1WorkerSpec. # noqa: E501 :type: list[V1EphemeralContainer] @@ -450,7 +455,7 @@ def ephemeral_containers(self, ephemeral_containers): def host_aliases(self): """Gets the host_aliases of this V1beta1WorkerSpec. # noqa: E501 - HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. This is only valid for non-hostNetwork pods. # noqa: E501 + HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. # noqa: E501 :return: The host_aliases of this V1beta1WorkerSpec. # noqa: E501 :rtype: list[V1HostAlias] @@ -461,7 +466,7 @@ def host_aliases(self): def host_aliases(self, host_aliases): """Sets the host_aliases of this V1beta1WorkerSpec. - HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. This is only valid for non-hostNetwork pods. # noqa: E501 + HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. # noqa: E501 :param host_aliases: The host_aliases of this V1beta1WorkerSpec. # noqa: E501 :type: list[V1HostAlias] @@ -588,7 +593,7 @@ def hostname(self, hostname): def image_pull_secrets(self): """Gets the image_pull_secrets of this V1beta1WorkerSpec. # noqa: E501 - ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. For example, in the case of docker, only DockerConfig type secrets are honored. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod # noqa: E501 + ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod # noqa: E501 :return: The image_pull_secrets of this V1beta1WorkerSpec. # noqa: E501 :rtype: list[V1LocalObjectReference] @@ -599,7 +604,7 @@ def image_pull_secrets(self): def image_pull_secrets(self, image_pull_secrets): """Sets the image_pull_secrets of this V1beta1WorkerSpec. - ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. For example, in the case of docker, only DockerConfig type secrets are honored. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod # noqa: E501 + ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod # noqa: E501 :param image_pull_secrets: The image_pull_secrets of this V1beta1WorkerSpec. # noqa: E501 :type: list[V1LocalObjectReference] @@ -611,7 +616,7 @@ def image_pull_secrets(self, image_pull_secrets): def init_containers(self): """Gets the init_containers of this V1beta1WorkerSpec. # noqa: E501 - List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ # noqa: E501 + List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ # noqa: E501 :return: The init_containers of this V1beta1WorkerSpec. # noqa: E501 :rtype: list[V1Container] @@ -622,7 +627,7 @@ def init_containers(self): def init_containers(self, init_containers): """Sets the init_containers of this V1beta1WorkerSpec. - List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ # noqa: E501 + List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ # noqa: E501 :param init_containers: The init_containers of this V1beta1WorkerSpec. # noqa: E501 :type: list[V1Container] @@ -634,7 +639,7 @@ def init_containers(self, init_containers): def node_name(self): """Gets the node_name of this V1beta1WorkerSpec. # noqa: E501 - NodeName is a request to schedule this pod onto a specific node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits resource requirements. # noqa: E501 + NodeName indicates in which node this pod is scheduled. If empty, this pod is a candidate for scheduling by the scheduler defined in schedulerName. Once this field is set, the kubelet for this node becomes responsible for the lifecycle of this pod. This field should not be used to express a desire for the pod to be scheduled on a specific node. https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodename # noqa: E501 :return: The node_name of this V1beta1WorkerSpec. # noqa: E501 :rtype: str @@ -645,7 +650,7 @@ def node_name(self): def node_name(self, node_name): """Sets the node_name of this V1beta1WorkerSpec. - NodeName is a request to schedule this pod onto a specific node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits resource requirements. # noqa: E501 + NodeName indicates in which node this pod is scheduled. If empty, this pod is a candidate for scheduling by the scheduler defined in schedulerName. Once this field is set, the kubelet for this node becomes responsible for the lifecycle of this pod. This field should not be used to express a desire for the pod to be scheduled on a specific node. https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodename # noqa: E501 :param node_name: The node_name of this V1beta1WorkerSpec. # noqa: E501 :type: str @@ -701,7 +706,7 @@ def os(self, os): def overhead(self): """Gets the overhead of this V1beta1WorkerSpec. # noqa: E501 - Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md This field is beta-level as of Kubernetes v1.18, and is only honored by servers that enable the PodOverhead feature. # noqa: E501 + Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md # noqa: E501 :return: The overhead of this V1beta1WorkerSpec. # noqa: E501 :rtype: dict(str, ResourceQuantity) @@ -712,7 +717,7 @@ def overhead(self): def overhead(self, overhead): """Sets the overhead of this V1beta1WorkerSpec. - Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md This field is beta-level as of Kubernetes v1.18, and is only honored by servers that enable the PodOverhead feature. # noqa: E501 + Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md # noqa: E501 :param overhead: The overhead of this V1beta1WorkerSpec. # noqa: E501 :type: dict(str, ResourceQuantity) @@ -747,7 +752,7 @@ def pipeline_parallel_size(self, pipeline_parallel_size): def preemption_policy(self): """Gets the preemption_policy of this V1beta1WorkerSpec. # noqa: E501 - PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset. This field is beta-level, gated by the NonPreemptingPriority feature-gate. # noqa: E501 + PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset. # noqa: E501 :return: The preemption_policy of this V1beta1WorkerSpec. # noqa: E501 :rtype: str @@ -758,7 +763,7 @@ def preemption_policy(self): def preemption_policy(self, preemption_policy): """Sets the preemption_policy of this V1beta1WorkerSpec. - PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset. This field is beta-level, gated by the NonPreemptingPriority feature-gate. # noqa: E501 + PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset. # noqa: E501 :param preemption_policy: The preemption_policy of this V1beta1WorkerSpec. # noqa: E501 :type: str @@ -858,11 +863,32 @@ def resource_claims(self, resource_claims): self._resource_claims = resource_claims + @property + def resources(self): + """Gets the resources of this V1beta1WorkerSpec. # noqa: E501 + + + :return: The resources of this V1beta1WorkerSpec. # noqa: E501 + :rtype: V1ResourceRequirements + """ + return self._resources + + @resources.setter + def resources(self, resources): + """Sets the resources of this V1beta1WorkerSpec. + + + :param resources: The resources of this V1beta1WorkerSpec. # noqa: E501 + :type: V1ResourceRequirements + """ + + self._resources = resources + @property def restart_policy(self): """Gets the restart_policy of this V1beta1WorkerSpec. # noqa: E501 - Restart policy for all containers within the pod. One of Always, OnFailure, Never. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy # noqa: E501 + Restart policy for all containers within the pod. One of Always, OnFailure, Never. In some contexts, only a subset of those values may be permitted. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy # noqa: E501 :return: The restart_policy of this V1beta1WorkerSpec. # noqa: E501 :rtype: str @@ -873,7 +899,7 @@ def restart_policy(self): def restart_policy(self, restart_policy): """Sets the restart_policy of this V1beta1WorkerSpec. - Restart policy for all containers within the pod. One of Always, OnFailure, Never. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy # noqa: E501 + Restart policy for all containers within the pod. One of Always, OnFailure, Never. In some contexts, only a subset of those values may be permitted. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy # noqa: E501 :param restart_policy: The restart_policy of this V1beta1WorkerSpec. # noqa: E501 :type: str @@ -885,7 +911,7 @@ def restart_policy(self, restart_policy): def runtime_class_name(self): """Gets the runtime_class_name of this V1beta1WorkerSpec. # noqa: E501 - RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class This is a beta feature as of Kubernetes v1.14. # noqa: E501 + RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class # noqa: E501 :return: The runtime_class_name of this V1beta1WorkerSpec. # noqa: E501 :rtype: str @@ -896,7 +922,7 @@ def runtime_class_name(self): def runtime_class_name(self, runtime_class_name): """Sets the runtime_class_name of this V1beta1WorkerSpec. - RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class This is a beta feature as of Kubernetes v1.14. # noqa: E501 + RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class # noqa: E501 :param runtime_class_name: The runtime_class_name of this V1beta1WorkerSpec. # noqa: E501 :type: str @@ -931,7 +957,7 @@ def scheduler_name(self, scheduler_name): def scheduling_gates(self): """Gets the scheduling_gates of this V1beta1WorkerSpec. # noqa: E501 - SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod. SchedulingGates can only be set at pod creation time, and be removed only afterwards. This is a beta feature enabled by the PodSchedulingReadiness feature gate. # noqa: E501 + SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod. SchedulingGates can only be set at pod creation time, and be removed only afterwards. # noqa: E501 :return: The scheduling_gates of this V1beta1WorkerSpec. # noqa: E501 :rtype: list[V1PodSchedulingGate] @@ -942,7 +968,7 @@ def scheduling_gates(self): def scheduling_gates(self, scheduling_gates): """Sets the scheduling_gates of this V1beta1WorkerSpec. - SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod. SchedulingGates can only be set at pod creation time, and be removed only afterwards. This is a beta feature enabled by the PodSchedulingReadiness feature gate. # noqa: E501 + SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod. SchedulingGates can only be set at pod creation time, and be removed only afterwards. # noqa: E501 :param scheduling_gates: The scheduling_gates of this V1beta1WorkerSpec. # noqa: E501 :type: list[V1PodSchedulingGate] @@ -975,7 +1001,7 @@ def security_context(self, security_context): def service_account(self): """Gets the service_account of this V1beta1WorkerSpec. # noqa: E501 - DeprecatedServiceAccount is a depreciated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead. # noqa: E501 + DeprecatedServiceAccount is a deprecated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead. # noqa: E501 :return: The service_account of this V1beta1WorkerSpec. # noqa: E501 :rtype: str @@ -986,7 +1012,7 @@ def service_account(self): def service_account(self, service_account): """Sets the service_account of this V1beta1WorkerSpec. - DeprecatedServiceAccount is a depreciated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead. # noqa: E501 + DeprecatedServiceAccount is a deprecated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead. # noqa: E501 :param service_account: The service_account of this V1beta1WorkerSpec. # noqa: E501 :type: str diff --git a/python/kserve/kserve/protocol/dataplane.py b/python/kserve/kserve/protocol/dataplane.py index 4c1300cf593..6badb7b24f4 100644 --- a/python/kserve/kserve/protocol/dataplane.py +++ b/python/kserve/kserve/protocol/dataplane.py @@ -35,7 +35,7 @@ from ..utils.inference_client_factory import InferenceClientFactory from ..utils.utils import create_response_cloudevent, is_structured_cloudevent from .infer_type import InferRequest, InferResponse -from .rest.openai import OpenAIModel +from .rest.openai import OpenAICompletionModel JSON_HEADERS = [ "application/json", @@ -258,7 +258,7 @@ async def ready(self) -> bool: self.predictor_config.predictor_base_url ) else: - return await self._inference_rest_client.is_server_ready( + return await self.rest_client.is_server_ready( self.predictor_config.predictor_base_url ) return True @@ -432,8 +432,8 @@ async def infer( # call model locally or remote model workers response_headers = {} model = await self.get_model(model_name) - if isinstance(model, OpenAIModel): - error_msg = f"Model {model_name} is of type OpenAIModel. It does not support the infer method." + if isinstance(model, OpenAICompletionModel): + error_msg = f"Model {model_name} is of type OpenAICompletionModel. It does not support the infer method." raise InvalidInput(reason=error_msg) if not isinstance(model, InferenceModel): raise ValueError( @@ -466,9 +466,9 @@ async def explain( # call model locally or remote model workers response_headers = headers if headers else {} model = await self.get_model(model_name) - if isinstance(model, OpenAIModel): + if isinstance(model, OpenAICompletionModel): logger.warning( - f"Model {model_name} is of type OpenAIModel. It does not support the explain method." + f"Model {model_name} is of type OpenAICompletionModel. It does not support the explain method." " A request exercised this path and will cause a server crash." ) if not isinstance(model, InferenceModel): diff --git a/python/kserve/kserve/protocol/grpc/server.py b/python/kserve/kserve/protocol/grpc/server.py index 0b7a3905228..28b41cb962b 100644 --- a/python/kserve/kserve/protocol/grpc/server.py +++ b/python/kserve/kserve/protocol/grpc/server.py @@ -65,6 +65,7 @@ async def start(self, max_workers): listen_addr = f"[::]:{self._port}" self._server.add_insecure_port(listen_addr) + logger.info(f"Starting gRPC server with {max_workers} workers") logger.info("Starting gRPC server on %s", listen_addr) await self._server.start() await self._server.wait_for_termination() diff --git a/python/kserve/kserve/protocol/rest/openai/__init__.py b/python/kserve/kserve/protocol/rest/openai/__init__.py index ea1412b9d31..9eca9bf48f8 100644 --- a/python/kserve/kserve/protocol/rest/openai/__init__.py +++ b/python/kserve/kserve/protocol/rest/openai/__init__.py @@ -16,7 +16,10 @@ ChatPrompt, CompletionRequest, ChatCompletionRequest, + EmbeddingRequest, OpenAIModel, + OpenAICompletionModel, + OpenAIEmbeddingModel, ) from .openai_proxy_model import OpenAIProxyModel @@ -26,9 +29,12 @@ __all__ = [ "OpenAIModel", "OpenAIChatAdapterModel", + "OpenAICompletionModel", + "OpenAIEmbeddingModel", "OpenAIProxyModel", "ChatPrompt", "CompletionRequest", "ChatCompletionRequest", "ChatCompletionRequestMessage", + "EmbeddingRequest", ] diff --git a/python/kserve/kserve/protocol/rest/openai/dataplane.py b/python/kserve/kserve/protocol/rest/openai/dataplane.py index 07ba3abc5ac..f9aa24c571c 100644 --- a/python/kserve/kserve/protocol/rest/openai/dataplane.py +++ b/python/kserve/kserve/protocol/rest/openai/dataplane.py @@ -28,9 +28,20 @@ from kserve.protocol.rest.openai.types.openapi import ( CreateCompletionResponse as Completion, ) +from kserve.protocol.rest.openai.types.openapi import CreateEmbeddingRequest +from kserve.protocol.rest.openai.types.openapi import ( + CreateEmbeddingResponse as Embedding, +) from ...dataplane import DataPlane -from .openai_model import ChatCompletionRequest, CompletionRequest, OpenAIModel +from .openai_model import ( + ChatCompletionRequest, + CompletionRequest, + EmbeddingRequest, + OpenAIModel, + OpenAICompletionModel, + OpenAIEmbeddingModel, +) class OpenAIDataPlane(DataPlane): @@ -57,7 +68,7 @@ async def create_completion( InvalidInput: An error when the body bytes can't be decoded as JSON. """ model = await self.get_model(model_name) - if not isinstance(model, OpenAIModel): + if not isinstance(model, OpenAICompletionModel): raise RuntimeError(f"Model {model_name} does not support completion") completion_request = CompletionRequest( @@ -88,7 +99,7 @@ async def create_chat_completion( InvalidInput: An error when the body bytes can't be decoded as JSON. """ model = await self.get_model(model_name) - if not isinstance(model, OpenAIModel): + if not isinstance(model, OpenAICompletionModel): raise RuntimeError(f"Model {model_name} does not support chat completion") completion_request = ChatCompletionRequest( @@ -99,6 +110,37 @@ async def create_chat_completion( ) return await model.create_chat_completion(completion_request) + async def create_embedding( + self, + model_name: str, + request: CreateEmbeddingRequest, + headers: Headers, + response: Response, + ) -> Embedding: + """Creates an embedding vector representing the input text. + + Args: + model_name (str): Model name. + request (CreateEmbeddingRequest): Params to create the embedding. + headers: (Optional[Dict[str, str]]): Request headers. + + Returns: + response: A non-streaming embedding response + + Raises: + InvalidInput: An error when the body bytes can't be decoded as JSON. + """ + model = await self.get_model(model_name) + if not isinstance(model, OpenAIEmbeddingModel): + raise RuntimeError(f"Model {model_name} does not support embeddings") + + embedding_request = EmbeddingRequest( + request_id=headers.get("x-request-id", None), + params=request, + context={"headers": dict(headers), "response": response}, + ) + return await model.create_embedding(embedding_request) + async def models(self) -> List[OpenAIModel]: """Retrieve a list of models diff --git a/python/kserve/kserve/protocol/rest/openai/endpoints.py b/python/kserve/kserve/protocol/rest/openai/endpoints.py index 7e11250b577..c00c6b14a8c 100644 --- a/python/kserve/kserve/protocol/rest/openai/endpoints.py +++ b/python/kserve/kserve/protocol/rest/openai/endpoints.py @@ -25,6 +25,7 @@ from kserve.protocol.rest.openai.types.openapi import ( CreateChatCompletionRequest, CreateCompletionRequest, + CreateEmbeddingRequest, ListModelsResponse, Model, ) @@ -41,6 +42,7 @@ CreateCompletionRequestAdapter = TypeAdapter(CreateCompletionRequest) ChatCompletionRequestAdapter = TypeAdapter(CreateChatCompletionRequest) +EmbeddingRequestAdapter = TypeAdapter(CreateEmbeddingRequest) class OpenAIEndpoints: @@ -58,8 +60,8 @@ async def create_completion( Args: raw_request (Request): fastapi request object, - model_name (str): Model name. request_body (CompletionCreateParams): Completion params body. + response (Response): fastapi response object Returns: InferenceResponse: Inference response object. @@ -102,8 +104,8 @@ async def create_chat_completion( Args: raw_request (Request): fastapi request object, - model_name (str): Model name. request_body (ChatCompletionRequestAdapter): Chat completion params body. + response (Response): fastapi response object Returns: InferenceResponse: Inference response object. @@ -137,6 +139,41 @@ async def stream_results() -> AsyncGenerator[str, None]: else: return completion + async def create_embedding( + self, + raw_request: Request, + request_body: CreateEmbeddingRequest, + response: Response, + ) -> Response: + """Create embedding handler. + + Args: + raw_request (Request): fastapi request object, + request_body (CreateEmbeddingRequest): Embedding params body. + response (Response): fastapi response object + + Returns: + InferenceResponse: Inference response object. + """ + try: + params = EmbeddingRequestAdapter.validate_python(request_body) + except ValidationError as e: + raise RequestValidationError(errors=e.errors()) + params = request_body + model_name = params.model + model_ready = await self.dataplane.model_ready(model_name) + + if not model_ready: + raise ModelNotReady(model_name) + + request_headers = raw_request.headers + return await self.dataplane.create_embedding( + model_name=model_name, + request=request_body, + headers=request_headers, + response=response, + ) + async def models( self, ) -> ListModelsResponse: @@ -180,6 +217,13 @@ def register_openai_endpoints(app: FastAPI, dataplane: OpenAIDataPlane): response_model_exclude_none=True, response_model_exclude_unset=True, ) + openai_router.add_api_route( + r"/v1/embeddings", + endpoints.create_embedding, + methods=["POST"], + response_model_exclude_none=True, + response_model_exclude_unset=True, + ) openai_router.add_api_route( r"/v1/models", endpoints.models, diff --git a/python/kserve/kserve/protocol/rest/openai/openai_chat_adapter_model.py b/python/kserve/kserve/protocol/rest/openai/openai_chat_adapter_model.py index 83002c3b854..f7ecef64252 100644 --- a/python/kserve/kserve/protocol/rest/openai/openai_chat_adapter_model.py +++ b/python/kserve/kserve/protocol/rest/openai/openai_chat_adapter_model.py @@ -35,7 +35,7 @@ from ....errors import InvalidInput from .openai_model import ( - OpenAIModel, + OpenAICompletionModel, ChatPrompt, CompletionRequest, ChatCompletionRequest, @@ -43,7 +43,7 @@ ) -class OpenAIChatAdapterModel(OpenAIModel): +class OpenAIChatAdapterModel(OpenAICompletionModel): """ A helper on top the OpenAI model that automatically maps chat completion requests (/v1/chat/completions) to completion requests (/v1/completions). diff --git a/python/kserve/kserve/protocol/rest/openai/openai_model.py b/python/kserve/kserve/protocol/rest/openai/openai_model.py index 0a169d04a06..5a92545ddec 100644 --- a/python/kserve/kserve/protocol/rest/openai/openai_model.py +++ b/python/kserve/kserve/protocol/rest/openai/openai_model.py @@ -24,6 +24,8 @@ Completion, CreateChatCompletionRequest, CreateCompletionRequest, + CreateEmbeddingRequest, + Embedding, ) from ....model import BaseKServeModel @@ -34,27 +36,29 @@ class ChatPrompt(BaseModel): prompt: str -class BaseCompletionRequest(BaseModel): +class BaseOpenAIRequest(BaseModel): request_id: Optional[str] = None context: Optional[Dict[str, Any]] = None # headers can go in here - params: Union[CreateCompletionRequest, CreateChatCompletionRequest] + params: Union[ + CreateCompletionRequest, CreateChatCompletionRequest, CreateEmbeddingRequest + ] -class CompletionRequest(BaseCompletionRequest): +class CompletionRequest(BaseOpenAIRequest): params: CreateCompletionRequest -class ChatCompletionRequest(BaseCompletionRequest): +class ChatCompletionRequest(BaseOpenAIRequest): params: CreateChatCompletionRequest +class EmbeddingRequest(BaseOpenAIRequest): + params: CreateEmbeddingRequest + + class OpenAIModel(BaseKServeModel): """ - An abstract model with methods for implementing OpenAI's completions (v1/completions) - and chat completions (v1/chat/completions) endpoints. - - Users should extend this model and implement the abstract methods in order to expose - these endpoints. + An abstract model with methods for implementing OpenAI's endpoints. """ def __init__(self, name: str): @@ -64,6 +68,16 @@ def __init__(self, name: str): # Assume the model is ready self.ready = True + +class OpenAICompletionModel(OpenAIModel): + """ + An abstract model with methods for implementing OpenAI's completions (v1/completions) + and chat completions (v1/chat/completions) endpoints. + + Users should extend this model and implement the abstract methods in order to expose + these endpoints. + """ + @abstractmethod async def create_completion( self, request: CompletionRequest @@ -77,6 +91,20 @@ async def create_chat_completion( pass +class OpenAIEmbeddingModel(OpenAIModel): + """ + An abstract model with a method for implementing OpenAI's embeddings (/v1/embeddings) + endpoint. + + Users should extend this model and implement the abstract methods in order to expose + these endpoints. + """ + + @abstractmethod + async def create_embedding(self, request: EmbeddingRequest) -> Embedding: + pass + + class AsyncMappingIterator: def __init__( self, diff --git a/python/kserve/kserve/protocol/rest/openai/openai_proxy_model.py b/python/kserve/kserve/protocol/rest/openai/openai_proxy_model.py index 59ec4ac9c23..ae3d9d95aee 100644 --- a/python/kserve/kserve/protocol/rest/openai/openai_proxy_model.py +++ b/python/kserve/kserve/protocol/rest/openai/openai_proxy_model.py @@ -22,8 +22,8 @@ from .openai_model import ( - BaseCompletionRequest, - OpenAIModel, + BaseOpenAIRequest, + OpenAICompletionModel, AsyncMappingIterator, CompletionRequest, ChatCompletionRequest, @@ -91,7 +91,7 @@ async def wrapper(*args, **kwargs): return wrapper -class OpenAIProxyModel(OpenAIModel): +class OpenAIProxyModel(OpenAICompletionModel): """ An implementation of OpenAIModel that proxies requests to a backend server exposing Open AI endpoints. @@ -218,7 +218,7 @@ def _handle_chat_completion_chunk( return chat_completion_chunk def _build_request( - self, endpoint: str, request: BaseCompletionRequest + self, endpoint: str, request: BaseOpenAIRequest ) -> httpx.Request: if request.context and "upstream_headers" in request.context: diff --git a/python/kserve/kserve/protocol/rest/openai/types/__init__.py b/python/kserve/kserve/protocol/rest/openai/types/__init__.py index 3400c917014..fae8def9ceb 100644 --- a/python/kserve/kserve/protocol/rest/openai/types/__init__.py +++ b/python/kserve/kserve/protocol/rest/openai/types/__init__.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Union +from typing import Union, List from kserve.protocol.rest.openai.types.openapi import ( ChatCompletionRequestAssistantMessage, @@ -40,6 +40,11 @@ from kserve.protocol.rest.openai.types.openapi import ( CreateCompletionResponse as Completion, ) +from kserve.protocol.rest.openai.types.openapi import CreateEmbeddingRequest +from kserve.protocol.rest.openai.types.openapi import ( + CreateEmbeddingResponse as Embedding, + Embedding as BaseEmbeddingObject, +) from kserve.protocol.rest.openai.types.openapi import Logprobs from kserve.protocol.rest.openai.types.openapi import ( Logprobs2 as ChatCompletionChoiceLogprobs, @@ -56,6 +61,12 @@ ChatCompletionRequestFunctionMessage, ] + +# The autogenerated OpenAPI specification doesn't seem to include the base64 encoding option. Add it manually. +class EmbeddingObject(BaseEmbeddingObject): + embedding: Union[List[float], str] + + __all__ = [ "ChatCompletion", "ChatCompletionChoice", @@ -76,6 +87,9 @@ "CompletionChoice", "CreateChatCompletionRequest", "CreateCompletionRequest", + "CreateEmbeddingRequest", + "Embedding", + "EmbeddingObject", "ErrorResponse", "Logprobs", "TopLogprob", diff --git a/python/kserve/kserve/protocol/rest/server.py b/python/kserve/kserve/protocol/rest/server.py index b7d89e9a2f7..15d89b6003e 100644 --- a/python/kserve/kserve/protocol/rest/server.py +++ b/python/kserve/kserve/protocol/rest/server.py @@ -40,7 +40,7 @@ server_not_ready_handler, unsupported_protocol_error_handler, ) -from kserve.logging import trace_logger +from kserve.logging import trace_logger, logger from kserve.protocol.dataplane import DataPlane from .openai.config import maybe_register_openai_endpoints @@ -59,24 +59,32 @@ def timing(self, metric_name, timing, tags): trace_logger.info(f"{metric_name}: {timing} {tags}") -class _NoSignalUvicornServer(uvicorn.Server): - def install_signal_handlers(self) -> None: - pass - - class RESTServer: def __init__( self, app: FastAPI, data_plane: DataPlane, model_repository_extension: ModelRepositoryExtension, + http_port: int, + log_config: Optional[Union[str, Dict]] = None, + access_log_format: Optional[str] = None, + workers: int = 1, ): self.app = app self.dataplane = data_plane self.model_repository_extension = model_repository_extension + self.access_log_format = access_log_format + self._server = uvicorn.Server( + config=uvicorn.Config( + app="kserve.model_server:app", + host="0.0.0.0", + log_config=log_config, + port=http_port, + workers=workers, + ) + ) - def create_application(self): - """Create a KServe ModelServer application with API routes.""" + def _register_endpoints(self): root_router = APIRouter() root_router.add_api_route(r"/", self.dataplane.live) root_router.add_api_route(r"/metrics", metrics_handler, methods=["GET"]) @@ -88,7 +96,7 @@ def create_application(self): # REST server. maybe_register_openai_endpoints(self.app, self.dataplane.model_registry) - # Add exception handlers + def _add_exception_handlers(self): self.app.add_exception_handler(InvalidInput, invalid_input_handler) self.app.add_exception_handler(InferenceError, inference_error_handler) self.app.add_exception_handler(ModelNotFound, model_not_found_handler) @@ -103,32 +111,13 @@ def create_application(self): self.app.add_exception_handler(ServerNotReady, server_not_ready_handler) self.app.add_exception_handler(Exception, generic_exception_handler) - -class UvicornServer: - def __init__( - self, - app: FastAPI, - http_port: int, - data_plane: DataPlane, - model_repository_extension, - log_config: Optional[Union[str, Dict]] = None, - access_log_format: Optional[str] = None, - workers: int = 1, - ): - super().__init__() - rest_server = RESTServer(app, data_plane, model_repository_extension) - rest_server.create_application() - app.add_middleware( + def _add_middlewares(self): + self.app.add_middleware( TimingMiddleware, client=PrintTimings(), - metric_namer=StarletteScopeToName(prefix="kserve.io", starlette_app=app), - ) - self.cfg = uvicorn.Config( - app="kserve.model_server:app", - host="0.0.0.0", - log_config=log_config, - port=http_port, - workers=workers, + metric_namer=StarletteScopeToName( + prefix="kserve.io", starlette_app=self.app + ), ) # More context in https://github.com/encode/uvicorn/pull/947 @@ -136,22 +125,25 @@ def __init__( # to change the access log format, and hence the Uvicorn upstream devs # chose to create a custom middleware for this. # The allowed log format is specified in https://github.com/Kludex/asgi-logger#usage - if access_log_format: + if self.access_log_format: from asgi_logger import AccessLoggerMiddleware # As indicated by the asgi-logger docs, we need to clear/unset # any setting for uvicorn.access to avoid log duplicates. logging.getLogger("uvicorn.access").handlers = [] - app.add_middleware(AccessLoggerMiddleware, format=access_log_format) + self.app.add_middleware( + AccessLoggerMiddleware, format=self.access_log_format + ) # The asgi-logger settings don't set propagate to False, # so we get duplicates if we don't set it explicitly. logging.getLogger("access").propagate = False - self.server = _NoSignalUvicornServer(config=self.cfg) - - async def run(self): - await self.server.serve() - - async def stop(self, sig: Optional[int] = None): - if self.server: - self.server.handle_exit(sig=sig, frame=None) + def create_application(self): + self._add_middlewares() + self._register_endpoints() + self._add_exception_handlers() + + async def start(self): + self.create_application() + logger.info(f"Starting uvicorn with {self._server.config.workers} workers") + await self._server.serve() diff --git a/python/kserve/kserve/storage/storage.py b/python/kserve/kserve/storage/storage.py index 2f19525409f..89ce7adf79a 100644 --- a/python/kserve/kserve/storage/storage.py +++ b/python/kserve/kserve/storage/storage.py @@ -176,6 +176,17 @@ def get_S3_config(): if accelerate: c = c.merge(Config(s3={"use_accelerate_endpoint": accelerate})) + # NOTE: If endpoint_url provided is legacy ("https://s3.amazonaws.com") and region is not global (us-east-1), set to virtual addressing style + # So that request would not return PermanentRedirect due to region not in the endpoint url + # AWS SDK retries under the hood to set the correct region when the valid virtual addressing style endpoint url is provided + endpoint_url = os.getenv("AWS_ENDPOINT_URL") + region = os.getenv("AWS_DEFAULT_REGION") + if endpoint_url == "https://s3.amazonaws.com" and region not in ( + None, + "us-east-1", + ): + c = c.merge(Config(s3={"addressing_style": "virtual"})) + return c @staticmethod @@ -248,18 +259,13 @@ def _download_s3(uri, temp_dir: str) -> str: # (without any subpaths). # If the bucket path is s3://test/models # Objects: churn, churn-pickle, churn-pickle-logs - bucket_path_last_part = bucket_path.split("/")[-1] - object_last_path = obj.key.split("/")[-1] if bucket_path == obj.key: target_key = obj.key.rsplit("/", 1)[-1] exact_obj_found = True - elif bucket_path_last_part and object_last_path.startswith( - bucket_path_last_part - ): - target_key = object_last_path + else: - target_key = obj.key.replace(bucket_path, "").lstrip("/") + target_key = re.sub(r"^" + re.escape(bucket_path) + r"/?", "", obj.key) target = f"{temp_dir}/{target_key}" if not os.path.exists(os.path.dirname(target)): diff --git a/python/kserve/kserve/storage/test/test_s3_storage.py b/python/kserve/kserve/storage/test/test_s3_storage.py index 58172171ff5..95f0d3843bc 100644 --- a/python/kserve/kserve/storage/test/test_s3_storage.py +++ b/python/kserve/kserve/storage/test/test_s3_storage.py @@ -253,6 +253,17 @@ def test_get_S3_config(): == USE_ACCELERATE_CONFIG.s3["use_accelerate_endpoint"] ) + # tests legacy endpoint url + with mock.patch.dict( + os.environ, + { + "AWS_ENDPOINT_URL": "https://s3.amazonaws.com", + "AWS_DEFAULT_REGION": "eu-west-1", + }, + ): + config8 = Storage.get_S3_config() + assert config8.s3["addressing_style"] == VIRTUAL_CONFIG.s3["addressing_style"] + def test_update_with_storage_spec_s3(monkeypatch): # save the environment and restore it after the test to avoid mutating it @@ -318,3 +329,51 @@ def test_target_startswith_parent_folder_name(mock_storage): == expected_call_args_list("test/artifacts/model", "dest_path", paths)[0] ) mock_boto3_bucket.objects.filter.assert_called_with(Prefix="test/artifacts/model") + + +@mock.patch("boto3.resource") +def test_file_name_preservation(mock_storage): + # given + bucket_name = "local-model" + paths = ["MLmodel"] + object_paths = ["model/" + p for p in paths] + expected_file_name = "MLmodel" # Expected file name after download + + # when + mock_boto3_bucket = create_mock_boto3_bucket(mock_storage, object_paths) + Storage._download_s3(f"s3://{bucket_name}/model", "dest_path") + + # then + arg_list = get_call_args(mock_boto3_bucket.download_file.call_args_list) + assert len(arg_list) == 1 # Ensure only one file was downloaded + downloaded_source, downloaded_target = arg_list[0] + + # Check if the source S3 key matches the original object key + assert ( + downloaded_source == object_paths[0] + ), f"Expected {object_paths[0]}, got {downloaded_source}" + + # Check if the target file path ends with the expected file name + assert downloaded_target.endswith( + expected_file_name + ), f"Expected file name to end with {expected_file_name}, got {downloaded_target}" + + mock_boto3_bucket.objects.filter.assert_called_with(Prefix="model") + + +@mock.patch("boto3.resource") +def test_target_download_path_and_name(mock_storage): + bucket_name = "foo" + paths = ["model.pkl", "a/model.pkl", "conda.yaml"] + object_paths = ["model/" + p for p in paths] + + # when + mock_boto3_bucket = create_mock_boto3_bucket(mock_storage, object_paths) + Storage._download_s3(f"s3://{bucket_name}/model", "dest_path") + + # then + arg_list = get_call_args(mock_boto3_bucket.download_file.call_args_list) + assert arg_list[0] == expected_call_args_list("model", "dest_path", paths)[0] + assert arg_list[1] == expected_call_args_list("model", "dest_path", paths)[1] + + mock_boto3_bucket.objects.filter.assert_called_with(Prefix="model") diff --git a/python/kserve/kserve/utils/inference_client_factory.py b/python/kserve/kserve/utils/inference_client_factory.py index 5fe1bef779b..94dee31266a 100644 --- a/python/kserve/kserve/utils/inference_client_factory.py +++ b/python/kserve/kserve/utils/inference_client_factory.py @@ -14,12 +14,14 @@ from typing import Optional from ..inference_client import InferenceGRPCClient, InferenceRESTClient, RESTConfig +from .utils import is_v2 class InferenceClientFactory: _instance = None _grpc_client: Optional[InferenceGRPCClient] = None - _rest_client: Optional[InferenceRESTClient] = None + _rest_v1_client: Optional[InferenceRESTClient] = None + _rest_v2_client: Optional[InferenceRESTClient] = None def __new__(cls): if cls._instance is None: @@ -32,12 +34,18 @@ def get_grpc_client(self, url: str, **kwargs) -> InferenceGRPCClient: return self._grpc_client def get_rest_client(self, config: RESTConfig = None) -> InferenceRESTClient: - if self._rest_client is None: - self._rest_client = InferenceRESTClient(config) - return self._rest_client + if config and is_v2(config.protocol): + if self._rest_v2_client is None: + self._rest_v2_client = InferenceRESTClient(config) + return self._rest_v2_client + if self._rest_v1_client is None: + self._rest_v1_client = InferenceRESTClient(config) + return self._rest_v1_client async def close(self): if self._grpc_client is not None: await self._grpc_client.close() - if self._rest_client is not None: - await self._rest_client.close() + if self._rest_v1_client is not None: + await self._rest_v1_client.close() + if self._rest_v2_client is not None: + await self._rest_v2_client.close() diff --git a/python/kserve/poetry.lock b/python/kserve/poetry.lock index e127673813a..c52169e5bcb 100644 --- a/python/kserve/poetry.lock +++ b/python/kserve/poetry.lock @@ -13,112 +13,112 @@ files = [ [[package]] name = "aiohttp" -version = "3.10.5" +version = "3.10.11" description = "Async http client/server framework (asyncio)" optional = true python-versions = ">=3.8" files = [ - {file = "aiohttp-3.10.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:18a01eba2574fb9edd5f6e5fb25f66e6ce061da5dab5db75e13fe1558142e0a3"}, - {file = "aiohttp-3.10.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:94fac7c6e77ccb1ca91e9eb4cb0ac0270b9fb9b289738654120ba8cebb1189c6"}, - {file = "aiohttp-3.10.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2f1f1c75c395991ce9c94d3e4aa96e5c59c8356a15b1c9231e783865e2772699"}, - {file = "aiohttp-3.10.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f7acae3cf1a2a2361ec4c8e787eaaa86a94171d2417aae53c0cca6ca3118ff6"}, - {file = "aiohttp-3.10.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:94c4381ffba9cc508b37d2e536b418d5ea9cfdc2848b9a7fea6aebad4ec6aac1"}, - {file = "aiohttp-3.10.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c31ad0c0c507894e3eaa843415841995bf8de4d6b2d24c6e33099f4bc9fc0d4f"}, - {file = "aiohttp-3.10.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0912b8a8fadeb32ff67a3ed44249448c20148397c1ed905d5dac185b4ca547bb"}, - {file = "aiohttp-3.10.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d93400c18596b7dc4794d48a63fb361b01a0d8eb39f28800dc900c8fbdaca91"}, - {file = "aiohttp-3.10.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d00f3c5e0d764a5c9aa5a62d99728c56d455310bcc288a79cab10157b3af426f"}, - {file = "aiohttp-3.10.5-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:d742c36ed44f2798c8d3f4bc511f479b9ceef2b93f348671184139e7d708042c"}, - {file = "aiohttp-3.10.5-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:814375093edae5f1cb31e3407997cf3eacefb9010f96df10d64829362ae2df69"}, - {file = "aiohttp-3.10.5-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8224f98be68a84b19f48e0bdc14224b5a71339aff3a27df69989fa47d01296f3"}, - {file = "aiohttp-3.10.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d9a487ef090aea982d748b1b0d74fe7c3950b109df967630a20584f9a99c0683"}, - {file = "aiohttp-3.10.5-cp310-cp310-win32.whl", hash = "sha256:d9ef084e3dc690ad50137cc05831c52b6ca428096e6deb3c43e95827f531d5ef"}, - {file = "aiohttp-3.10.5-cp310-cp310-win_amd64.whl", hash = "sha256:66bf9234e08fe561dccd62083bf67400bdbf1c67ba9efdc3dac03650e97c6088"}, - {file = "aiohttp-3.10.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8c6a4e5e40156d72a40241a25cc226051c0a8d816610097a8e8f517aeacd59a2"}, - {file = "aiohttp-3.10.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2c634a3207a5445be65536d38c13791904fda0748b9eabf908d3fe86a52941cf"}, - {file = "aiohttp-3.10.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4aff049b5e629ef9b3e9e617fa6e2dfeda1bf87e01bcfecaf3949af9e210105e"}, - {file = "aiohttp-3.10.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1942244f00baaacaa8155eca94dbd9e8cc7017deb69b75ef67c78e89fdad3c77"}, - {file = "aiohttp-3.10.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e04a1f2a65ad2f93aa20f9ff9f1b672bf912413e5547f60749fa2ef8a644e061"}, - {file = "aiohttp-3.10.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7f2bfc0032a00405d4af2ba27f3c429e851d04fad1e5ceee4080a1c570476697"}, - {file = "aiohttp-3.10.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:424ae21498790e12eb759040bbb504e5e280cab64693d14775c54269fd1d2bb7"}, - {file = "aiohttp-3.10.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:975218eee0e6d24eb336d0328c768ebc5d617609affaca5dbbd6dd1984f16ed0"}, - {file = "aiohttp-3.10.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4120d7fefa1e2d8fb6f650b11489710091788de554e2b6f8347c7a20ceb003f5"}, - {file = "aiohttp-3.10.5-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:b90078989ef3fc45cf9221d3859acd1108af7560c52397ff4ace8ad7052a132e"}, - {file = "aiohttp-3.10.5-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ba5a8b74c2a8af7d862399cdedce1533642fa727def0b8c3e3e02fcb52dca1b1"}, - {file = "aiohttp-3.10.5-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:02594361128f780eecc2a29939d9dfc870e17b45178a867bf61a11b2a4367277"}, - {file = "aiohttp-3.10.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8fb4fc029e135859f533025bc82047334e24b0d489e75513144f25408ecaf058"}, - {file = "aiohttp-3.10.5-cp311-cp311-win32.whl", hash = "sha256:e1ca1ef5ba129718a8fc827b0867f6aa4e893c56eb00003b7367f8a733a9b072"}, - {file = "aiohttp-3.10.5-cp311-cp311-win_amd64.whl", hash = "sha256:349ef8a73a7c5665cca65c88ab24abe75447e28aa3bc4c93ea5093474dfdf0ff"}, - {file = "aiohttp-3.10.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:305be5ff2081fa1d283a76113b8df7a14c10d75602a38d9f012935df20731487"}, - {file = "aiohttp-3.10.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3a1c32a19ee6bbde02f1cb189e13a71b321256cc1d431196a9f824050b160d5a"}, - {file = "aiohttp-3.10.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:61645818edd40cc6f455b851277a21bf420ce347baa0b86eaa41d51ef58ba23d"}, - {file = "aiohttp-3.10.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c225286f2b13bab5987425558baa5cbdb2bc925b2998038fa028245ef421e75"}, - {file = "aiohttp-3.10.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ba01ebc6175e1e6b7275c907a3a36be48a2d487549b656aa90c8a910d9f3178"}, - {file = "aiohttp-3.10.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8eaf44ccbc4e35762683078b72bf293f476561d8b68ec8a64f98cf32811c323e"}, - {file = "aiohttp-3.10.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1c43eb1ab7cbf411b8e387dc169acb31f0ca0d8c09ba63f9eac67829585b44f"}, - {file = "aiohttp-3.10.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:de7a5299827253023c55ea549444e058c0eb496931fa05d693b95140a947cb73"}, - {file = "aiohttp-3.10.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4790f0e15f00058f7599dab2b206d3049d7ac464dc2e5eae0e93fa18aee9e7bf"}, - {file = "aiohttp-3.10.5-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:44b324a6b8376a23e6ba25d368726ee3bc281e6ab306db80b5819999c737d820"}, - {file = "aiohttp-3.10.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:0d277cfb304118079e7044aad0b76685d30ecb86f83a0711fc5fb257ffe832ca"}, - {file = "aiohttp-3.10.5-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:54d9ddea424cd19d3ff6128601a4a4d23d54a421f9b4c0fff740505813739a91"}, - {file = "aiohttp-3.10.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4f1c9866ccf48a6df2b06823e6ae80573529f2af3a0992ec4fe75b1a510df8a6"}, - {file = "aiohttp-3.10.5-cp312-cp312-win32.whl", hash = "sha256:dc4826823121783dccc0871e3f405417ac116055bf184ac04c36f98b75aacd12"}, - {file = "aiohttp-3.10.5-cp312-cp312-win_amd64.whl", hash = "sha256:22c0a23a3b3138a6bf76fc553789cb1a703836da86b0f306b6f0dc1617398abc"}, - {file = "aiohttp-3.10.5-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7f6b639c36734eaa80a6c152a238242bedcee9b953f23bb887e9102976343092"}, - {file = "aiohttp-3.10.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f29930bc2921cef955ba39a3ff87d2c4398a0394ae217f41cb02d5c26c8b1b77"}, - {file = "aiohttp-3.10.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f489a2c9e6455d87eabf907ac0b7d230a9786be43fbe884ad184ddf9e9c1e385"}, - {file = "aiohttp-3.10.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:123dd5b16b75b2962d0fff566effb7a065e33cd4538c1692fb31c3bda2bfb972"}, - {file = "aiohttp-3.10.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b98e698dc34966e5976e10bbca6d26d6724e6bdea853c7c10162a3235aba6e16"}, - {file = "aiohttp-3.10.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3b9162bab7e42f21243effc822652dc5bb5e8ff42a4eb62fe7782bcbcdfacf6"}, - {file = "aiohttp-3.10.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1923a5c44061bffd5eebeef58cecf68096e35003907d8201a4d0d6f6e387ccaa"}, - {file = "aiohttp-3.10.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d55f011da0a843c3d3df2c2cf4e537b8070a419f891c930245f05d329c4b0689"}, - {file = "aiohttp-3.10.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:afe16a84498441d05e9189a15900640a2d2b5e76cf4efe8cbb088ab4f112ee57"}, - {file = "aiohttp-3.10.5-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f8112fb501b1e0567a1251a2fd0747baae60a4ab325a871e975b7bb67e59221f"}, - {file = "aiohttp-3.10.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:1e72589da4c90337837fdfe2026ae1952c0f4a6e793adbbfbdd40efed7c63599"}, - {file = "aiohttp-3.10.5-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:4d46c7b4173415d8e583045fbc4daa48b40e31b19ce595b8d92cf639396c15d5"}, - {file = "aiohttp-3.10.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:33e6bc4bab477c772a541f76cd91e11ccb6d2efa2b8d7d7883591dfb523e5987"}, - {file = "aiohttp-3.10.5-cp313-cp313-win32.whl", hash = "sha256:c58c6837a2c2a7cf3133983e64173aec11f9c2cd8e87ec2fdc16ce727bcf1a04"}, - {file = "aiohttp-3.10.5-cp313-cp313-win_amd64.whl", hash = "sha256:38172a70005252b6893088c0f5e8a47d173df7cc2b2bd88650957eb84fcf5022"}, - {file = "aiohttp-3.10.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:f6f18898ace4bcd2d41a122916475344a87f1dfdec626ecde9ee802a711bc569"}, - {file = "aiohttp-3.10.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5ede29d91a40ba22ac1b922ef510aab871652f6c88ef60b9dcdf773c6d32ad7a"}, - {file = "aiohttp-3.10.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:673f988370f5954df96cc31fd99c7312a3af0a97f09e407399f61583f30da9bc"}, - {file = "aiohttp-3.10.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58718e181c56a3c02d25b09d4115eb02aafe1a732ce5714ab70326d9776457c3"}, - {file = "aiohttp-3.10.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4b38b1570242fbab8d86a84128fb5b5234a2f70c2e32f3070143a6d94bc854cf"}, - {file = "aiohttp-3.10.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:074d1bff0163e107e97bd48cad9f928fa5a3eb4b9d33366137ffce08a63e37fe"}, - {file = "aiohttp-3.10.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd31f176429cecbc1ba499d4aba31aaccfea488f418d60376b911269d3b883c5"}, - {file = "aiohttp-3.10.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7384d0b87d4635ec38db9263e6a3f1eb609e2e06087f0aa7f63b76833737b471"}, - {file = "aiohttp-3.10.5-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:8989f46f3d7ef79585e98fa991e6ded55d2f48ae56d2c9fa5e491a6e4effb589"}, - {file = "aiohttp-3.10.5-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:c83f7a107abb89a227d6c454c613e7606c12a42b9a4ca9c5d7dad25d47c776ae"}, - {file = "aiohttp-3.10.5-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:cde98f323d6bf161041e7627a5fd763f9fd829bcfcd089804a5fdce7bb6e1b7d"}, - {file = "aiohttp-3.10.5-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:676f94c5480d8eefd97c0c7e3953315e4d8c2b71f3b49539beb2aa676c58272f"}, - {file = "aiohttp-3.10.5-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:2d21ac12dc943c68135ff858c3a989f2194a709e6e10b4c8977d7fcd67dfd511"}, - {file = "aiohttp-3.10.5-cp38-cp38-win32.whl", hash = "sha256:17e997105bd1a260850272bfb50e2a328e029c941c2708170d9d978d5a30ad9a"}, - {file = "aiohttp-3.10.5-cp38-cp38-win_amd64.whl", hash = "sha256:1c19de68896747a2aa6257ae4cf6ef59d73917a36a35ee9d0a6f48cff0f94db8"}, - {file = "aiohttp-3.10.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7e2fe37ac654032db1f3499fe56e77190282534810e2a8e833141a021faaab0e"}, - {file = "aiohttp-3.10.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f5bf3ead3cb66ab990ee2561373b009db5bc0e857549b6c9ba84b20bc462e172"}, - {file = "aiohttp-3.10.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1b2c16a919d936ca87a3c5f0e43af12a89a3ce7ccbce59a2d6784caba945b68b"}, - {file = "aiohttp-3.10.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad146dae5977c4dd435eb31373b3fe9b0b1bf26858c6fc452bf6af394067e10b"}, - {file = "aiohttp-3.10.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8c5c6fa16412b35999320f5c9690c0f554392dc222c04e559217e0f9ae244b92"}, - {file = "aiohttp-3.10.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:95c4dc6f61d610bc0ee1edc6f29d993f10febfe5b76bb470b486d90bbece6b22"}, - {file = "aiohttp-3.10.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da452c2c322e9ce0cfef392e469a26d63d42860f829026a63374fde6b5c5876f"}, - {file = "aiohttp-3.10.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:898715cf566ec2869d5cb4d5fb4be408964704c46c96b4be267442d265390f32"}, - {file = "aiohttp-3.10.5-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:391cc3a9c1527e424c6865e087897e766a917f15dddb360174a70467572ac6ce"}, - {file = "aiohttp-3.10.5-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:380f926b51b92d02a34119d072f178d80bbda334d1a7e10fa22d467a66e494db"}, - {file = "aiohttp-3.10.5-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ce91db90dbf37bb6fa0997f26574107e1b9d5ff939315247b7e615baa8ec313b"}, - {file = "aiohttp-3.10.5-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9093a81e18c45227eebe4c16124ebf3e0d893830c6aca7cc310bfca8fe59d857"}, - {file = "aiohttp-3.10.5-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ee40b40aa753d844162dcc80d0fe256b87cba48ca0054f64e68000453caead11"}, - {file = "aiohttp-3.10.5-cp39-cp39-win32.whl", hash = "sha256:03f2645adbe17f274444953bdea69f8327e9d278d961d85657cb0d06864814c1"}, - {file = "aiohttp-3.10.5-cp39-cp39-win_amd64.whl", hash = "sha256:d17920f18e6ee090bdd3d0bfffd769d9f2cb4c8ffde3eb203777a3895c128862"}, - {file = "aiohttp-3.10.5.tar.gz", hash = "sha256:f071854b47d39591ce9a17981c46790acb30518e2f83dfca8db2dfa091178691"}, + {file = "aiohttp-3.10.11-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5077b1a5f40ffa3ba1f40d537d3bec4383988ee51fbba6b74aa8fb1bc466599e"}, + {file = "aiohttp-3.10.11-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8d6a14a4d93b5b3c2891fca94fa9d41b2322a68194422bef0dd5ec1e57d7d298"}, + {file = "aiohttp-3.10.11-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ffbfde2443696345e23a3c597049b1dd43049bb65337837574205e7368472177"}, + {file = "aiohttp-3.10.11-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20b3d9e416774d41813bc02fdc0663379c01817b0874b932b81c7f777f67b217"}, + {file = "aiohttp-3.10.11-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2b943011b45ee6bf74b22245c6faab736363678e910504dd7531a58c76c9015a"}, + {file = "aiohttp-3.10.11-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48bc1d924490f0d0b3658fe5c4b081a4d56ebb58af80a6729d4bd13ea569797a"}, + {file = "aiohttp-3.10.11-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e12eb3f4b1f72aaaf6acd27d045753b18101524f72ae071ae1c91c1cd44ef115"}, + {file = "aiohttp-3.10.11-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f14ebc419a568c2eff3c1ed35f634435c24ead2fe19c07426af41e7adb68713a"}, + {file = "aiohttp-3.10.11-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:72b191cdf35a518bfc7ca87d770d30941decc5aaf897ec8b484eb5cc8c7706f3"}, + {file = "aiohttp-3.10.11-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5ab2328a61fdc86424ee540d0aeb8b73bbcad7351fb7cf7a6546fc0bcffa0038"}, + {file = "aiohttp-3.10.11-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:aa93063d4af05c49276cf14e419550a3f45258b6b9d1f16403e777f1addf4519"}, + {file = "aiohttp-3.10.11-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:30283f9d0ce420363c24c5c2421e71a738a2155f10adbb1a11a4d4d6d2715cfc"}, + {file = "aiohttp-3.10.11-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e5358addc8044ee49143c546d2182c15b4ac3a60be01c3209374ace05af5733d"}, + {file = "aiohttp-3.10.11-cp310-cp310-win32.whl", hash = "sha256:e1ffa713d3ea7cdcd4aea9cddccab41edf6882fa9552940344c44e59652e1120"}, + {file = "aiohttp-3.10.11-cp310-cp310-win_amd64.whl", hash = "sha256:778cbd01f18ff78b5dd23c77eb82987ee4ba23408cbed233009fd570dda7e674"}, + {file = "aiohttp-3.10.11-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:80ff08556c7f59a7972b1e8919f62e9c069c33566a6d28586771711e0eea4f07"}, + {file = "aiohttp-3.10.11-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2c8f96e9ee19f04c4914e4e7a42a60861066d3e1abf05c726f38d9d0a466e695"}, + {file = "aiohttp-3.10.11-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fb8601394d537da9221947b5d6e62b064c9a43e88a1ecd7414d21a1a6fba9c24"}, + {file = "aiohttp-3.10.11-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ea224cf7bc2d8856d6971cea73b1d50c9c51d36971faf1abc169a0d5f85a382"}, + {file = "aiohttp-3.10.11-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:db9503f79e12d5d80b3efd4d01312853565c05367493379df76d2674af881caa"}, + {file = "aiohttp-3.10.11-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0f449a50cc33f0384f633894d8d3cd020e3ccef81879c6e6245c3c375c448625"}, + {file = "aiohttp-3.10.11-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82052be3e6d9e0c123499127782a01a2b224b8af8c62ab46b3f6197035ad94e9"}, + {file = "aiohttp-3.10.11-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:20063c7acf1eec550c8eb098deb5ed9e1bb0521613b03bb93644b810986027ac"}, + {file = "aiohttp-3.10.11-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:489cced07a4c11488f47aab1f00d0c572506883f877af100a38f1fedaa884c3a"}, + {file = "aiohttp-3.10.11-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ea9b3bab329aeaa603ed3bf605f1e2a6f36496ad7e0e1aa42025f368ee2dc07b"}, + {file = "aiohttp-3.10.11-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ca117819d8ad113413016cb29774b3f6d99ad23c220069789fc050267b786c16"}, + {file = "aiohttp-3.10.11-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:2dfb612dcbe70fb7cdcf3499e8d483079b89749c857a8f6e80263b021745c730"}, + {file = "aiohttp-3.10.11-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9b615d3da0d60e7d53c62e22b4fd1c70f4ae5993a44687b011ea3a2e49051b8"}, + {file = "aiohttp-3.10.11-cp311-cp311-win32.whl", hash = "sha256:29103f9099b6068bbdf44d6a3d090e0a0b2be6d3c9f16a070dd9d0d910ec08f9"}, + {file = "aiohttp-3.10.11-cp311-cp311-win_amd64.whl", hash = "sha256:236b28ceb79532da85d59aa9b9bf873b364e27a0acb2ceaba475dc61cffb6f3f"}, + {file = "aiohttp-3.10.11-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:7480519f70e32bfb101d71fb9a1f330fbd291655a4c1c922232a48c458c52710"}, + {file = "aiohttp-3.10.11-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f65267266c9aeb2287a6622ee2bb39490292552f9fbf851baabc04c9f84e048d"}, + {file = "aiohttp-3.10.11-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7400a93d629a0608dc1d6c55f1e3d6e07f7375745aaa8bd7f085571e4d1cee97"}, + {file = "aiohttp-3.10.11-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f34b97e4b11b8d4eb2c3a4f975be626cc8af99ff479da7de49ac2c6d02d35725"}, + {file = "aiohttp-3.10.11-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1e7b825da878464a252ccff2958838f9caa82f32a8dbc334eb9b34a026e2c636"}, + {file = "aiohttp-3.10.11-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f9f92a344c50b9667827da308473005f34767b6a2a60d9acff56ae94f895f385"}, + {file = "aiohttp-3.10.11-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc6f1ab987a27b83c5268a17218463c2ec08dbb754195113867a27b166cd6087"}, + {file = "aiohttp-3.10.11-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1dc0f4ca54842173d03322793ebcf2c8cc2d34ae91cc762478e295d8e361e03f"}, + {file = "aiohttp-3.10.11-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7ce6a51469bfaacff146e59e7fb61c9c23006495d11cc24c514a455032bcfa03"}, + {file = "aiohttp-3.10.11-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:aad3cd91d484d065ede16f3cf15408254e2469e3f613b241a1db552c5eb7ab7d"}, + {file = "aiohttp-3.10.11-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f4df4b8ca97f658c880fb4b90b1d1ec528315d4030af1ec763247ebfd33d8b9a"}, + {file = "aiohttp-3.10.11-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:2e4e18a0a2d03531edbc06c366954e40a3f8d2a88d2b936bbe78a0c75a3aab3e"}, + {file = "aiohttp-3.10.11-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6ce66780fa1a20e45bc753cda2a149daa6dbf1561fc1289fa0c308391c7bc0a4"}, + {file = "aiohttp-3.10.11-cp312-cp312-win32.whl", hash = "sha256:a919c8957695ea4c0e7a3e8d16494e3477b86f33067478f43106921c2fef15bb"}, + {file = "aiohttp-3.10.11-cp312-cp312-win_amd64.whl", hash = "sha256:b5e29706e6389a2283a91611c91bf24f218962717c8f3b4e528ef529d112ee27"}, + {file = "aiohttp-3.10.11-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:703938e22434d7d14ec22f9f310559331f455018389222eed132808cd8f44127"}, + {file = "aiohttp-3.10.11-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9bc50b63648840854e00084c2b43035a62e033cb9b06d8c22b409d56eb098413"}, + {file = "aiohttp-3.10.11-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5f0463bf8b0754bc744e1feb61590706823795041e63edf30118a6f0bf577461"}, + {file = "aiohttp-3.10.11-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f6c6dec398ac5a87cb3a407b068e1106b20ef001c344e34154616183fe684288"}, + {file = "aiohttp-3.10.11-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bcaf2d79104d53d4dcf934f7ce76d3d155302d07dae24dff6c9fffd217568067"}, + {file = "aiohttp-3.10.11-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:25fd5470922091b5a9aeeb7e75be609e16b4fba81cdeaf12981393fb240dd10e"}, + {file = "aiohttp-3.10.11-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbde2ca67230923a42161b1f408c3992ae6e0be782dca0c44cb3206bf330dee1"}, + {file = "aiohttp-3.10.11-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:249c8ff8d26a8b41a0f12f9df804e7c685ca35a207e2410adbd3e924217b9006"}, + {file = "aiohttp-3.10.11-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:878ca6a931ee8c486a8f7b432b65431d095c522cbeb34892bee5be97b3481d0f"}, + {file = "aiohttp-3.10.11-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:8663f7777ce775f0413324be0d96d9730959b2ca73d9b7e2c2c90539139cbdd6"}, + {file = "aiohttp-3.10.11-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:6cd3f10b01f0c31481fba8d302b61603a2acb37b9d30e1d14e0f5a58b7b18a31"}, + {file = "aiohttp-3.10.11-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:4e8d8aad9402d3aa02fdc5ca2fe68bcb9fdfe1f77b40b10410a94c7f408b664d"}, + {file = "aiohttp-3.10.11-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:38e3c4f80196b4f6c3a85d134a534a56f52da9cb8d8e7af1b79a32eefee73a00"}, + {file = "aiohttp-3.10.11-cp313-cp313-win32.whl", hash = "sha256:fc31820cfc3b2863c6e95e14fcf815dc7afe52480b4dc03393c4873bb5599f71"}, + {file = "aiohttp-3.10.11-cp313-cp313-win_amd64.whl", hash = "sha256:4996ff1345704ffdd6d75fb06ed175938c133425af616142e7187f28dc75f14e"}, + {file = "aiohttp-3.10.11-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:74baf1a7d948b3d640badeac333af581a367ab916b37e44cf90a0334157cdfd2"}, + {file = "aiohttp-3.10.11-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:473aebc3b871646e1940c05268d451f2543a1d209f47035b594b9d4e91ce8339"}, + {file = "aiohttp-3.10.11-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c2f746a6968c54ab2186574e15c3f14f3e7f67aef12b761e043b33b89c5b5f95"}, + {file = "aiohttp-3.10.11-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d110cabad8360ffa0dec8f6ec60e43286e9d251e77db4763a87dcfe55b4adb92"}, + {file = "aiohttp-3.10.11-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e0099c7d5d7afff4202a0c670e5b723f7718810000b4abcbc96b064129e64bc7"}, + {file = "aiohttp-3.10.11-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0316e624b754dbbf8c872b62fe6dcb395ef20c70e59890dfa0de9eafccd2849d"}, + {file = "aiohttp-3.10.11-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a5f7ab8baf13314e6b2485965cbacb94afff1e93466ac4d06a47a81c50f9cca"}, + {file = "aiohttp-3.10.11-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c891011e76041e6508cbfc469dd1a8ea09bc24e87e4c204e05f150c4c455a5fa"}, + {file = "aiohttp-3.10.11-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:9208299251370ee815473270c52cd3f7069ee9ed348d941d574d1457d2c73e8b"}, + {file = "aiohttp-3.10.11-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:459f0f32c8356e8125f45eeff0ecf2b1cb6db1551304972702f34cd9e6c44658"}, + {file = "aiohttp-3.10.11-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:14cdc8c1810bbd4b4b9f142eeee23cda528ae4e57ea0923551a9af4820980e39"}, + {file = "aiohttp-3.10.11-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:971aa438a29701d4b34e4943e91b5e984c3ae6ccbf80dd9efaffb01bd0b243a9"}, + {file = "aiohttp-3.10.11-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:9a309c5de392dfe0f32ee57fa43ed8fc6ddf9985425e84bd51ed66bb16bce3a7"}, + {file = "aiohttp-3.10.11-cp38-cp38-win32.whl", hash = "sha256:9ec1628180241d906a0840b38f162a3215114b14541f1a8711c368a8739a9be4"}, + {file = "aiohttp-3.10.11-cp38-cp38-win_amd64.whl", hash = "sha256:9c6e0ffd52c929f985c7258f83185d17c76d4275ad22e90aa29f38e211aacbec"}, + {file = "aiohttp-3.10.11-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:cdc493a2e5d8dc79b2df5bec9558425bcd39aff59fc949810cbd0832e294b106"}, + {file = "aiohttp-3.10.11-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b3e70f24e7d0405be2348da9d5a7836936bf3a9b4fd210f8c37e8d48bc32eca6"}, + {file = "aiohttp-3.10.11-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:968b8fb2a5eee2770eda9c7b5581587ef9b96fbdf8dcabc6b446d35ccc69df01"}, + {file = "aiohttp-3.10.11-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:deef4362af9493d1382ef86732ee2e4cbc0d7c005947bd54ad1a9a16dd59298e"}, + {file = "aiohttp-3.10.11-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:686b03196976e327412a1b094f4120778c7c4b9cff9bce8d2fdfeca386b89829"}, + {file = "aiohttp-3.10.11-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3bf6d027d9d1d34e1c2e1645f18a6498c98d634f8e373395221121f1c258ace8"}, + {file = "aiohttp-3.10.11-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:099fd126bf960f96d34a760e747a629c27fb3634da5d05c7ef4d35ef4ea519fc"}, + {file = "aiohttp-3.10.11-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c73c4d3dae0b4644bc21e3de546530531d6cdc88659cdeb6579cd627d3c206aa"}, + {file = "aiohttp-3.10.11-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0c5580f3c51eea91559db3facd45d72e7ec970b04528b4709b1f9c2555bd6d0b"}, + {file = "aiohttp-3.10.11-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:fdf6429f0caabfd8a30c4e2eaecb547b3c340e4730ebfe25139779b9815ba138"}, + {file = "aiohttp-3.10.11-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:d97187de3c276263db3564bb9d9fad9e15b51ea10a371ffa5947a5ba93ad6777"}, + {file = "aiohttp-3.10.11-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:0acafb350cfb2eba70eb5d271f55e08bd4502ec35e964e18ad3e7d34d71f7261"}, + {file = "aiohttp-3.10.11-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c13ed0c779911c7998a58e7848954bd4d63df3e3575f591e321b19a2aec8df9f"}, + {file = "aiohttp-3.10.11-cp39-cp39-win32.whl", hash = "sha256:22b7c540c55909140f63ab4f54ec2c20d2635c0289cdd8006da46f3327f971b9"}, + {file = "aiohttp-3.10.11-cp39-cp39-win_amd64.whl", hash = "sha256:7b26b1551e481012575dab8e3727b16fe7dd27eb2711d2e63ced7368756268fb"}, + {file = "aiohttp-3.10.11.tar.gz", hash = "sha256:9dc2b8f3dcab2e39e0fa309c8da50c3b55e6f34ab25f1a71d3288f24924d33a7"}, ] [package.dependencies] aiohappyeyeballs = ">=2.3.0" aiosignal = ">=1.1.2" -async-timeout = {version = ">=4.0,<5.0", markers = "python_version < \"3.11\""} +async-timeout = {version = ">=4.0,<6.0", markers = "python_version < \"3.11\""} attrs = ">=17.3.0" frozenlist = ">=1.1.1" multidict = ">=4.5,<7.0" -yarl = ">=1.0,<2.0" +yarl = ">=1.12.0,<2.0" [package.extras] speedups = ["Brotli", "aiodns (>=3.2.0)", "brotlicffi"] @@ -2514,6 +2514,97 @@ files = [ [package.extras] twisted = ["twisted"] +[[package]] +name = "propcache" +version = "0.2.1" +description = "Accelerated property cache" +optional = true +python-versions = ">=3.9" +files = [ + {file = "propcache-0.2.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6b3f39a85d671436ee3d12c017f8fdea38509e4f25b28eb25877293c98c243f6"}, + {file = "propcache-0.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d51fbe4285d5db5d92a929e3e21536ea3dd43732c5b177c7ef03f918dff9f2"}, + {file = "propcache-0.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6445804cf4ec763dc70de65a3b0d9954e868609e83850a47ca4f0cb64bd79fea"}, + {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9479aa06a793c5aeba49ce5c5692ffb51fcd9a7016e017d555d5e2b0045d212"}, + {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9631c5e8b5b3a0fda99cb0d29c18133bca1e18aea9effe55adb3da1adef80d3"}, + {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3156628250f46a0895f1f36e1d4fbe062a1af8718ec3ebeb746f1d23f0c5dc4d"}, + {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b6fb63ae352e13748289f04f37868099e69dba4c2b3e271c46061e82c745634"}, + {file = "propcache-0.2.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:887d9b0a65404929641a9fabb6452b07fe4572b269d901d622d8a34a4e9043b2"}, + {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a96dc1fa45bd8c407a0af03b2d5218392729e1822b0c32e62c5bf7eeb5fb3958"}, + {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:a7e65eb5c003a303b94aa2c3852ef130230ec79e349632d030e9571b87c4698c"}, + {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:999779addc413181912e984b942fbcc951be1f5b3663cd80b2687758f434c583"}, + {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:19a0f89a7bb9d8048d9c4370c9c543c396e894c76be5525f5e1ad287f1750ddf"}, + {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:1ac2f5fe02fa75f56e1ad473f1175e11f475606ec9bd0be2e78e4734ad575034"}, + {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:574faa3b79e8ebac7cb1d7930f51184ba1ccf69adfdec53a12f319a06030a68b"}, + {file = "propcache-0.2.1-cp310-cp310-win32.whl", hash = "sha256:03ff9d3f665769b2a85e6157ac8b439644f2d7fd17615a82fa55739bc97863f4"}, + {file = "propcache-0.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:2d3af2e79991102678f53e0dbf4c35de99b6b8b58f29a27ca0325816364caaba"}, + {file = "propcache-0.2.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1ffc3cca89bb438fb9c95c13fc874012f7b9466b89328c3c8b1aa93cdcfadd16"}, + {file = "propcache-0.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f174bbd484294ed9fdf09437f889f95807e5f229d5d93588d34e92106fbf6717"}, + {file = "propcache-0.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:70693319e0b8fd35dd863e3e29513875eb15c51945bf32519ef52927ca883bc3"}, + {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b480c6a4e1138e1aa137c0079b9b6305ec6dcc1098a8ca5196283e8a49df95a9"}, + {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d27b84d5880f6d8aa9ae3edb253c59d9f6642ffbb2c889b78b60361eed449787"}, + {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:857112b22acd417c40fa4595db2fe28ab900c8c5fe4670c7989b1c0230955465"}, + {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf6c4150f8c0e32d241436526f3c3f9cbd34429492abddbada2ffcff506c51af"}, + {file = "propcache-0.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66d4cfda1d8ed687daa4bc0274fcfd5267873db9a5bc0418c2da19273040eeb7"}, + {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c2f992c07c0fca81655066705beae35fc95a2fa7366467366db627d9f2ee097f"}, + {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:4a571d97dbe66ef38e472703067021b1467025ec85707d57e78711c085984e54"}, + {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:bb6178c241278d5fe853b3de743087be7f5f4c6f7d6d22a3b524d323eecec505"}, + {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ad1af54a62ffe39cf34db1aa6ed1a1873bd548f6401db39d8e7cd060b9211f82"}, + {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e7048abd75fe40712005bcfc06bb44b9dfcd8e101dda2ecf2f5aa46115ad07ca"}, + {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:160291c60081f23ee43d44b08a7e5fb76681221a8e10b3139618c5a9a291b84e"}, + {file = "propcache-0.2.1-cp311-cp311-win32.whl", hash = "sha256:819ce3b883b7576ca28da3861c7e1a88afd08cc8c96908e08a3f4dd64a228034"}, + {file = "propcache-0.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:edc9fc7051e3350643ad929df55c451899bb9ae6d24998a949d2e4c87fb596d3"}, + {file = "propcache-0.2.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:081a430aa8d5e8876c6909b67bd2d937bfd531b0382d3fdedb82612c618bc41a"}, + {file = "propcache-0.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d2ccec9ac47cf4e04897619c0e0c1a48c54a71bdf045117d3a26f80d38ab1fb0"}, + {file = "propcache-0.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:14d86fe14b7e04fa306e0c43cdbeebe6b2c2156a0c9ce56b815faacc193e320d"}, + {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:049324ee97bb67285b49632132db351b41e77833678432be52bdd0289c0e05e4"}, + {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1cd9a1d071158de1cc1c71a26014dcdfa7dd3d5f4f88c298c7f90ad6f27bb46d"}, + {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98110aa363f1bb4c073e8dcfaefd3a5cea0f0834c2aab23dda657e4dab2f53b5"}, + {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:647894f5ae99c4cf6bb82a1bb3a796f6e06af3caa3d32e26d2350d0e3e3faf24"}, + {file = "propcache-0.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfd3223c15bebe26518d58ccf9a39b93948d3dcb3e57a20480dfdd315356baff"}, + {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d71264a80f3fcf512eb4f18f59423fe82d6e346ee97b90625f283df56aee103f"}, + {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:e73091191e4280403bde6c9a52a6999d69cdfde498f1fdf629105247599b57ec"}, + {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3935bfa5fede35fb202c4b569bb9c042f337ca4ff7bd540a0aa5e37131659348"}, + {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f508b0491767bb1f2b87fdfacaba5f7eddc2f867740ec69ece6d1946d29029a6"}, + {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:1672137af7c46662a1c2be1e8dc78cb6d224319aaa40271c9257d886be4363a6"}, + {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b74c261802d3d2b85c9df2dfb2fa81b6f90deeef63c2db9f0e029a3cac50b518"}, + {file = "propcache-0.2.1-cp312-cp312-win32.whl", hash = "sha256:d09c333d36c1409d56a9d29b3a1b800a42c76a57a5a8907eacdbce3f18768246"}, + {file = "propcache-0.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:c214999039d4f2a5b2073ac506bba279945233da8c786e490d411dfc30f855c1"}, + {file = "propcache-0.2.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aca405706e0b0a44cc6bfd41fbe89919a6a56999157f6de7e182a990c36e37bc"}, + {file = "propcache-0.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:12d1083f001ace206fe34b6bdc2cb94be66d57a850866f0b908972f90996b3e9"}, + {file = "propcache-0.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d93f3307ad32a27bda2e88ec81134b823c240aa3abb55821a8da553eed8d9439"}, + {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba278acf14471d36316159c94a802933d10b6a1e117b8554fe0d0d9b75c9d536"}, + {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4e6281aedfca15301c41f74d7005e6e3f4ca143584ba696ac69df4f02f40d629"}, + {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5b750a8e5a1262434fb1517ddf64b5de58327f1adc3524a5e44c2ca43305eb0b"}, + {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf72af5e0fb40e9babf594308911436c8efde3cb5e75b6f206c34ad18be5c052"}, + {file = "propcache-0.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b2d0a12018b04f4cb820781ec0dffb5f7c7c1d2a5cd22bff7fb055a2cb19ebce"}, + {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e800776a79a5aabdb17dcc2346a7d66d0777e942e4cd251defeb084762ecd17d"}, + {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:4160d9283bd382fa6c0c2b5e017acc95bc183570cd70968b9202ad6d8fc48dce"}, + {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:30b43e74f1359353341a7adb783c8f1b1c676367b011709f466f42fda2045e95"}, + {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:58791550b27d5488b1bb52bc96328456095d96206a250d28d874fafe11b3dfaf"}, + {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:0f022d381747f0dfe27e99d928e31bc51a18b65bb9e481ae0af1380a6725dd1f"}, + {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:297878dc9d0a334358f9b608b56d02e72899f3b8499fc6044133f0d319e2ec30"}, + {file = "propcache-0.2.1-cp313-cp313-win32.whl", hash = "sha256:ddfab44e4489bd79bda09d84c430677fc7f0a4939a73d2bba3073036f487a0a6"}, + {file = "propcache-0.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:556fc6c10989f19a179e4321e5d678db8eb2924131e64652a51fe83e4c3db0e1"}, + {file = "propcache-0.2.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:6a9a8c34fb7bb609419a211e59da8887eeca40d300b5ea8e56af98f6fbbb1541"}, + {file = "propcache-0.2.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ae1aa1cd222c6d205853b3013c69cd04515f9d6ab6de4b0603e2e1c33221303e"}, + {file = "propcache-0.2.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:accb6150ce61c9c4b7738d45550806aa2b71c7668c6942f17b0ac182b6142fd4"}, + {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5eee736daafa7af6d0a2dc15cc75e05c64f37fc37bafef2e00d77c14171c2097"}, + {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7a31fc1e1bd362874863fdeed71aed92d348f5336fd84f2197ba40c59f061bd"}, + {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba4cfa1052819d16699e1d55d18c92b6e094d4517c41dd231a8b9f87b6fa681"}, + {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f089118d584e859c62b3da0892b88a83d611c2033ac410e929cb6754eec0ed16"}, + {file = "propcache-0.2.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:781e65134efaf88feb447e8c97a51772aa75e48b794352f94cb7ea717dedda0d"}, + {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:31f5af773530fd3c658b32b6bdc2d0838543de70eb9a2156c03e410f7b0d3aae"}, + {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:a7a078f5d37bee6690959c813977da5291b24286e7b962e62a94cec31aa5188b"}, + {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:cea7daf9fc7ae6687cf1e2c049752f19f146fdc37c2cc376e7d0032cf4f25347"}, + {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:8b3489ff1ed1e8315674d0775dc7d2195fb13ca17b3808721b54dbe9fd020faf"}, + {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9403db39be1393618dd80c746cb22ccda168efce239c73af13c3763ef56ffc04"}, + {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5d97151bc92d2b2578ff7ce779cdb9174337390a535953cbb9452fb65164c587"}, + {file = "propcache-0.2.1-cp39-cp39-win32.whl", hash = "sha256:9caac6b54914bdf41bcc91e7eb9147d331d29235a7c967c150ef5df6464fd1bb"}, + {file = "propcache-0.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:92fc4500fcb33899b05ba73276dfb684a20d31caa567b7cb5252d48f896a91b1"}, + {file = "propcache-0.2.1-py3-none-any.whl", hash = "sha256:52277518d6aae65536e9cea52d4e7fd2f7a66f4aa2d30ed3f2fcea620ace3c54"}, + {file = "propcache-0.2.1.tar.gz", hash = "sha256:3f77ce728b19cb537714499928fe800c3dda29e8d9428778fc7c186da4c09a64"}, +] + [[package]] name = "proto-plus" version = "1.24.0" @@ -3893,108 +3984,99 @@ files = [ [[package]] name = "yarl" -version = "1.11.1" +version = "1.18.3" description = "Yet another URL library" optional = true -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "yarl-1.11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:400cd42185f92de559d29eeb529e71d80dfbd2f45c36844914a4a34297ca6f00"}, - {file = "yarl-1.11.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8258c86f47e080a258993eed877d579c71da7bda26af86ce6c2d2d072c11320d"}, - {file = "yarl-1.11.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2164cd9725092761fed26f299e3f276bb4b537ca58e6ff6b252eae9631b5c96e"}, - {file = "yarl-1.11.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08ea567c16f140af8ddc7cb58e27e9138a1386e3e6e53982abaa6f2377b38cc"}, - {file = "yarl-1.11.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:768ecc550096b028754ea28bf90fde071c379c62c43afa574edc6f33ee5daaec"}, - {file = "yarl-1.11.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2909fa3a7d249ef64eeb2faa04b7957e34fefb6ec9966506312349ed8a7e77bf"}, - {file = "yarl-1.11.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01a8697ec24f17c349c4f655763c4db70eebc56a5f82995e5e26e837c6eb0e49"}, - {file = "yarl-1.11.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e286580b6511aac7c3268a78cdb861ec739d3e5a2a53b4809faef6b49778eaff"}, - {file = "yarl-1.11.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4179522dc0305c3fc9782549175c8e8849252fefeb077c92a73889ccbcd508ad"}, - {file = "yarl-1.11.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:27fcb271a41b746bd0e2a92182df507e1c204759f460ff784ca614e12dd85145"}, - {file = "yarl-1.11.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f61db3b7e870914dbd9434b560075e0366771eecbe6d2b5561f5bc7485f39efd"}, - {file = "yarl-1.11.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:c92261eb2ad367629dc437536463dc934030c9e7caca861cc51990fe6c565f26"}, - {file = "yarl-1.11.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d95b52fbef190ca87d8c42f49e314eace4fc52070f3dfa5f87a6594b0c1c6e46"}, - {file = "yarl-1.11.1-cp310-cp310-win32.whl", hash = "sha256:489fa8bde4f1244ad6c5f6d11bb33e09cf0d1d0367edb197619c3e3fc06f3d91"}, - {file = "yarl-1.11.1-cp310-cp310-win_amd64.whl", hash = "sha256:476e20c433b356e16e9a141449f25161e6b69984fb4cdbd7cd4bd54c17844998"}, - {file = "yarl-1.11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:946eedc12895873891aaceb39bceb484b4977f70373e0122da483f6c38faaa68"}, - {file = "yarl-1.11.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:21a7c12321436b066c11ec19c7e3cb9aec18884fe0d5b25d03d756a9e654edfe"}, - {file = "yarl-1.11.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c35f493b867912f6fda721a59cc7c4766d382040bdf1ddaeeaa7fa4d072f4675"}, - {file = "yarl-1.11.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25861303e0be76b60fddc1250ec5986c42f0a5c0c50ff57cc30b1be199c00e63"}, - {file = "yarl-1.11.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e4b53f73077e839b3f89c992223f15b1d2ab314bdbdf502afdc7bb18e95eae27"}, - {file = "yarl-1.11.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:327c724b01b8641a1bf1ab3b232fb638706e50f76c0b5bf16051ab65c868fac5"}, - {file = "yarl-1.11.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4307d9a3417eea87715c9736d050c83e8c1904e9b7aada6ce61b46361b733d92"}, - {file = "yarl-1.11.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48a28bed68ab8fb7e380775f0029a079f08a17799cb3387a65d14ace16c12e2b"}, - {file = "yarl-1.11.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:067b961853c8e62725ff2893226fef3d0da060656a9827f3f520fb1d19b2b68a"}, - {file = "yarl-1.11.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8215f6f21394d1f46e222abeb06316e77ef328d628f593502d8fc2a9117bde83"}, - {file = "yarl-1.11.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:498442e3af2a860a663baa14fbf23fb04b0dd758039c0e7c8f91cb9279799bff"}, - {file = "yarl-1.11.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:69721b8effdb588cb055cc22f7c5105ca6fdaa5aeb3ea09021d517882c4a904c"}, - {file = "yarl-1.11.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1e969fa4c1e0b1a391f3fcbcb9ec31e84440253325b534519be0d28f4b6b533e"}, - {file = "yarl-1.11.1-cp311-cp311-win32.whl", hash = "sha256:7d51324a04fc4b0e097ff8a153e9276c2593106a811704025bbc1d6916f45ca6"}, - {file = "yarl-1.11.1-cp311-cp311-win_amd64.whl", hash = "sha256:15061ce6584ece023457fb8b7a7a69ec40bf7114d781a8c4f5dcd68e28b5c53b"}, - {file = "yarl-1.11.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:a4264515f9117be204935cd230fb2a052dd3792789cc94c101c535d349b3dab0"}, - {file = "yarl-1.11.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f41fa79114a1d2eddb5eea7b912d6160508f57440bd302ce96eaa384914cd265"}, - {file = "yarl-1.11.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:02da8759b47d964f9173c8675710720b468aa1c1693be0c9c64abb9d8d9a4867"}, - {file = "yarl-1.11.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9361628f28f48dcf8b2f528420d4d68102f593f9c2e592bfc842f5fb337e44fd"}, - {file = "yarl-1.11.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b91044952da03b6f95fdba398d7993dd983b64d3c31c358a4c89e3c19b6f7aef"}, - {file = "yarl-1.11.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:74db2ef03b442276d25951749a803ddb6e270d02dda1d1c556f6ae595a0d76a8"}, - {file = "yarl-1.11.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e975a2211952a8a083d1b9d9ba26472981ae338e720b419eb50535de3c02870"}, - {file = "yarl-1.11.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8aef97ba1dd2138112890ef848e17d8526fe80b21f743b4ee65947ea184f07a2"}, - {file = "yarl-1.11.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a7915ea49b0c113641dc4d9338efa9bd66b6a9a485ffe75b9907e8573ca94b84"}, - {file = "yarl-1.11.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:504cf0d4c5e4579a51261d6091267f9fd997ef58558c4ffa7a3e1460bd2336fa"}, - {file = "yarl-1.11.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:3de5292f9f0ee285e6bd168b2a77b2a00d74cbcfa420ed078456d3023d2f6dff"}, - {file = "yarl-1.11.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a34e1e30f1774fa35d37202bbeae62423e9a79d78d0874e5556a593479fdf239"}, - {file = "yarl-1.11.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:66b63c504d2ca43bf7221a1f72fbe981ff56ecb39004c70a94485d13e37ebf45"}, - {file = "yarl-1.11.1-cp312-cp312-win32.whl", hash = "sha256:a28b70c9e2213de425d9cba5ab2e7f7a1c8ca23a99c4b5159bf77b9c31251447"}, - {file = "yarl-1.11.1-cp312-cp312-win_amd64.whl", hash = "sha256:17b5a386d0d36fb828e2fb3ef08c8829c1ebf977eef88e5367d1c8c94b454639"}, - {file = "yarl-1.11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:1fa2e7a406fbd45b61b4433e3aa254a2c3e14c4b3186f6e952d08a730807fa0c"}, - {file = "yarl-1.11.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:750f656832d7d3cb0c76be137ee79405cc17e792f31e0a01eee390e383b2936e"}, - {file = "yarl-1.11.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0b8486f322d8f6a38539136a22c55f94d269addb24db5cb6f61adc61eabc9d93"}, - {file = "yarl-1.11.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3fce4da3703ee6048ad4138fe74619c50874afe98b1ad87b2698ef95bf92c96d"}, - {file = "yarl-1.11.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ed653638ef669e0efc6fe2acb792275cb419bf9cb5c5049399f3556995f23c7"}, - {file = "yarl-1.11.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18ac56c9dd70941ecad42b5a906820824ca72ff84ad6fa18db33c2537ae2e089"}, - {file = "yarl-1.11.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:688654f8507464745ab563b041d1fb7dab5d9912ca6b06e61d1c4708366832f5"}, - {file = "yarl-1.11.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4973eac1e2ff63cf187073cd4e1f1148dcd119314ab79b88e1b3fad74a18c9d5"}, - {file = "yarl-1.11.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:964a428132227edff96d6f3cf261573cb0f1a60c9a764ce28cda9525f18f7786"}, - {file = "yarl-1.11.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:6d23754b9939cbab02c63434776df1170e43b09c6a517585c7ce2b3d449b7318"}, - {file = "yarl-1.11.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c2dc4250fe94d8cd864d66018f8344d4af50e3758e9d725e94fecfa27588ff82"}, - {file = "yarl-1.11.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09696438cb43ea6f9492ef237761b043f9179f455f405279e609f2bc9100212a"}, - {file = "yarl-1.11.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:999bfee0a5b7385a0af5ffb606393509cfde70ecca4f01c36985be6d33e336da"}, - {file = "yarl-1.11.1-cp313-cp313-win32.whl", hash = "sha256:ce928c9c6409c79e10f39604a7e214b3cb69552952fbda8d836c052832e6a979"}, - {file = "yarl-1.11.1-cp313-cp313-win_amd64.whl", hash = "sha256:501c503eed2bb306638ccb60c174f856cc3246c861829ff40eaa80e2f0330367"}, - {file = "yarl-1.11.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:dae7bd0daeb33aa3e79e72877d3d51052e8b19c9025ecf0374f542ea8ec120e4"}, - {file = "yarl-1.11.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3ff6b1617aa39279fe18a76c8d165469c48b159931d9b48239065767ee455b2b"}, - {file = "yarl-1.11.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3257978c870728a52dcce8c2902bf01f6c53b65094b457bf87b2644ee6238ddc"}, - {file = "yarl-1.11.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f351fa31234699d6084ff98283cb1e852270fe9e250a3b3bf7804eb493bd937"}, - {file = "yarl-1.11.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8aef1b64da41d18026632d99a06b3fefe1d08e85dd81d849fa7c96301ed22f1b"}, - {file = "yarl-1.11.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7175a87ab8f7fbde37160a15e58e138ba3b2b0e05492d7351314a250d61b1591"}, - {file = "yarl-1.11.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba444bdd4caa2a94456ef67a2f383710928820dd0117aae6650a4d17029fa25e"}, - {file = "yarl-1.11.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0ea9682124fc062e3d931c6911934a678cb28453f957ddccf51f568c2f2b5e05"}, - {file = "yarl-1.11.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:8418c053aeb236b20b0ab8fa6bacfc2feaaf7d4683dd96528610989c99723d5f"}, - {file = "yarl-1.11.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:61a5f2c14d0a1adfdd82258f756b23a550c13ba4c86c84106be4c111a3a4e413"}, - {file = "yarl-1.11.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:f3a6d90cab0bdf07df8f176eae3a07127daafcf7457b997b2bf46776da2c7eb7"}, - {file = "yarl-1.11.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:077da604852be488c9a05a524068cdae1e972b7dc02438161c32420fb4ec5e14"}, - {file = "yarl-1.11.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:15439f3c5c72686b6c3ff235279630d08936ace67d0fe5c8d5bbc3ef06f5a420"}, - {file = "yarl-1.11.1-cp38-cp38-win32.whl", hash = "sha256:238a21849dd7554cb4d25a14ffbfa0ef380bb7ba201f45b144a14454a72ffa5a"}, - {file = "yarl-1.11.1-cp38-cp38-win_amd64.whl", hash = "sha256:67459cf8cf31da0e2cbdb4b040507e535d25cfbb1604ca76396a3a66b8ba37a6"}, - {file = "yarl-1.11.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:884eab2ce97cbaf89f264372eae58388862c33c4f551c15680dd80f53c89a269"}, - {file = "yarl-1.11.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8a336eaa7ee7e87cdece3cedb395c9657d227bfceb6781295cf56abcd3386a26"}, - {file = "yarl-1.11.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:87f020d010ba80a247c4abc335fc13421037800ca20b42af5ae40e5fd75e7909"}, - {file = "yarl-1.11.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:637c7ddb585a62d4469f843dac221f23eec3cbad31693b23abbc2c366ad41ff4"}, - {file = "yarl-1.11.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:48dfd117ab93f0129084577a07287376cc69c08138694396f305636e229caa1a"}, - {file = "yarl-1.11.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75e0ae31fb5ccab6eda09ba1494e87eb226dcbd2372dae96b87800e1dcc98804"}, - {file = "yarl-1.11.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f46f81501160c28d0c0b7333b4f7be8983dbbc161983b6fb814024d1b4952f79"}, - {file = "yarl-1.11.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:04293941646647b3bfb1719d1d11ff1028e9c30199509a844da3c0f5919dc520"}, - {file = "yarl-1.11.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:250e888fa62d73e721f3041e3a9abf427788a1934b426b45e1b92f62c1f68366"}, - {file = "yarl-1.11.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e8f63904df26d1a66aabc141bfd258bf738b9bc7bc6bdef22713b4f5ef789a4c"}, - {file = "yarl-1.11.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:aac44097d838dda26526cffb63bdd8737a2dbdf5f2c68efb72ad83aec6673c7e"}, - {file = "yarl-1.11.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:267b24f891e74eccbdff42241c5fb4f974de2d6271dcc7d7e0c9ae1079a560d9"}, - {file = "yarl-1.11.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6907daa4b9d7a688063ed098c472f96e8181733c525e03e866fb5db480a424df"}, - {file = "yarl-1.11.1-cp39-cp39-win32.whl", hash = "sha256:14438dfc5015661f75f85bc5adad0743678eefee266ff0c9a8e32969d5d69f74"}, - {file = "yarl-1.11.1-cp39-cp39-win_amd64.whl", hash = "sha256:94d0caaa912bfcdc702a4204cd5e2bb01eb917fc4f5ea2315aa23962549561b0"}, - {file = "yarl-1.11.1-py3-none-any.whl", hash = "sha256:72bf26f66456baa0584eff63e44545c9f0eaed9b73cb6601b647c91f14c11f38"}, - {file = "yarl-1.11.1.tar.gz", hash = "sha256:1bb2d9e212fb7449b8fb73bc461b51eaa17cc8430b4a87d87be7b25052d92f53"}, + {file = "yarl-1.18.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7df647e8edd71f000a5208fe6ff8c382a1de8edfbccdbbfe649d263de07d8c34"}, + {file = "yarl-1.18.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c69697d3adff5aa4f874b19c0e4ed65180ceed6318ec856ebc423aa5850d84f7"}, + {file = "yarl-1.18.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:602d98f2c2d929f8e697ed274fbadc09902c4025c5a9963bf4e9edfc3ab6f7ed"}, + {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c654d5207c78e0bd6d749f6dae1dcbbfde3403ad3a4b11f3c5544d9906969dde"}, + {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5094d9206c64181d0f6e76ebd8fb2f8fe274950a63890ee9e0ebfd58bf9d787b"}, + {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35098b24e0327fc4ebdc8ffe336cee0a87a700c24ffed13161af80124b7dc8e5"}, + {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3236da9272872443f81fedc389bace88408f64f89f75d1bdb2256069a8730ccc"}, + {file = "yarl-1.18.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2c08cc9b16f4f4bc522771d96734c7901e7ebef70c6c5c35dd0f10845270bcd"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:80316a8bd5109320d38eef8833ccf5f89608c9107d02d2a7f985f98ed6876990"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:c1e1cc06da1491e6734f0ea1e6294ce00792193c463350626571c287c9a704db"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fea09ca13323376a2fdfb353a5fa2e59f90cd18d7ca4eaa1fd31f0a8b4f91e62"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e3b9fd71836999aad54084906f8663dffcd2a7fb5cdafd6c37713b2e72be1760"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:757e81cae69244257d125ff31663249b3013b5dc0a8520d73694aed497fb195b"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b1771de9944d875f1b98a745bc547e684b863abf8f8287da8466cf470ef52690"}, + {file = "yarl-1.18.3-cp310-cp310-win32.whl", hash = "sha256:8874027a53e3aea659a6d62751800cf6e63314c160fd607489ba5c2edd753cf6"}, + {file = "yarl-1.18.3-cp310-cp310-win_amd64.whl", hash = "sha256:93b2e109287f93db79210f86deb6b9bbb81ac32fc97236b16f7433db7fc437d8"}, + {file = "yarl-1.18.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8503ad47387b8ebd39cbbbdf0bf113e17330ffd339ba1144074da24c545f0069"}, + {file = "yarl-1.18.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:02ddb6756f8f4517a2d5e99d8b2f272488e18dd0bfbc802f31c16c6c20f22193"}, + {file = "yarl-1.18.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:67a283dd2882ac98cc6318384f565bffc751ab564605959df4752d42483ad889"}, + {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d980e0325b6eddc81331d3f4551e2a333999fb176fd153e075c6d1c2530aa8a8"}, + {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b643562c12680b01e17239be267bc306bbc6aac1f34f6444d1bded0c5ce438ca"}, + {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c017a3b6df3a1bd45b9fa49a0f54005e53fbcad16633870104b66fa1a30a29d8"}, + {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75674776d96d7b851b6498f17824ba17849d790a44d282929c42dbb77d4f17ae"}, + {file = "yarl-1.18.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ccaa3a4b521b780a7e771cc336a2dba389a0861592bbce09a476190bb0c8b4b3"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2d06d3005e668744e11ed80812e61efd77d70bb7f03e33c1598c301eea20efbb"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:9d41beda9dc97ca9ab0b9888cb71f7539124bc05df02c0cff6e5acc5a19dcc6e"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ba23302c0c61a9999784e73809427c9dbedd79f66a13d84ad1b1943802eaaf59"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:6748dbf9bfa5ba1afcc7556b71cda0d7ce5f24768043a02a58846e4a443d808d"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0b0cad37311123211dc91eadcb322ef4d4a66008d3e1bdc404808992260e1a0e"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0fb2171a4486bb075316ee754c6d8382ea6eb8b399d4ec62fde2b591f879778a"}, + {file = "yarl-1.18.3-cp311-cp311-win32.whl", hash = "sha256:61b1a825a13bef4a5f10b1885245377d3cd0bf87cba068e1d9a88c2ae36880e1"}, + {file = "yarl-1.18.3-cp311-cp311-win_amd64.whl", hash = "sha256:b9d60031cf568c627d028239693fd718025719c02c9f55df0a53e587aab951b5"}, + {file = "yarl-1.18.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1dd4bdd05407ced96fed3d7f25dbbf88d2ffb045a0db60dbc247f5b3c5c25d50"}, + {file = "yarl-1.18.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7c33dd1931a95e5d9a772d0ac5e44cac8957eaf58e3c8da8c1414de7dd27c576"}, + {file = "yarl-1.18.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:25b411eddcfd56a2f0cd6a384e9f4f7aa3efee14b188de13048c25b5e91f1640"}, + {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:436c4fc0a4d66b2badc6c5fc5ef4e47bb10e4fd9bf0c79524ac719a01f3607c2"}, + {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e35ef8683211db69ffe129a25d5634319a677570ab6b2eba4afa860f54eeaf75"}, + {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:84b2deecba4a3f1a398df819151eb72d29bfeb3b69abb145a00ddc8d30094512"}, + {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00e5a1fea0fd4f5bfa7440a47eff01d9822a65b4488f7cff83155a0f31a2ecba"}, + {file = "yarl-1.18.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d0e883008013c0e4aef84dcfe2a0b172c4d23c2669412cf5b3371003941f72bb"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5a3f356548e34a70b0172d8890006c37be92995f62d95a07b4a42e90fba54272"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ccd17349166b1bee6e529b4add61727d3f55edb7babbe4069b5764c9587a8cc6"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b958ddd075ddba5b09bb0be8a6d9906d2ce933aee81100db289badbeb966f54e"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c7d79f7d9aabd6011004e33b22bc13056a3e3fb54794d138af57f5ee9d9032cb"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:4891ed92157e5430874dad17b15eb1fda57627710756c27422200c52d8a4e393"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ce1af883b94304f493698b00d0f006d56aea98aeb49d75ec7d98cd4a777e9285"}, + {file = "yarl-1.18.3-cp312-cp312-win32.whl", hash = "sha256:f91c4803173928a25e1a55b943c81f55b8872f0018be83e3ad4938adffb77dd2"}, + {file = "yarl-1.18.3-cp312-cp312-win_amd64.whl", hash = "sha256:7e2ee16578af3b52ac2f334c3b1f92262f47e02cc6193c598502bd46f5cd1477"}, + {file = "yarl-1.18.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:90adb47ad432332d4f0bc28f83a5963f426ce9a1a8809f5e584e704b82685dcb"}, + {file = "yarl-1.18.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:913829534200eb0f789d45349e55203a091f45c37a2674678744ae52fae23efa"}, + {file = "yarl-1.18.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ef9f7768395923c3039055c14334ba4d926f3baf7b776c923c93d80195624782"}, + {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88a19f62ff30117e706ebc9090b8ecc79aeb77d0b1f5ec10d2d27a12bc9f66d0"}, + {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e17c9361d46a4d5addf777c6dd5eab0715a7684c2f11b88c67ac37edfba6c482"}, + {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a74a13a4c857a84a845505fd2d68e54826a2cd01935a96efb1e9d86c728e186"}, + {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41f7ce59d6ee7741af71d82020346af364949314ed3d87553763a2df1829cc58"}, + {file = "yarl-1.18.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f52a265001d830bc425f82ca9eabda94a64a4d753b07d623a9f2863fde532b53"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:82123d0c954dc58db301f5021a01854a85bf1f3bb7d12ae0c01afc414a882ca2"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:2ec9bbba33b2d00999af4631a3397d1fd78290c48e2a3e52d8dd72db3a067ac8"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:fbd6748e8ab9b41171bb95c6142faf068f5ef1511935a0aa07025438dd9a9bc1"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:877d209b6aebeb5b16c42cbb377f5f94d9e556626b1bfff66d7b0d115be88d0a"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b464c4ab4bfcb41e3bfd3f1c26600d038376c2de3297760dfe064d2cb7ea8e10"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8d39d351e7faf01483cc7ff7c0213c412e38e5a340238826be7e0e4da450fdc8"}, + {file = "yarl-1.18.3-cp313-cp313-win32.whl", hash = "sha256:61ee62ead9b68b9123ec24bc866cbef297dd266175d53296e2db5e7f797f902d"}, + {file = "yarl-1.18.3-cp313-cp313-win_amd64.whl", hash = "sha256:578e281c393af575879990861823ef19d66e2b1d0098414855dd367e234f5b3c"}, + {file = "yarl-1.18.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:61e5e68cb65ac8f547f6b5ef933f510134a6bf31bb178be428994b0cb46c2a04"}, + {file = "yarl-1.18.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fe57328fbc1bfd0bd0514470ac692630f3901c0ee39052ae47acd1d90a436719"}, + {file = "yarl-1.18.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a440a2a624683108a1b454705ecd7afc1c3438a08e890a1513d468671d90a04e"}, + {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09c7907c8548bcd6ab860e5f513e727c53b4a714f459b084f6580b49fa1b9cee"}, + {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b4f6450109834af88cb4cc5ecddfc5380ebb9c228695afc11915a0bf82116789"}, + {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9ca04806f3be0ac6d558fffc2fdf8fcef767e0489d2684a21912cc4ed0cd1b8"}, + {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77a6e85b90a7641d2e07184df5557132a337f136250caafc9ccaa4a2a998ca2c"}, + {file = "yarl-1.18.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6333c5a377c8e2f5fae35e7b8f145c617b02c939d04110c76f29ee3676b5f9a5"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0b3c92fa08759dbf12b3a59579a4096ba9af8dd344d9a813fc7f5070d86bbab1"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:4ac515b860c36becb81bb84b667466885096b5fc85596948548b667da3bf9f24"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:045b8482ce9483ada4f3f23b3774f4e1bf4f23a2d5c912ed5170f68efb053318"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:a4bb030cf46a434ec0225bddbebd4b89e6471814ca851abb8696170adb163985"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:54d6921f07555713b9300bee9c50fb46e57e2e639027089b1d795ecd9f7fa910"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1d407181cfa6e70077df3377938c08012d18893f9f20e92f7d2f314a437c30b1"}, + {file = "yarl-1.18.3-cp39-cp39-win32.whl", hash = "sha256:ac36703a585e0929b032fbaab0707b75dc12703766d0b53486eabd5139ebadd5"}, + {file = "yarl-1.18.3-cp39-cp39-win_amd64.whl", hash = "sha256:ba87babd629f8af77f557b61e49e7c7cac36f22f871156b91e10a6e9d4f829e9"}, + {file = "yarl-1.18.3-py3-none-any.whl", hash = "sha256:b57f4f58099328dfb26c6a771d09fb20dbbae81d20cfb66141251ea063bd101b"}, + {file = "yarl-1.18.3.tar.gz", hash = "sha256:ac1801c45cbf77b6c99242eeff4fffb5e4e73a800b5c4ad4fc0be5def634d2e1"}, ] [package.dependencies] idna = ">=2.0" multidict = ">=4.0" +propcache = ">=0.2.0" [extras] logging = ["asgi-logger"] diff --git a/python/kserve/pyproject.toml b/python/kserve/pyproject.toml index d0c00700867..40c331ce519 100644 --- a/python/kserve/pyproject.toml +++ b/python/kserve/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "kserve" -version = "0.14.0" +version = "0.15.0rc0" description = "KServe Python SDK" authors = [ "The KServe Authors ", diff --git a/python/kserve/test/conftest.py b/python/kserve/test/conftest.py new file mode 100644 index 00000000000..8e1975cb840 --- /dev/null +++ b/python/kserve/test/conftest.py @@ -0,0 +1,32 @@ +# Copyright 2024 The KServe Authors. +# +# Licensed 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. + +import pytest +from kserve import ModelServer, ModelRepository +from kserve.protocol.rest.server import RESTServer +from kserve.model_server import app as kserve_app + + +@pytest.fixture(scope="session") +def server(): + server = ModelServer(registered_models=ModelRepository()) + rest_server = RESTServer( + kserve_app, + server.dataplane, + server.model_repository_extension, + http_port=8080, + ) + rest_server.create_application() + yield server + kserve_app.routes.clear() diff --git a/python/kserve/test/fixtures/openai/embedding.json b/python/kserve/test/fixtures/openai/embedding.json new file mode 100644 index 00000000000..e22b9217d64 --- /dev/null +++ b/python/kserve/test/fixtures/openai/embedding.json @@ -0,0 +1,400 @@ +{ + "data": [ + { + "index": 0, + "embedding": [ + 0.09812460094690323, + 0.06781269609928131, + 0.06252317875623703, + 0.09508483111858368, + 0.03664761036634445, + -0.003984640818089247, + 0.007477519102394581, + -0.013231460005044937, + 0.06288369745016098, + 0.02249549701809883, + 0.072695791721344, + -0.03127428889274597, + 0.04635513201355934, + -0.012554519809782505, + 0.04781476408243179, + -0.004910346586257219, + 0.04941992461681366, + -0.06410930305719376, + -0.09696582704782486, + 0.03288879990577698, + 0.05410446599125862, + 0.03532857075333595, + 0.0330505445599556, + 0.014699422754347324, + -0.03343060985207558, + -0.025615829974412918, + -0.050792112946510315, + 0.07325457781553268, + 0.11027400195598602, + -0.029661813750863075, + -0.06755708158016205, + -0.03057151474058628, + 0.039560236036777496, + 0.04547601938247681, + 0.015996191650629044, + 0.038550399243831635, + -0.010954048484563828, + 0.08483563363552094, + -0.04428705945611, + -0.006796404253691435, + 0.00942566804587841, + 0.000050668884796323255, + 0.0013035491574555635, + -0.011969789862632751, + 0.013645154424011707, + -0.08417423069477081, + -0.00016514321032445878, + 0.005483775865286589, + 0.025615083053708076, + -0.031545281410217285, + -0.10734470188617706, + -0.04578779265284538, + -0.09117487818002701, + -0.0025105553213506937, + 0.01799839362502098, + 0.049401599913835526, + 0.006184802390635014, + 0.059796303510665894, + 0.027002600952982903, + -0.016122261062264442, + -0.018149685114622116, + -0.02363482303917408, + -0.09489713609218597, + 0.06621629744768143, + 0.1492275595664978, + 0.024338779971003532, + 0.0012102446053177118, + 0.006072014570236206, + -0.09917040914297104, + 0.08505810797214508, + 0.0308261476457119, + 0.02918657846748829, + -0.02395595796406269, + -0.0105593865737319, + -0.07215200364589691, + -0.03525640070438385, + 0.033346593379974365, + -0.04392950236797333, + 0.11975520849227905, + 0.08864743262529373, + -0.09591902047395706, + -0.059080179780721664, + -0.008292674086987972, + 0.03816923126578331, + 0.04979965463280678, + -0.0326821506023407, + 0.019901003688573837, + -0.10837855935096741, + 0.014679051004350185, + 0.01811782829463482, + -0.06131783127784729, + -0.08975033462047577, + 0.04922163859009743, + -0.02087101712822914, + 0.004055131692439318, + -0.03543014079332352, + -0.05657777190208435, + -0.048313725739717484, + -0.024660276249051094, + 0.07359182834625244, + -0.020923534408211708, + 0.04069769009947777, + 0.032326482236385345, + 0.07668346911668777, + -0.014747701585292816, + -0.1030452772974968, + -0.04274492338299751, + -0.0662931501865387, + 0.015474040061235428, + -0.026800300925970078, + -0.09175194799900055, + -0.035600464791059494, + -0.018932482227683067, + -0.004029575269669294, + -0.020193425938487053, + -0.05853481963276863, + -0.10180167108774185, + -0.004487900994718075, + -0.06976304948329926, + 0.06024095416069031, + 0.029810858890414238, + 0.06380830705165863, + -0.06730612367391586, + 0.0033509465865790844, + -0.060039862990379333, + -0.09313665330410004, + 0.045675043016672134, + -6.719925045458755E-33, + 0.007433182094246149, + -0.06510976701974869, + -0.021259166300296783, + 0.035196054726839066, + 0.046336084604263306, + -0.07030496001243591, + -0.06128956750035286, + 0.017366604879498482, + -0.0074797123670578, + -0.01846875622868538, + 0.001607456593774259, + -0.02580185979604721, + 0.030047021806240082, + 0.00875167828053236, + 0.05092949420213699, + 0.07171623408794403, + -0.07271793484687805, + 0.1326150894165039, + -0.00803415384143591, + 0.03693223372101784, + -0.023691322654485703, + 0.024269403889775276, + -0.026396209374070168, + -0.018338508903980255, + -0.014753326773643494, + -0.07431734353303909, + 0.016344867646694183, + 0.01803700067102909, + -0.003123299218714237, + 0.04128265753388405, + 0.050598617643117905, + 0.04449482262134552, + -0.04755300655961037, + -0.002626477275043726, + 0.054667823016643524, + 0.046977970749139786, + 0.06379827111959457, + -0.02183675765991211, + 0.0011920140823349357, + 0.006154955364763737, + -0.010474730283021927, + -0.05154884234070778, + 0.021718978881835938, + -0.03787209838628769, + 0.0879114493727684, + 0.02365758642554283, + 0.019885694608092308, + 0.021743159741163254, + -0.06192107871174812, + 0.020616168156266212, + -0.010686979629099369, + 0.05776217207312584, + 0.059402864426374435, + -0.0288755651563406, + 0.06947711855173111, + 0.001013647997751832, + 0.042007993906736374, + 0.034752000123262405, + -0.0016647797310724854, + -0.010128127411007881, + -0.054719988256692886, + 0.04056468978524208, + -0.056016407907009125, + 0.057263199239969254, + -0.025760957971215248, + 0.029396990314126015, + 0.010298472829163074, + -0.04915247857570648, + 0.015427723526954651, + 0.02869337983429432, + 0.011681458912789822, + -0.006968752946704626, + -0.08346767723560333, + 0.10836982727050781, + -0.04198267310857773, + -0.04266303777694702, + -0.021897369995713234, + -0.021625958383083344, + -0.007852158509194851, + 0.03822033107280731, + -0.01800902746617794, + -0.14943544566631317, + 0.023226428776979446, + 0.015028486028313637, + -0.03989404812455177, + 0.0037516923621296883, + 0.043006666004657745, + -0.08475463092327118, + -0.03029675781726837, + -0.02013072930276394, + -0.025030167773365974, + 0.003526018001139164, + 0.0582348108291626, + 0.00549469655379653, + 0.006489734165370464, + 3.174682205178722E-33, + -0.04243830591440201, + 0.02992221526801586, + -0.08836192637681961, + 0.06575305014848709, + 0.10463074594736099, + -0.008896679617464542, + 0.042894456535577774, + -0.09550556540489197, + 0.03168181702494621, + 0.07352744787931442, + -0.13084876537322998, + 0.025529906153678894, + 0.016062166541814804, + -0.0043756430968642235, + 0.029202574864029884, + -0.008134165778756142, + 0.06525744497776031, + 0.0006216196925379336, + -0.00783581379801035, + 0.04156062752008438, + -0.04372049495577812, + 0.09504026919603348, + -0.025842653587460518, + 0.08701659739017487, + -0.04147154465317726, + 0.03544049710035324, + -0.014387118630111217, + -0.026075217872858047, + -0.09590037912130356, + -0.008881507441401482, + 0.007069987244904041, + -0.09052383154630661, + -0.0797419473528862, + -0.014227607287466526, + -0.05348994582891464, + 0.05122567340731621, + 0.024540821090340614, + -0.03697463870048523, + -0.07341103255748749, + -0.006874367594718933, + 0.0068125249817967415, + -0.0329182967543602, + 0.015790680423378944, + 0.1059456467628479, + 0.022715147584676743, + -0.05641724914312363, + -0.03206418454647064, + -0.10509151220321655, + -0.032904643565416336, + 0.0513918362557888, + -0.10034642368555069, + 0.04499524086713791, + -0.03296136111021042, + -0.019640814512968063, + -0.10540889203548431, + 0.011973707005381584, + -0.011170891113579273, + -0.06957735121250153, + -0.009789666160941124, + 0.016010869294404984, + -0.05117956921458244, + 0.008791363798081875, + -0.006576142739504576, + 0.06629248708486557, + 0.10956112295389175, + -0.05475931614637375, + -0.05709327384829521, + 0.050297729671001434, + 0.01262251753360033, + 0.02845410816371441, + 0.1343768984079361, + 0.011013655923306942, + -0.1519516557455063, + -0.049476105719804764, + -0.06537330895662308, + -0.06709235161542892, + -0.06969746947288513, + -0.03129567205905914, + -0.061766088008880615, + -0.0742039680480957, + 0.02897379919886589, + -0.008312447927892208, + 0.04321928694844246, + -0.017557401210069656, + -0.005178697872906923, + -0.05432431399822235, + -0.04127553850412369, + -0.08270692080259323, + -0.015986543148756027, + 0.008618013933300972, + 0.0006255805492401123, + -0.06523493677377701, + 0.07926347851753235, + 0.026802241802215576, + -0.025414051488041878, + -1.965175222551352E-8, + -0.022532736882567406, + -0.015100589022040367, + 0.08194082230329514, + -0.03959040343761444, + 0.04711084067821503, + 0.015206287615001202, + 0.06537682563066483, + 0.018435340374708176, + -0.007971749641001225, + -0.03773093223571777, + 0.023044027388095856, + 0.040450308471918106, + -0.05697207897901535, + -0.003070066450163722, + 0.05366634577512741, + 0.06094937399029732, + 0.0383155420422554, + -0.03807567432522774, + -0.02675454691052437, + 0.0674038827419281, + -0.05417702719569206, + 0.04589824378490448, + -0.012908662669360638, + 0.015068446286022663, + 0.03300931304693222, + 0.004980806726962328, + 0.0434085987508297, + 0.06518961489200592, + 0.016215743497014046, + 0.03613708168268204, + 0.05567425861954689, + 0.11904114484786987, + 0.024056648835539818, + 0.015141750685870647, + 0.060563839972019196, + 0.03681957721710205, + 0.041256338357925415, + -0.03155893832445145, + 0.05625439062714577, + -0.025755975395441055, + 0.013329358771443367, + 0.04844075068831444, + -0.021774696186184883, + 0.11008109897375107, + 0.03858953341841698, + 0.03805913031101227, + -0.004871034529060125, + -0.04328330606222153, + -0.02537413313984871, + 0.016013741493225098, + 0.018330948427319527, + 0.05638059601187706, + 0.03461501747369766, + -0.020659904927015305, + 0.02127760276198387, + 0.01893790438771248, + 0.03383444622159004, + -0.0069625237956643105, + -0.024325989186763763, + -0.019780240952968597, + -0.01989535242319107, + 0.034644052386283875, + 0.049627240747213364, + -0.029213014990091324 + ], + "object": "embedding" + } + ], + "model": "hf-text-embedding-openai", + "object": "list", + "usage": { + "prompt_tokens": 8, + "total_tokens": 8 + } +} diff --git a/python/kserve/test/fixtures/openai/embedding_create_params.json b/python/kserve/test/fixtures/openai/embedding_create_params.json new file mode 100644 index 00000000000..4eb7fb177bb --- /dev/null +++ b/python/kserve/test/fixtures/openai/embedding_create_params.json @@ -0,0 +1,5 @@ +{ + "input": "This is an example sentence.", + "model": "hf-text-embedding-openai", + "encoding_format": "float" +} diff --git a/python/kserve/test/test_dataplane.py b/python/kserve/test/test_dataplane.py index 2f7e5409958..9243f725c88 100644 --- a/python/kserve/test/test_dataplane.py +++ b/python/kserve/test/test_dataplane.py @@ -36,7 +36,7 @@ from kserve.errors import InvalidInput, ModelNotFound from kserve.model import PredictorProtocol, PredictorConfig from kserve.protocol.dataplane import DataPlane -from kserve.protocol.rest.openai import CompletionRequest, OpenAIModel +from kserve.protocol.rest.openai import CompletionRequest, OpenAICompletionModel from kserve.model_repository import ModelRepository from kserve.ray import RayModel from test.test_server import ( @@ -423,7 +423,7 @@ async def test_infer_ce_avro_binary(self, dataplane_with_ce_model): class TestDataPlaneOpenAI: MODEL_NAME = "TestModel" - class DummyOpenAIModel(OpenAIModel): + class DummyOpenAIModel(OpenAICompletionModel): async def create_completion( self, params: CompletionRequest ) -> Union[Completion, AsyncIterator[Completion]]: @@ -434,7 +434,7 @@ async def create_chat_completion( ) -> Union[ChatCompletion, AsyncIterator[ChatCompletionChunk]]: pass - async def test_infer_on_openai_model_raises(self): + async def test_infer_on_openai_completion_model_raises(self): openai_model = self.DummyOpenAIModel(self.MODEL_NAME) repo = ModelRepository() repo.update(openai_model) @@ -448,16 +448,13 @@ async def test_infer_on_openai_model_raises(self): assert ( exc.value.reason - == "Model TestModel is of type OpenAIModel. It does not support the infer method." + == "Model TestModel is of type OpenAICompletionModel. It does not support the infer method." ) @pytest.mark.asyncio class TestDataplaneTransformer: - @pytest.mark.skip( - "pytest_httpx requires python >= 3.9. It can be enabled once we remove the support for python 3.8" - ) async def test_dataplane_rest_with_ssl_enabled(self, httpx_mock): # scenario: getting a 2xx response from predictor with ssl enabled predictor_host = "ready.host" @@ -499,9 +496,6 @@ async def test_dataplane_grpc_with_ssl_enabled(self, mock_grpc_client): url=predictor_host, timeout=5, retries=2, use_ssl=True ) - @pytest.mark.skip( - "pytest_httpx requires python >= 3.9. It can be enabled once we remove the support for python 3.8" - ) async def test_server_readiness_v1(self, httpx_mock): # scenario: getting a 2xx response from predictor predictor_host = "ready.host" @@ -538,9 +532,6 @@ async def test_server_readiness_v1(self, httpx_mock): with pytest.raises(httpx.HTTPStatusError): await dataplane.ready() - @pytest.mark.skip( - "pytest_httpx requires python >= 3.9. It can be enabled once we remove the support for python 3.8" - ) async def test_server_readiness_v2(self, httpx_mock): # scenario: getting a 2xx response from predictor predictor_host = "ready.host" @@ -634,9 +625,6 @@ async def test_server_readiness_grpc_v2(self, mock_grpc_client): url=predictor_host, timeout=5, retries=2, use_ssl=False ) - @pytest.mark.skip( - "pytest_httpx requires python >= 3.9. It can be enabled once we remove the support for python 3.8" - ) async def test_model_readiness_v1(self, httpx_mock): # scenario: getting a 2xx response from predictor predictor_host = "ready.host" @@ -698,9 +686,6 @@ async def test_model_readiness_v1(self, httpx_mock): dataplane._model_registry.update(not_ready_model) assert await dataplane.model_ready(not_ready_model.name) is False - @pytest.mark.skip( - "pytest_httpx requires python >= 3.9. It can be enabled once we remove the support for python 3.8" - ) async def test_model_readiness_v2(self, httpx_mock): # scenario: getting a 2xx response from predictor predictor_host = "ready.host" diff --git a/python/kserve/test/test_inference_client.py b/python/kserve/test/test_inference_client.py index 50eca5197f3..69d145463da 100644 --- a/python/kserve/test/test_inference_client.py +++ b/python/kserve/test/test_inference_client.py @@ -19,12 +19,11 @@ import pytest import pytest_asyncio -from kserve import ModelServer, InferenceRESTClient, InferRequest, InferInput +from kserve import InferenceRESTClient, InferRequest, InferInput from kserve.model_server import app as kserve_app from kserve.errors import UnsupportedProtocol from kserve.inference_client import RESTConfig from kserve.protocol.infer_type import RequestedOutput -from kserve.protocol.rest.server import RESTServer from test.test_server import DummyModel @@ -32,12 +31,7 @@ class TestInferenceRESTClient: @pytest_asyncio.fixture(scope="class") - async def app(self): - server = ModelServer() - rest_server = RESTServer( - kserve_app, server.dataplane, server.model_repository_extension - ) - rest_server.create_application() + async def app(self, server): model = DummyModel("TestModel") model.load() not_ready_model = DummyModel("NotReadyModel") @@ -47,7 +41,6 @@ async def app(self): yield kserve_app await server.model_repository_extension.unload("TestModel") await server.model_repository_extension.unload("NotReadyModel") - kserve_app.routes.clear() @pytest_asyncio.fixture(scope="class") async def rest_client(self, request, app): diff --git a/python/kserve/test/test_inference_client_factory.py b/python/kserve/test/test_inference_client_factory.py new file mode 100644 index 00000000000..bcfdf374c18 --- /dev/null +++ b/python/kserve/test/test_inference_client_factory.py @@ -0,0 +1,60 @@ +# Copyright 2024 The KServe Authors. +# +# Licensed 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. + +import pytest +from kserve.utils.inference_client_factory import InferenceClientFactory +from kserve.inference_client import InferenceRESTClient, RESTConfig + + +@pytest.fixture +def factory(): + return InferenceClientFactory() + + +def test_get_rest_client_v1(factory): + config = RESTConfig(protocol="v1") + client = factory.get_rest_client(config) + assert client._config.protocol == "v1" + assert factory._rest_v1_client is client + + +def test_get_rest_cleint_v1_config_none(factory): + client = factory.get_rest_client() + assert isinstance(client, InferenceRESTClient) + assert client._config.protocol == "v1" + assert factory._rest_v1_client is client + + +def test_get_rest_client_v2(factory): + config = RESTConfig(protocol="v2") + client = factory.get_rest_client(config) + assert isinstance(client, InferenceRESTClient) + assert client._config.protocol == "v2" + assert factory._rest_v2_client is client + + +def test_get_rest_client_v1_cached(factory): + config = RESTConfig(protocol="v1") + client1 = factory.get_rest_client(config) + client2 = factory.get_rest_client(config) + assert client1._config.protocol == "v1" + assert client1 is client2 + + +def test_get_rest_client_v2_cached(factory): + config = RESTConfig(protocol="v2") + client1 = factory.get_rest_client(config) + client2 = factory.get_rest_client(config) + assert client1._config.protocol == "v2" + assert client1 is client2 diff --git a/python/kserve/test/test_model_repository.py b/python/kserve/test/test_model_repository.py index 20197058cda..306db097d70 100644 --- a/python/kserve/test/test_model_repository.py +++ b/python/kserve/test/test_model_repository.py @@ -14,17 +14,23 @@ import pytest from kserve import ModelRepository, Model -from kserve.protocol.rest.openai import CompletionRequest, OpenAIModel +from kserve.protocol.rest.openai import ( + CompletionRequest, + OpenAICompletionModel, + EmbeddingRequest, + OpenAIEmbeddingModel, +) from unittest.mock import patch from kserve.protocol.rest.openai.types.openapi import ( CreateChatCompletionResponse as ChatCompletion, CreateChatCompletionStreamResponse as ChatCompletionChunk, CreateCompletionResponse as Completion, + Embedding, ) from typing import AsyncIterator, Union -class DummyOpenAIModel(OpenAIModel): +class DummyOpenAICompletionModel(OpenAICompletionModel): async def create_completion( self, params: CompletionRequest ) -> Union[Completion, AsyncIterator[Completion]]: @@ -36,6 +42,11 @@ async def create_chat_completion( pass +class DummyOpenAIEmbeddingModel(OpenAIEmbeddingModel): + async def create_embedding(self, params: EmbeddingRequest) -> Embedding: + pass + + def test_adding_kserve_model(): repo = ModelRepository() repo.update(Model(name="kserve-model")) @@ -47,15 +58,26 @@ def test_adding_kserve_model(): assert actual.name == "kserve-model" -def test_adding_openai_model(): +def test_adding_openai_completion_model(): + repo = ModelRepository() + repo.update(DummyOpenAICompletionModel(name="openai-completion-model")) + + actual = repo.get_model("openai-completion-model") + + assert actual is not None + assert isinstance(actual, OpenAICompletionModel) + assert actual.name == "openai-completion-model" + + +def test_adding_openai_embedding_model(): repo = ModelRepository() - repo.update(DummyOpenAIModel(name="openai-model")) + repo.update(DummyOpenAIEmbeddingModel(name="openai-embedding-model")) - actual = repo.get_model("openai-model") + actual = repo.get_model("openai-embedding-model") assert actual is not None - assert isinstance(actual, OpenAIModel) - assert actual.name == "openai-model" + assert isinstance(actual, OpenAIEmbeddingModel) + assert actual.name == "openai-embedding-model" @pytest.mark.asyncio @@ -83,7 +105,7 @@ async def test_is_model_ready_kserve_model(): @pytest.mark.asyncio async def test_is_model_ready_openai_model(): repo = ModelRepository() - model = DummyOpenAIModel(name="openai-model") + model = DummyOpenAICompletionModel(name="openai-model") repo.update(model) actual = await repo.is_model_ready("openai-model") diff --git a/python/kserve/test/test_openai.py b/python/kserve/test/test_openai_completion.py similarity index 100% rename from python/kserve/test/test_openai.py rename to python/kserve/test/test_openai_completion.py diff --git a/python/kserve/test/test_openai_embedding.py b/python/kserve/test/test_openai_embedding.py new file mode 100644 index 00000000000..44cb813da0d --- /dev/null +++ b/python/kserve/test/test_openai_embedding.py @@ -0,0 +1,73 @@ +# Copyright 2025 The KServe Authors. +# +# Licensed 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. + +from pathlib import Path +from typing import Tuple + +import pytest + +from kserve.protocol.rest.openai import ( + EmbeddingRequest, + OpenAIEmbeddingModel, +) +from kserve.protocol.rest.openai.types.openapi import ( + CreateEmbeddingRequest, +) +from kserve.protocol.rest.openai.types.openapi import ( + CreateEmbeddingResponse as Embedding, +) + +FIXTURES_PATH = Path(__file__).parent / "fixtures" / "openai" + + +class DummyModel(OpenAIEmbeddingModel): + data: Tuple[Embedding] + + def __init__(self, data: Tuple[Embedding]): + super().__init__("dummy-model") + self.data = data + + async def create_embedding(self, request: EmbeddingRequest) -> Embedding: + return self.data[0] + + +@pytest.fixture +def embedding(): + with open(FIXTURES_PATH / "embedding.json") as f: + return Embedding.model_validate_json(f.read()) + + +@pytest.fixture +def embedding_create_params(): + with open(FIXTURES_PATH / "embedding_create_params.json") as f: + return CreateEmbeddingRequest.model_validate_json(f.read()) + + +@pytest.fixture +def dummy_model(embedding): + return DummyModel((embedding,)) + + +class TestOpenAICreateEmbedding: + @pytest.mark.asyncio + async def test_create_embedding( + self, + dummy_model: DummyModel, + embedding: Embedding, + embedding_create_params: CreateEmbeddingRequest, + ): + request = EmbeddingRequest(params=embedding_create_params) + c = await dummy_model.create_embedding(request) + assert isinstance(c, Embedding) + assert c.model_dump_json(indent=2) == embedding.model_dump_json(indent=2) diff --git a/python/kserve/test/test_server.py b/python/kserve/test/test_server.py index c0b2b67286c..76b0c9806ec 100644 --- a/python/kserve/test/test_server.py +++ b/python/kserve/test/test_server.py @@ -46,7 +46,6 @@ InferResponse, RequestedOutput, ) -from kserve.protocol.rest.server import RESTServer from kserve.protocol.rest.v2_datamodels import is_pydantic_2 from kserve.utils.utils import generate_uuid, get_predict_input, get_predict_response @@ -109,15 +108,6 @@ async def predict(self, request, headers=None): class TestStreamPredict: - @pytest.fixture(scope="class") - def server(self): - server = ModelServer() - rest_server = RESTServer( - kserve_app, server.dataplane, server.model_repository_extension - ) - rest_server.create_application() - yield server - kserve_app.routes.clear() @pytest_asyncio.fixture(scope="class") async def app(self, server): # pylint: disable=no-self-use @@ -372,6 +362,11 @@ async def explain(self, payload, headers=None): return {"predictions": [datetime.datetime.now(tz=datetime.timezone.utc)]} +@pytest.fixture(scope="module") +def http_server_client(): + return TestClient(kserve_app, headers={"content-type": "application/json"}) + + @pytest.mark.asyncio class TestModel: async def test_validate(self): @@ -398,17 +393,7 @@ async def test_validate(self): class TestV1Endpoints: - @pytest.fixture(scope="class") - def server(self): - server = ModelServer(registered_models=ModelRepository()) - rest_server = RESTServer( - kserve_app, server.dataplane, server.model_repository_extension - ) - rest_server.create_application() - yield server - kserve_app.routes.clear() - - @pytest_asyncio.fixture(scope="class") + @pytest_asyncio.fixture(scope="class", autouse=True) async def app(self, server): model = DummyModel("TestModel") model.load() @@ -420,10 +405,6 @@ async def app(self, server): await server.model_repository_extension.unload("TestModel") await server.model_repository_extension.unload("DateTimeModel") - @pytest.fixture(scope="class") - def http_server_client(self, app): - return TestClient(app, headers={"content-type": "application/json"}) - def test_liveness_v1(self, http_server_client): resp = http_server_client.get("/") assert resp.status_code == 200 @@ -484,17 +465,8 @@ def test_datetime_output_v1(self, http_server_client): class TestV2Endpoints: - @pytest.fixture(scope="class") - def server(self): - server = ModelServer() - rest_server = RESTServer( - kserve_app, server.dataplane, server.model_repository_extension - ) - rest_server.create_application() - yield server - kserve_app.routes.clear() - @pytest_asyncio.fixture(scope="class") + @pytest_asyncio.fixture(scope="class", autouse=True) async def app(self, server): model = DummyModel("TestModel") model.load() @@ -514,10 +486,6 @@ async def app(self, server): await server.model_repository_extension.unload("FP16OutputModel") await server.model_repository_extension.unload("DateTimeModel") - @pytest.fixture(scope="class") - def http_server_client(self, app): - return TestClient(app, headers={"content-type": "application/json"}) - def test_list_models_v2(self, http_server_client): resp = http_server_client.get("/v2/models") assert resp.status_code == 200 @@ -883,17 +851,8 @@ def test_datetime_output(self, http_server_client): class TestRayServer: - @pytest.fixture(scope="class") - def server(self): - server = ModelServer() - rest_server = RESTServer( - kserve_app, server.dataplane, server.model_repository_extension - ) - rest_server.create_application() - yield server - kserve_app.routes.clear() - @pytest_asyncio.fixture(scope="class") + @pytest_asyncio.fixture(scope="class", autouse=True) async def app(self, server): # pylint: disable=no-self-use serve.start(http_options={"host": "0.0.0.0", "port": 9071}) @@ -909,10 +868,6 @@ async def app(self, server): # pylint: disable=no-self-use await server.model_repository_extension.unload("TestModel") serve.shutdown() - @pytest.fixture(scope="class") - def http_server_client(self, app): - return TestClient(app, headers={"content-type": "application/json"}) - def test_liveness_handler(self, http_server_client): resp = http_server_client.get("/") assert resp.status_code == 200 @@ -962,44 +917,22 @@ def test_explain(self, http_server_client): class TestTFHttpServerModelNotLoaded: - @pytest.fixture(scope="class") - def server(self): - server = ModelServer() - rest_server = RESTServer( - kserve_app, server.dataplane, server.model_repository_extension - ) - rest_server.create_application() - yield server - kserve_app.routes.clear() - @pytest_asyncio.fixture(scope="class") + @pytest_asyncio.fixture(scope="class", autouse=True) async def app(self, server): # pylint: disable=no-self-use model = DummyModel("TestModel") server.register_model(model) yield kserve_app await server.model_repository_extension.unload("TestModel") - @pytest.fixture(scope="class") - def http_server_client(self, app): - return TestClient(app) - def test_model_not_ready_error(self, http_server_client): resp = http_server_client.get("/v1/models/TestModel") assert resp.status_code == 503 class TestTFHttpServerCloudEvent: - @pytest.fixture(scope="class") - def server(self): - server = ModelServer() - rest_server = RESTServer( - kserve_app, server.dataplane, server.model_repository_extension - ) - rest_server.create_application() - yield server - kserve_app.routes.clear() - @pytest_asyncio.fixture(scope="class") + @pytest_asyncio.fixture(scope="class", autouse=True) async def app(self, server): # pylint: disable=no-self-use model = DummyCEModel("TestModel") model.load() @@ -1007,10 +940,6 @@ async def app(self, server): # pylint: disable=no-self-use yield kserve_app await server.model_repository_extension.unload("TestModel") - @pytest.fixture(scope="class") - def http_server_client(self, app): - return TestClient(app) - def test_predict_ce_structured(self, http_server_client): event = dummy_cloud_event({"instances": [[1, 2]]}) headers, body = to_structured(event) @@ -1165,17 +1094,8 @@ def test_predict_ce_bytes_bad_hex_format_exception(self, http_server_client): class TestTFHttpServerAvroCloudEvent: - @pytest.fixture(scope="class") - def server(self): - server = ModelServer() - rest_server = RESTServer( - kserve_app, server.dataplane, server.model_repository_extension - ) - rest_server.create_application() - yield server - kserve_app.routes.clear() - @pytest_asyncio.fixture(scope="class") + @pytest_asyncio.fixture(scope="class", autouse=True) async def app(self, server): # pylint: disable=no-self-use model = DummyAvroCEModel("TestModel") model.load() @@ -1183,10 +1103,6 @@ async def app(self, server): # pylint: disable=no-self-use yield kserve_app await server.model_repository_extension.unload("TestModel") - @pytest.fixture(scope="class") - def http_server_client(self, app): - return TestClient(app) - def test_predict_ce_avro_binary(self, http_server_client): schema = avro.schema.parse(test_avsc_schema) msg = {"name": "foo", "favorite_number": 1, "favorite_color": "pink"} @@ -1217,21 +1133,16 @@ def test_predict_ce_avro_binary(self, http_server_client): class TestTFHttpServerLoadAndUnLoad: - @pytest.fixture(scope="class") - def app(self): - server = ModelServer( - registered_models=DummyModelRepository(test_load_success=True) - ) - rest_server = RESTServer( - kserve_app, server.dataplane, server.model_repository_extension + @pytest_asyncio.fixture(scope="class", autouse=True) + def app(self, server): + mp = pytest.MonkeyPatch() + mp.setattr( + server.model_repository_extension, + "_model_registry", + DummyModelRepository(test_load_success=True), ) - rest_server.create_application() - yield kserve_app - kserve_app.routes.clear() - - @pytest.fixture(scope="class") - def http_server_client(self, app): - return TestClient(app) + yield + mp.undo() def test_load(self, http_server_client): resp = http_server_client.post("/v2/repository/models/model/load", content=b"") @@ -1247,21 +1158,16 @@ def test_unload(self, http_server_client): class TestTFHttpServerLoadAndUnLoadFailure: - @pytest.fixture(scope="class") - def app(self): - server = ModelServer( - registered_models=DummyModelRepository(test_load_success=False) - ) - rest_server = RESTServer( - kserve_app, server.dataplane, server.model_repository_extension + @pytest.fixture(scope="class", autouse=True) + def app(self, server): + mp = pytest.MonkeyPatch() + mp.setattr( + server.model_repository_extension, + "_model_registry", + DummyModelRepository(test_load_success=False), ) - rest_server.create_application() - yield kserve_app - kserve_app.routes.clear() - - @pytest.fixture(scope="class") - def http_server_client(self, app): - return TestClient(app) + yield + mp.undo() def test_load_fail(self, http_server_client): resp = http_server_client.post("/v2/repository/models/model/load", content=b"") @@ -1275,27 +1181,14 @@ def test_unload_fail(self, http_server_client): class TestTFHttpServerModelNotReady: - @pytest.fixture(scope="class") - def server(self): - server = ModelServer() - rest_server = RESTServer( - kserve_app, server.dataplane, server.model_repository_extension - ) - rest_server.create_application() - yield server - kserve_app.routes.clear() - @pytest_asyncio.fixture(scope="class") + @pytest_asyncio.fixture(scope="class", autouse=True) async def app(self, server): # pylint: disable=no-self-use model = DummyModel("TestModel") server.register_model(model) yield kserve_app await server.model_repository_extension.unload("TestModel") - @pytest.fixture(scope="class") - def http_server_client(self, app): - return TestClient(app) - def test_model_not_ready_v1(self, http_server_client): resp = http_server_client.get("/v1/models/TestModel") assert resp.status_code == 503 diff --git a/python/kserve/test/test_v1beta1_inference_services_config.py b/python/kserve/test/test_v1beta1_inference_services_config.py index 27823906375..6cdfefa56d8 100644 --- a/python/kserve/test/test_v1beta1_inference_services_config.py +++ b/python/kserve/test/test_v1beta1_inference_services_config.py @@ -59,6 +59,10 @@ def make_instance(self, include_optional): image="0", ), ), + resource=kserve.models.v1beta1_resource_config.V1beta1ResourceConfig( + cpu_limit="", + memory_limit="", + ), ) else: return V1beta1InferenceServicesConfig( @@ -68,6 +72,10 @@ def make_instance(self, include_optional): image="0", ), ), + resource=kserve.models.v1beta1_resource_config.V1beta1ResourceConfig( + cpu_limit="", + memory_limit="", + ), ) def testV1beta1InferenceServicesConfig(self): diff --git a/python/kserve/test/test_v1beta1_resource_config.py b/python/kserve/test/test_v1beta1_resource_config.py new file mode 100644 index 00000000000..d6073899b05 --- /dev/null +++ b/python/kserve/test/test_v1beta1_resource_config.py @@ -0,0 +1,67 @@ +# Copyright 2023 The KServe Authors. +# +# Licensed 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. + +# coding: utf-8 + +""" + KServe + + Python SDK for KServe # noqa: E501 + + The version of the OpenAPI document: v0.1 + Generated by: https://openapi-generator.tech +""" + + +from __future__ import absolute_import + +import unittest +import datetime + +import kserve +from kserve.models.v1beta1_resource_config import V1beta1ResourceConfig # noqa: E501 +from kserve.rest import ApiException + + +class TestV1beta1ResourceConfig(unittest.TestCase): + """V1beta1ResourceConfig unit test stubs""" + + def setUp(self): + pass + + def tearDown(self): + pass + + def make_instance(self, include_optional): + """Test V1beta1ResourceConfig + include_option is a boolean, when False only required + params are included, when True both required and + optional params are included""" + # model = kserve.models.v1beta1_resource_config.V1beta1ResourceConfig() # noqa: E501 + if include_optional: + return V1beta1ResourceConfig(cpu_limit="", memory_limit="") + else: + return V1beta1ResourceConfig( + cpu_limit="", + memory_limit="", + ) + + def testV1beta1ResourceConfig(self): + """Test V1beta1ResourceConfig""" + inst_req_only = self.make_instance(include_optional=False) + inst_req_and_optional = self.make_instance(include_optional=True) + + +if __name__ == "__main__": + unittest.main() diff --git a/python/lgbserver/poetry.lock b/python/lgbserver/poetry.lock index 3b295886345..d0fc18b9212 100644 --- a/python/lgbserver/poetry.lock +++ b/python/lgbserver/poetry.lock @@ -1220,7 +1220,7 @@ files = [ [[package]] name = "kserve" -version = "0.14.0" +version = "0.15.0rc0" description = "KServe Python SDK" optional = false python-versions = ">=3.9,<3.13" diff --git a/python/lgbserver/pyproject.toml b/python/lgbserver/pyproject.toml index c9bc11452d0..a8fd43f086f 100644 --- a/python/lgbserver/pyproject.toml +++ b/python/lgbserver/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "lgbserver" -version = "0.14.0" +version = "0.15.0rc0" description = "Model Server implementation for LightGBM. Not intended for use outside KServe Frameworks Images." authors = ["Lin Yiming "] license = "https://github.com/kserve/kserve/blob/master/LICENSE" diff --git a/python/paddleserver/poetry.lock b/python/paddleserver/poetry.lock index 0eb327c73ad..947c6782f56 100644 --- a/python/paddleserver/poetry.lock +++ b/python/paddleserver/poetry.lock @@ -1231,7 +1231,7 @@ files = [ [[package]] name = "kserve" -version = "0.14.0" +version = "0.15.0rc0" description = "KServe Python SDK" optional = false python-versions = ">=3.9,<3.13" diff --git a/python/paddleserver/pyproject.toml b/python/paddleserver/pyproject.toml index 5e0bd3da89f..1c189e6c66f 100644 --- a/python/paddleserver/pyproject.toml +++ b/python/paddleserver/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "paddleserver" -version = "0.14.0" +version = "0.15.0rc0" description = "Model Server implementation for Paddle. Not intended for use outside KServe Frameworks Images" authors = ["Zhengyuan Zhang "] license = "https://github.com/kserve/kserve/blob/master/LICENSE" diff --git a/python/pmmlserver/poetry.lock b/python/pmmlserver/poetry.lock index fcbfb969f81..cbb834d5565 100644 --- a/python/pmmlserver/poetry.lock +++ b/python/pmmlserver/poetry.lock @@ -1264,7 +1264,7 @@ tests = ["pytest"] [[package]] name = "kserve" -version = "0.14.0" +version = "0.15.0rc0" description = "KServe Python SDK" optional = false python-versions = ">=3.9,<3.13" diff --git a/python/pmmlserver/pyproject.toml b/python/pmmlserver/pyproject.toml index aecf1449bfe..d429316aaee 100644 --- a/python/pmmlserver/pyproject.toml +++ b/python/pmmlserver/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pmmlserver" -version = "0.14.0" +version = "0.15.0rc0" description = "Model Server implementation for PMML. Not intended for use outside KServe Frameworks Images." authors = ["AnyISalIn "] license = "https://github.com/kserve/kserve/blob/master/LICENSE" diff --git a/python/sklearnserver/poetry.lock b/python/sklearnserver/poetry.lock index e4dfe65d0ea..efbdaaecb89 100644 --- a/python/sklearnserver/poetry.lock +++ b/python/sklearnserver/poetry.lock @@ -1220,7 +1220,7 @@ files = [ [[package]] name = "kserve" -version = "0.14.0" +version = "0.15.0rc0" description = "KServe Python SDK" optional = false python-versions = ">=3.9,<3.13" diff --git a/python/sklearnserver/pyproject.toml b/python/sklearnserver/pyproject.toml index 0672b629b0c..830eab99771 100644 --- a/python/sklearnserver/pyproject.toml +++ b/python/sklearnserver/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "sklearnserver" -version = "0.14.0" +version = "0.15.0rc0" description = "Model Server implementation for scikit-learn. Not intended for use outside KServe Frameworks Images." authors = ["singhan "] license = "https://github.com/kserve/kserve/blob/master/LICENSE" diff --git a/python/test_resources/graph/error_404_isvc/poetry.lock b/python/test_resources/graph/error_404_isvc/poetry.lock index dbd2937e5bc..9f9a09b0a48 100644 --- a/python/test_resources/graph/error_404_isvc/poetry.lock +++ b/python/test_resources/graph/error_404_isvc/poetry.lock @@ -507,7 +507,7 @@ files = [ [[package]] name = "kserve" -version = "0.14.0" +version = "0.15.0rc0" description = "KServe Python SDK" optional = false python-versions = ">=3.9,<3.13" diff --git a/python/test_resources/graph/error_404_isvc/pyproject.toml b/python/test_resources/graph/error_404_isvc/pyproject.toml index fb2ca85af64..e523f0e8285 100644 --- a/python/test_resources/graph/error_404_isvc/pyproject.toml +++ b/python/test_resources/graph/error_404_isvc/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "error_404_isvc" -version = "0.14.0" +version = "0.15.0rc0" description = "Custom isvc that always returns 404 with a JSON message" authors = ["The KServe Authors"] license = "https://github.com/kserve/kserve/blob/master/LICENSE" diff --git a/python/test_resources/graph/success_200_isvc/poetry.lock b/python/test_resources/graph/success_200_isvc/poetry.lock index dbd2937e5bc..9f9a09b0a48 100644 --- a/python/test_resources/graph/success_200_isvc/poetry.lock +++ b/python/test_resources/graph/success_200_isvc/poetry.lock @@ -507,7 +507,7 @@ files = [ [[package]] name = "kserve" -version = "0.14.0" +version = "0.15.0rc0" description = "KServe Python SDK" optional = false python-versions = ">=3.9,<3.13" diff --git a/python/test_resources/graph/success_200_isvc/pyproject.toml b/python/test_resources/graph/success_200_isvc/pyproject.toml index b539fa7c6b9..51f70223d2a 100644 --- a/python/test_resources/graph/success_200_isvc/pyproject.toml +++ b/python/test_resources/graph/success_200_isvc/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "success_200_isvc" -version = "0.14.0" +version = "0.15.0rc0" description = "Custom isvc that always returns 200 with a JSON SUCCESS message" authors = ["The KServe Authors"] license = "https://github.com/kserve/kserve/blob/master/LICENSE" diff --git a/python/xgbserver/poetry.lock b/python/xgbserver/poetry.lock index 2d859de2604..3ff0b1b39f8 100644 --- a/python/xgbserver/poetry.lock +++ b/python/xgbserver/poetry.lock @@ -1220,7 +1220,7 @@ files = [ [[package]] name = "kserve" -version = "0.14.0" +version = "0.15.0rc0" description = "KServe Python SDK" optional = false python-versions = ">=3.9,<3.13" diff --git a/python/xgbserver/pyproject.toml b/python/xgbserver/pyproject.toml index 47a58643846..bb176294dae 100644 --- a/python/xgbserver/pyproject.toml +++ b/python/xgbserver/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "xgbserver" -version = "0.14.0" +version = "0.15.0rc0" description = "Model Server implementation for XGBoost. Not intended for use outside KServe Frameworks Images." authors = ["Ellis Tarn "] license = "https://github.com/kserve/kserve/blob/master/LICENSE" diff --git a/qpext/go.mod b/qpext/go.mod index 206752fa8e8..43aca1fb658 100644 --- a/qpext/go.mod +++ b/qpext/go.mod @@ -1,16 +1,14 @@ module github.com/kserve/kserve/qpext -go 1.22.0 - -toolchain go1.22.6 +go 1.23.6 require ( github.com/hashicorp/go-multierror v1.1.1 github.com/prometheus/client_model v0.6.1 - github.com/prometheus/common v0.57.0 - github.com/stretchr/testify v1.9.0 + github.com/prometheus/common v0.62.0 + github.com/stretchr/testify v1.10.0 go.uber.org/zap v1.27.0 - knative.dev/serving v0.42.2 + knative.dev/serving v0.44.0 ) require ( @@ -28,18 +26,18 @@ require ( github.com/go-logfmt/logfmt v0.6.0 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/go-cmp v0.6.0 // indirect - github.com/google/go-containerregistry v0.20.2 // indirect + github.com/google/go-containerregistry v0.13.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/gorilla/websocket v1.5.3 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/golang-lru v1.0.2 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/kelseyhightower/envconfig v1.4.0 // indirect - github.com/klauspost/compress v1.17.9 // indirect + github.com/klauspost/compress v1.17.11 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect @@ -47,38 +45,40 @@ require ( github.com/openzipkin/zipkin-go v0.4.3 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/prometheus/client_golang v1.20.2 // indirect + github.com/prometheus/client_golang v1.20.4 // indirect github.com/prometheus/procfs v0.15.1 // indirect github.com/prometheus/statsd_exporter v0.27.1 // indirect github.com/x448/float16 v0.8.4 // indirect go.opencensus.io v0.24.0 // indirect - go.uber.org/atomic v1.11.0 // indirect - go.uber.org/automaxprocs v1.5.3 // indirect + go.uber.org/automaxprocs v1.6.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/net v0.28.0 // indirect - golang.org/x/oauth2 v0.22.0 // indirect - golang.org/x/sync v0.8.0 // indirect - golang.org/x/sys v0.24.0 // indirect - golang.org/x/term v0.23.0 // indirect - golang.org/x/text v0.17.0 // indirect + golang.org/x/net v0.34.0 // indirect + golang.org/x/oauth2 v0.26.0 // indirect + golang.org/x/sync v0.11.0 // indirect + golang.org/x/sys v0.29.0 // indirect + golang.org/x/term v0.28.0 // indirect + golang.org/x/text v0.22.0 // indirect golang.org/x/time v0.6.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect - google.golang.org/api v0.195.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240827150818-7e3bb234dfed // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240827150818-7e3bb234dfed // indirect - google.golang.org/grpc v1.66.0 // indirect - google.golang.org/protobuf v1.34.2 // indirect + google.golang.org/api v0.198.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250204164813-702378808489 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250204164813-702378808489 // indirect + google.golang.org/grpc v1.70.0 // indirect + google.golang.org/protobuf v1.36.5 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/api v0.31.0 // indirect - k8s.io/apimachinery v0.31.0 // indirect - k8s.io/client-go v0.31.0 // indirect + k8s.io/api v0.31.4 // indirect + k8s.io/apimachinery v0.31.4 // indirect + k8s.io/client-go v0.31.4 // indirect k8s.io/klog/v2 v2.130.1 // indirect - k8s.io/utils v0.0.0-20240821151609-f90d01438635 // indirect - knative.dev/networking v0.0.0-20240815142417-37fdbdd0854b // indirect - knative.dev/pkg v0.0.0-20240815051656-89743d9bbf7c // indirect + k8s.io/utils v0.0.0-20240921022957-49e7df575cb6 // indirect + knative.dev/networking v0.0.0-20250117155906-67d1c274ba6a // indirect + knative.dev/pkg v0.0.0-20250117084104-c43477f0052b // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect sigs.k8s.io/yaml v1.4.0 // indirect ) + +// Fixes CVE-2024-45338 +replace golang.org/x/net => golang.org/x/net v0.33.0 diff --git a/qpext/go.sum b/qpext/go.sum index 98173ccffb1..63b4668ac9a 100644 --- a/qpext/go.sum +++ b/qpext/go.sum @@ -1,4 +1,3 @@ -cel.dev/expr v0.15.0/go.mod h1:TRSuuV7DlVCE/uwv5QbAiW/v8l5O8C4eEPHeu7gf7Sg= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= @@ -14,17 +13,12 @@ cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKV cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.115.1/go.mod h1:DuujITeaufu3gL68/lOFIirVNJwQeyf5UXyi+Wbgknc= -cloud.google.com/go/auth v0.9.1/go.mod h1:Sw8ocT5mhhXxFklyhT12Eiy0ed6tTrPMCJjSI8KhYLk= -cloud.google.com/go/auth/oauth2adapt v0.2.4/go.mod h1:jC/jOpwFP6JBxhB3P5Rr0a9HLMC/Pe3eaL4NmdvqPtc= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/compute v1.19.3/go.mod h1:qxvISKp/gYnXkSAD1ppcSOveRAmzxicEv/JlizULFrI= -cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= @@ -43,89 +37,41 @@ contrib.go.opencensus.io/exporter/prometheus v0.4.2/go.mod h1:dvEHbiKmgvbr5pjaF9 contrib.go.opencensus.io/exporter/zipkin v0.1.2 h1:YqE293IZrKtqPnpwDPH/lOqTWD/s3Iwabycam74JV3g= contrib.go.opencensus.io/exporter/zipkin v0.1.2/go.mod h1:mP5xM3rrgOjpn79MM8fZbj3gsxcuytSqtH0dxSWW1RE= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest v0.11.29/go.mod h1:ZtEzC4Jy2JDrZLxvWs8LrBWEBycl1hbT1eknI8MtfAs= -github.com/Azure/go-autorest/autorest/adal v0.9.23/go.mod h1:5pcMqFkdPhviJdlEy3kC/v1ZLnQl0MH6XA5YCcMhy4c= -github.com/Azure/go-autorest/autorest/azure/auth v0.5.11/go.mod h1:84w/uV8E37feW2NCJ08uT9VBfjfUHpgLVnG2InYD6cg= -github.com/Azure/go-autorest/autorest/azure/cli v0.4.6/go.mod h1:piCfgPho7BiIDdEQ1+g4VmKyD5y+p/XtSNqE6Hc4QD0= -github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= -github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= -github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/IBM/sarama v1.43.1/go.mod h1:GG5q1RURtDNPz8xxJs3mgX6Ytak8Z9eLhAkJPObe2xE= -github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= -github.com/ahmetb/gen-crd-api-reference-docs v0.3.1-0.20210609063737-0067dc6dcea2/go.mod h1:TdjdkYhlOifCQWPs1UdTma97kQQMozf5h26hTuG70u8= -github.com/alecthomas/kingpin/v2 v2.4.0/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= -github.com/alecthomas/units v0.0.0-20240626203959-61d1e3462e30/go.mod h1:fvzegU4vN3H1qMT+8wDmzjAcDONcgo2/SZ/TyfdUOFs= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -github.com/aws/aws-sdk-go-v2 v1.16.16/go.mod h1:SwiyXi/1zTUZ6KIAmLK5V5ll8SiURNUYOqTerZPaF9k= -github.com/aws/aws-sdk-go-v2/config v1.17.8/go.mod h1:UkCI3kb0sCdvtjiXYiU4Zx5h07BOpgBTtkPu/49r+kA= -github.com/aws/aws-sdk-go-v2/credentials v1.12.21/go.mod h1:O+4XyAt4e+oBAoIwNUYkRg3CVMscaIJdmZBOcPgJ8D8= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.17/go.mod h1:yIkQcCDYNsZfXpd5UX2Cy+sWA1jPgIhGTw9cOBzfVnQ= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.23/go.mod h1:2DFxAQ9pfIRy0imBCJv+vZ2X6RKxves6fbnEuSry6b4= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.17/go.mod h1:pRwaTYCJemADaqCbUAxltMoHKata7hmB5PjEXeu0kfg= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.24/go.mod h1:jULHjqqjDlbyTa7pfM7WICATnOv+iOhjletM3N0Xbu8= -github.com/aws/aws-sdk-go-v2/service/ecr v1.17.18/go.mod h1:DQtDYmexqR+z+B6HBCvY7zK/tuXKv6Zy/IwOXOK3eow= -github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.13.17/go.mod h1:r1Vuka0kyzqN0sZm4lYTXf0Vhl+o/mTLq6vKpBBZYaQ= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.17/go.mod h1:4nYOrY41Lrbk2170/BGkcJKBhws9Pfn8MG3aGqjjeFI= -github.com/aws/aws-sdk-go-v2/service/sso v1.11.23/go.mod h1:/w0eg9IhFGjGyyncHIQrXtU8wvNsTJOP0R6PPj0wf80= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.6/go.mod h1:csZuQY65DAdFBt1oIjO5hhBR49kQqop4+lcuCjf2arA= -github.com/aws/aws-sdk-go-v2/service/sts v1.16.19/go.mod h1:h4J3oPZQbxLhzGnk+j9dfYHi5qIOVJ5kczZd658/ydM= -github.com/aws/smithy-go v1.13.3/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= -github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20221004211355-a250ad2ca1e3/go.mod h1:m06KtrZgOloUaePAQMv+Ha8kRmTnKdozTHZrweepIrw= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/blendle/zapdriver v1.3.1 h1:C3dydBOWYRiOk+B8X9IVZ5IOe+7cl+tGOexN4QqHfpE= github.com/blendle/zapdriver v1.3.1/go.mod h1:mdXfREi6u5MArG4j9fewC+FGnXaBR+T4Ox4J2u4eHCc= -github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g= github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= -github.com/cert-manager/cert-manager v1.13.3/go.mod h1:BM2+Pt/NmSv1Zr25/MHv6BgIEF9IUxA1xAjp80qkxgc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chrismellard/docker-credential-acr-env v0.0.0-20221002210726-e883f69e0206/go.mod h1:1UmFRnmMnVsHwD+ZntmLkoVBB1ZLa6V+XXEbF6hZCxU= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= -github.com/containerd/stargz-snapshotter/estargz v0.14.3/go.mod h1:KY//uOCIkSuNAHhJogcZtrNHdKrA99/FCCRjE3HD36o= -github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/deepmap/oapi-codegen v1.8.2/go.mod h1:YLgSKSDv/bZQB7N4ws6luhozi3cEdRktEqrX88CvjIw= -github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= -github.com/docker/cli v27.1.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v25.0.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0= -github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= -github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= -github.com/eapache/go-resiliency v1.6.0/go.mod h1:5yPzW0MIvSe0JDsv0v+DvcjEv2FyD6iZYSs1ZI+iQho= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= -github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3/go.mod h1:YvSRo5mw33fLEx1+DlK6L2VV43tJt5Eyel9n9XBcR+0= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/emicklei/go-restful/v3 v3.12.1 h1:PJMDIM/ak7btuL8Ex0iYET9hxM3CI2sjZtzpL63nKAU= github.com/emicklei/go-restful/v3 v3.12.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= @@ -133,14 +79,11 @@ github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4s github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.12.1-0.20240621013728-1eb8caab5155/go.mod h1:5Wkq+JduFtdAXihLmeTJf+tRYIT4KBc2vPXDhwVo1pA= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew= github.com/evanphx/json-patch v5.9.0+incompatible h1:fBXyNpNMuTTDdquAq/uisOr2lShz4oaXpDTX2bLe7ls= github.com/evanphx/json-patch v5.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg= github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= -github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= @@ -162,6 +105,7 @@ github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= @@ -170,22 +114,18 @@ github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDsl github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= -github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= -github.com/gobuffalo/flect v1.0.2/go.mod h1:A5msMlrHtLqh9umBSnvabjsMrCcCpAyzglnDvkbYKHs= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.2.1/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= @@ -212,10 +152,8 @@ github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiu github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -232,10 +170,8 @@ github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-containerregistry v0.20.2 h1:B1wPJ1SN/S7pB+ZAimcciVD+r+yV/l/DSArMxlbwseo= -github.com/google/go-containerregistry v0.20.2/go.mod h1:z38EKdKh4h7IP2gSfUUqEvalZBqs6AoLeWfUy34nQC8= -github.com/google/go-containerregistry/pkg/authn/k8schain v0.0.0-20230209165335-3624968304fd/go.mod h1:x5fIlj5elU+/eYF60q4eASMQ9kDc+GMFa7UU9M3mFFw= -github.com/google/go-containerregistry/pkg/authn/kubernetes v0.0.0-20230209165335-3624968304fd/go.mod h1:6pjZpt+0dg+Z0kUEn53qLtD57raiZo/bqWzsuX6dDjo= +github.com/google/go-containerregistry v0.13.0 h1:y1C7Z3e149OJbOPDBxLYR8ITPz8dTKqQwjErKVHJC8k= +github.com/google/go-containerregistry v0.13.0/go.mod h1:J9FQ+eSS4a1aC2GNZxvNpbWhgp0487v+cgiilB4FqDo= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -248,30 +184,24 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.13.0/go.mod h1:Z/fvTZXF8/uw7Xu5GuslPw+bplx6SS338j1Is2S+B7A= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/grpc-gateway v1.14.6/go.mod h1:zdiPV4Yse/1gnckTHtghG4GkDEdKCRJduHpTxT3/jcw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 h1:e9Rjr40Z98/clHv5Yg79Is0NtosR5LXRvdr7o/6NwbA= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1/go.mod h1:tIxuGz/9mpox++sgp9fJjHO0+q1X9/UOWd798aAm22M= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= @@ -280,17 +210,6 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= -github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/influxdata/influxdb-client-go/v2 v2.9.0/go.mod h1:x7Jo5UHHl+w8wu8UnGiNobDDHygojXwJX4mx7rXGKMk= -github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo= -github.com/influxdata/tdigest v0.0.1/go.mod h1:Z0kXnxzbTC2qrx4NaIzYkE1k66+6oEDQTvL95hQFh5Y= -github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs= -github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM= -github.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo= -github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs= -github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= -github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jmespath/go-jmespath v0.4.1-0.20220621161143-b0104c826a24/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= @@ -307,8 +226,8 @@ github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dv github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= -github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= +github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= @@ -325,9 +244,6 @@ github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0Q github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/moby/spdystream v0.4.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= -github.com/moby/term v0.0.0-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -335,32 +251,24 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.1.0-rc3/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/openzipkin/zipkin-go v0.4.3 h1:9EGwpqkgnwdEIJ+Od7QVSEIH+ocmm5nPat0G7sjsSdg= github.com/openzipkin/zipkin-go v0.4.3/go.mod h1:M9wCJZFWCo2RiY+o1eBCEMe0Dp2S5LDHcMZmk3RmK7c= -github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= -github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= -github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -373,8 +281,8 @@ github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqr github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ= -github.com/prometheus/client_golang v1.20.2 h1:5ctymQzZlyOON1666svgwn3s6IKWgfbjsejTMiXIyjg= -github.com/prometheus/client_golang v1.20.2/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_golang v1.20.4 h1:Tgh3Yr67PaOv/uTqloMsCEdeuFTatm5zIq5+qNN23vI= +github.com/prometheus/client_golang v1.20.4/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -387,9 +295,8 @@ github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9 github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/common v0.35.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= -github.com/prometheus/common v0.57.0 h1:Ro/rKjwdq9mZn1K5QPctzh+MA4Lp0BuYk5ZZEVhoNcY= -github.com/prometheus/common v0.57.0/go.mod h1:7uRPFSUTbfZWsJ7MHY56sqt7hLQu3bxXHDnNhl8E9qI= -github.com/prometheus/exporter-toolkit v0.11.0/go.mod h1:BVnENhnNecpwoTLiABx7mrPB/OLRIgN74qlQbV+FK1Q= +github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= +github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= @@ -401,20 +308,14 @@ github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoG github.com/prometheus/statsd_exporter v0.22.7/go.mod h1:N/TevpjkIh9ccs6nuzY3jQn9dFqnUakOjnEuMPJJJnI= github.com/prometheus/statsd_exporter v0.27.1 h1:tcRJOmwlA83HPfWzosAgr2+zEN5XDFv+M2mn/uYkn5Y= github.com/prometheus/statsd_exporter v0.27.1/go.mod h1:vA6ryDfsN7py/3JApEst6nLTJboq66XsNcJGNmC88NQ= -github.com/rabbitmq/amqp091-go v1.9.0/go.mod h1:+jPrT9iY2eLjRaMSRHUhc3z14E/l85kv/f+6luSD3pc= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= -github.com/rs/dnscache v0.0.0-20230804202142-fc85eb664529/go.mod h1:qe5TWALJ8/a1Lqznoc5BDHpYX/8HU60Hm2AwRmqzxqA= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= @@ -422,7 +323,6 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -430,19 +330,16 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stvp/go-udp-testing v0.0.0-20201019212854-469649b16807/go.mod h1:7jxmlfBCDBXRzr0eAQJ48XC1hBu1np4CS5+cHEYfwpc= -github.com/tsenart/go-tsz v0.0.0-20180814235614-0bd30b3df1c3/go.mod h1:SWZznP1z5Ki7hDT2ioqiFKEse8K9tU2OUvaRI0NeGQo= -github.com/tsenart/vegeta/v12 v12.12.0/go.mod h1:gpdfR++WHV9/RZh4oux0f6lNPhsOH8pCjIGUlcPQe1M= -github.com/vbatts/tar-split v0.11.3/go.mod h1:9QlHN18E+fEH7RdG+QAJJcuya3rqT7eXSTY7wGrAokY= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= -github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -451,16 +348,19 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= -go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= -go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= -go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= +go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U= +go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg= +go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M= +go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8= +go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4= +go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU= +go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU= +go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ= +go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM= +go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= -go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8= -go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0= +go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= +go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= @@ -470,12 +370,11 @@ go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -486,7 +385,6 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -507,45 +405,14 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= -golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= -golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= +golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -553,8 +420,8 @@ golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4Iltr golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= -golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.26.0 h1:afQXWNNaeC4nvZ0Ed9XvCCzXM6UHJG7iCg0W4fPqSBE= +golang.org/x/oauth2 v0.26.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -566,8 +433,13 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= +golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -591,7 +463,6 @@ golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -600,31 +471,34 @@ golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= -golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= -golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= +golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= +golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= -golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -672,8 +546,11 @@ golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= -golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE= +golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -697,15 +574,14 @@ google.golang.org/api v0.25.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.195.0 h1:Ude4N8FvTKnnQJHU48RFI40jOBgIrL8Zqr3/QeST6yU= -google.golang.org/api v0.195.0/go.mod h1:DOGRWuv3P8TU8Lnz7uQc4hyNqrBpMtD9ppW3wBJurgc= +google.golang.org/api v0.198.0 h1:OOH5fZatk57iN0A7tjJQzt6aPfYQ1JiWkt1yGseazks= +google.golang.org/api v0.198.0/go.mod h1:/Lblzl3/Xqqk9hw/yS97TImKTUwnf1bv89v7+OagJzc= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -737,12 +613,10 @@ google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7Fc google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20240823204242-4ba0660f739c/go.mod h1:2rC5OendXvZ8wGEo/cSLheztrZDZaSoHanUcd1xtZnw= -google.golang.org/genproto/googleapis/api v0.0.0-20240827150818-7e3bb234dfed h1:3RgNmBoI9MZhsj3QxC+AP/qQhNwpCLOvYDYYsFrhFt0= -google.golang.org/genproto/googleapis/api v0.0.0-20240827150818-7e3bb234dfed/go.mod h1:OCdP9MfskevB/rbYvHTsXTtKC+3bHWajPdoKgjcYkfo= -google.golang.org/genproto/googleapis/bytestream v0.0.0-20240823204242-4ba0660f739c/go.mod h1:gQizMG9jZ0L2ADJaM+JdZV4yTCON/CQpnHRPoM+54w4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240827150818-7e3bb234dfed h1:J6izYgfBXAI3xTKLgxzTmUltdYaLsuBxFCgDHWJ/eXg= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240827150818-7e3bb234dfed/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= +google.golang.org/genproto/googleapis/api v0.0.0-20250204164813-702378808489 h1:fCuMM4fowGzigT89NCIsW57Pk9k2D12MMi2ODn+Nk+o= +google.golang.org/genproto/googleapis/api v0.0.0-20250204164813-702378808489/go.mod h1:iYONQfRdizDB8JJBybql13nArx91jcUk7zCXEsOofM4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250204164813-702378808489 h1:5bKytslY8ViY0Cj/ewmRtrWHW64bNF03cAatUUFCdFI= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250204164813-702378808489/go.mod h1:8BS3B93F/U1juMFq9+EDk+qOT5CO1R9IzXxG3PTqiRk= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= @@ -757,8 +631,8 @@ google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3Iji google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.66.0 h1:DibZuoBznOxbDQxRINckZcUvnCEvrW9pcWIE2yF9r1c= -google.golang.org/grpc v1.66.0/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y= +google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ= +google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -773,8 +647,8 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= +google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -800,7 +674,6 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -808,35 +681,27 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.31.0 h1:b9LiSjR2ym/SzTOlfMHm1tr7/21aD7fSkqgD/CVJBCo= -k8s.io/api v0.31.0/go.mod h1:0YiFF+JfFxMM6+1hQei8FY8M7s1Mth+z/q7eF1aJkTE= -k8s.io/apiextensions-apiserver v0.30.3/go.mod h1:uhXxYDkMAvl6CJw4lrDN4CPbONkF3+XL9cacCT44kV4= -k8s.io/apimachinery v0.31.0 h1:m9jOiSr3FoSSL5WO9bjm1n6B9KROYYgNZOb4tyZ1lBc= -k8s.io/apimachinery v0.31.0/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= -k8s.io/client-go v0.31.0 h1:QqEJzNjbN2Yv1H79SsS+SWnXkBgVu4Pj3CJQgbx0gI8= -k8s.io/client-go v0.31.0/go.mod h1:Y9wvC76g4fLjmU0BA+rV+h2cncoadjvjjkkIGoTLcGU= -k8s.io/code-generator v0.30.3/go.mod h1:PFgBiv+miFV7TZYp+RXgROkhA+sWYZ+mtpbMLofMke8= -k8s.io/gengo v0.0.0-20240404160639-a0386bf69313/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= -k8s.io/gengo/v2 v2.0.0-20240228010128-51d4e06bde70/go.mod h1:VH3AT8AaQOqiGjMF9p0/IM1Dj+82ZwjfxUP1IxaHE+8= -k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= +k8s.io/api v0.31.4 h1:I2QNzitPVsPeLQvexMEsj945QumYraqv9m74isPDKhM= +k8s.io/api v0.31.4/go.mod h1:d+7vgXLvmcdT1BCo79VEgJxHHryww3V5np2OYTr6jdw= +k8s.io/apimachinery v0.31.4 h1:8xjE2C4CzhYVm9DGf60yohpNUh5AEBnPxCryPBECmlM= +k8s.io/apimachinery v0.31.4/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= +k8s.io/client-go v0.31.4 h1:t4QEXt4jgHIkKKlx06+W3+1JOwAFU/2OPiOo7H92eRQ= +k8s.io/client-go v0.31.4/go.mod h1:kvuMro4sFYIa8sulL5Gi5GFqUPvfH2O/dXuKstbaaeg= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kube-openapi v0.0.0-20240808142205-8e686545bdb8 h1:1Wof1cGQgA5pqgo8MxKPtf+qN6Sh/0JzznmeGPm1HnE= -k8s.io/kube-openapi v0.0.0-20240808142205-8e686545bdb8/go.mod h1:Os6V6dZwLNii3vxFpxcNaTmH8LJJBkOTg1N0tOA0fvA= -k8s.io/utils v0.0.0-20240821151609-f90d01438635 h1:2wThSvJoW/Ncn9TmQEYXRnevZXi2duqHWf5OX9S3zjI= -k8s.io/utils v0.0.0-20240821151609-f90d01438635/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -knative.dev/caching v0.0.0-20240716132144-989f54c83776/go.mod h1:Uj74eO9rLiK1eb8wmDBED1hJBZQ7MJ9cvq/d8Ktsm3c= -knative.dev/hack v0.0.0-20240814130635-06f7aff93954/go.mod h1:R0ritgYtjLDO9527h5vb5X6gfvt5LCrJ55BNbVDsWiY= -knative.dev/networking v0.0.0-20240815142417-37fdbdd0854b h1:ws/Jeho6on84+5tfNKLAKriVVGIwivHbgPEtZjBfcs0= -knative.dev/networking v0.0.0-20240815142417-37fdbdd0854b/go.mod h1:2eMQVGLBZ5Kj1C4kKPuPhO7BsUeF6fkmhZFDQPIP+88= -knative.dev/pkg v0.0.0-20240815051656-89743d9bbf7c h1:2crXVk4FG0dSG6WHaIT+WKbUzn7qG2wn0AfYmvA22zs= -knative.dev/pkg v0.0.0-20240815051656-89743d9bbf7c/go.mod h1:cI2RPEEHZk+/dBpfHobs0aBdPA1mMZVUVWnGAc8NSzM= -knative.dev/serving v0.42.2 h1:yKieg3MeNvpVz+4JJPbvmpee3v3LK3zO5h5HJBtzaNk= -knative.dev/serving v0.42.2/go.mod h1:3cgU8/864RcqA0ZPrc3jFcmS3uJL/mOlUZiYsXonwaE= +k8s.io/kube-openapi v0.0.0-20240903163716-9e1beecbcb38 h1:1dWzkmJrrprYvjGwh9kEUxmcUV/CtNU8QM7h1FLWQOo= +k8s.io/kube-openapi v0.0.0-20240903163716-9e1beecbcb38/go.mod h1:coRQXBK9NxO98XUv3ZD6AK3xzHCxV6+b7lrquKwaKzA= +k8s.io/utils v0.0.0-20240921022957-49e7df575cb6 h1:MDF6h2H/h4tbzmtIKTuctcwZmY0tY9mD9fNT47QO6HI= +k8s.io/utils v0.0.0-20240921022957-49e7df575cb6/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +knative.dev/networking v0.0.0-20250117155906-67d1c274ba6a h1:FaDPXtv42+AkYh/mE269pttPSZ3fDVAjJiEsYUaM4SM= +knative.dev/networking v0.0.0-20250117155906-67d1c274ba6a/go.mod h1:AIKYMfZydhwXR/60c/3KXEnqEnH6aNEEqulifdqJVcQ= +knative.dev/pkg v0.0.0-20250117084104-c43477f0052b h1:a+gP7Yzu5NmoX2w1p8nfTgmSKF+aHLKGzqYT82ijJTw= +knative.dev/pkg v0.0.0-20250117084104-c43477f0052b/go.mod h1:bedSpkdLybR6JhL1J7XDLpd+JMKM/x8M5Apr80i5TeE= +knative.dev/serving v0.44.0 h1:c6TXhoSAI6eXt0/1ET3C69jMWYA4ES9FskSan/fBaac= +knative.dev/serving v0.44.0/go.mod h1:9bFONngDZtkdYZkP5ko9LDS9ZelnFY9SaPoHKG0vFxs= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/gateway-api v0.8.0/go.mod h1:okOnjPNBFbIS/Rw9kAhuIUaIkLhTKEu+ARIuXk2dgaM= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= diff --git a/qpext/qpext.Dockerfile b/qpext/qpext.Dockerfile index b7e9ceab2b2..29c07386c1d 100644 --- a/qpext/qpext.Dockerfile +++ b/qpext/qpext.Dockerfile @@ -1,5 +1,5 @@ # Build the inference qpext binary -FROM golang:1.22 AS builder +FROM golang:1.23 AS builder # Copy in the go src WORKDIR /go/src/github.com/kserve/kserve/qpext diff --git a/release/RELEASE_PROCESS_v2.md b/release/RELEASE_PROCESS_v2.md index 4ef37a814a5..5199de15b0a 100644 --- a/release/RELEASE_PROCESS_v2.md +++ b/release/RELEASE_PROCESS_v2.md @@ -45,7 +45,7 @@ Create a branch from the master and do the following: 3. [Chart.yaml in kserve-crd](../charts/kserve-crd/Chart.yaml#L3) to `v${MAJOR}.${MINOR}.${PATCH}-rc${RELEASE_CANDIDATE_VERSION}` 4. [Chart.yaml in kserve-crd-minimal](../charts/kserve-crd-minimal/Chart.yaml#L3) to `v${MAJOR}.${MINOR}.${PATCH}-rc${RELEASE_CANDIDATE_VERSION}` 5. [Chart.yaml in kserve-resources](../charts/kserve-resources/Chart.yaml#L3) to `v${MAJOR}.${MINOR}.${PATCH}-rc${RELEASE_CANDIDATE_VERSION}` - 6. [values.yaml in kserve-resources](../charts/kserve-resources/values.yaml#L2) to `v${MAJOR}.${MINOR}.${PATCH}-rc${RELEASE_CANDIDATE_VERSION}`. In addition, bump `modelmeshVersion` if it a newer version of ModelMesh is available. + 6. [values.yaml in kserve-resources](../charts/kserve-resources/values.yaml#L2) to `v${MAJOR}.${MINOR}.${PATCH}-rc${RELEASE_CANDIDATE_VERSION}`. 7. Run `pre-commit run` to update the Helm chart documentation 8. The steps are automated in the script [prepare-for-release.sh](../hack/prepare-for-release.sh) 9. To use it execute: `make bump-version NEW_VERSION=0.14.0-rc2 PRIOR_VERSION=0.14.0-rc1` diff --git a/router.Dockerfile b/router.Dockerfile index 05995f53095..f3bbb9bd6f1 100644 --- a/router.Dockerfile +++ b/router.Dockerfile @@ -1,5 +1,5 @@ # Build the inference-router binary -FROM golang:1.22 AS builder +FROM golang:1.23 AS builder # Copy in the go src WORKDIR /go/src/github.com/kserve/kserve diff --git a/test/crds/gatewayapi_httproute.yaml b/test/crds/gatewayapi_httproute.yaml new file mode 100644 index 00000000000..bbecbc442fc --- /dev/null +++ b/test/crds/gatewayapi_httproute.yaml @@ -0,0 +1,5535 @@ +# +# Gateway API v1.2.1 Standard channel install +# + +# +# https://github.com/kubernetes-sigs/gateway-api/blob/v1.2.1/config/crd/standard/gateway.networking.k8s.io_httproutes.yaml +# +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 + gateway.networking.k8s.io/bundle-version: v1.2.1 + gateway.networking.k8s.io/channel: standard + creationTimestamp: null + name: httproutes.gateway.networking.k8s.io +spec: + group: gateway.networking.k8s.io + names: + categories: + - gateway-api + kind: HTTPRoute + listKind: HTTPRouteList + plural: httproutes + singular: httproute + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.hostnames + name: Hostnames + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1 + schema: + openAPIV3Schema: + description: |- + HTTPRoute provides a way to route HTTP requests. This includes the capability + to match requests by hostname, path, header, or query param. Filters can be + used to specify additional processing steps. Backends specify where matching + requests should be routed. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of HTTPRoute. + properties: + hostnames: + description: |- + Hostnames defines a set of hostnames that should match against the HTTP Host + header to select a HTTPRoute used to process the request. Implementations + MUST ignore any port value specified in the HTTP Host header while + performing a match and (absent of any applicable header modification + configuration) MUST forward this header unmodified to the backend. + + Valid values for Hostnames are determined by RFC 1123 definition of a + hostname with 2 notable exceptions: + + 1. IPs are not allowed. + 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard + label must appear by itself as the first label. + + If a hostname is specified by both the Listener and HTTPRoute, there + must be at least one intersecting hostname for the HTTPRoute to be + attached to the Listener. For example: + + * A Listener with `test.example.com` as the hostname matches HTTPRoutes + that have either not specified any hostnames, or have specified at + least one of `test.example.com` or `*.example.com`. + * A Listener with `*.example.com` as the hostname matches HTTPRoutes + that have either not specified any hostnames or have specified at least + one hostname that matches the Listener hostname. For example, + `*.example.com`, `test.example.com`, and `foo.test.example.com` would + all match. On the other hand, `example.com` and `test.example.net` would + not match. + + Hostnames that are prefixed with a wildcard label (`*.`) are interpreted + as a suffix match. That means that a match for `*.example.com` would match + both `test.example.com`, and `foo.test.example.com`, but not `example.com`. + + If both the Listener and HTTPRoute have specified hostnames, any + HTTPRoute hostnames that do not match the Listener hostname MUST be + ignored. For example, if a Listener specified `*.example.com`, and the + HTTPRoute specified `test.example.com` and `test.example.net`, + `test.example.net` must not be considered for a match. + + If both the Listener and HTTPRoute have specified hostnames, and none + match with the criteria above, then the HTTPRoute is not accepted. The + implementation must raise an 'Accepted' Condition with a status of + `False` in the corresponding RouteParentStatus. + + In the event that multiple HTTPRoutes specify intersecting hostnames (e.g. + overlapping wildcard matching and exact matching hostnames), precedence must + be given to rules from the HTTPRoute with the largest number of: + + * Characters in a matching non-wildcard hostname. + * Characters in a matching hostname. + + If ties exist across multiple Routes, the matching precedence rules for + HTTPRouteMatches takes over. + + Support: Core + items: + description: |- + Hostname is the fully qualified domain name of a network host. This matches + the RFC 1123 definition of a hostname with 2 notable exceptions: + + 1. IPs are not allowed. + 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard + label must appear by itself as the first label. + + Hostname can be "precise" which is a domain name without the terminating + dot of a network host (e.g. "foo.example.com") or "wildcard", which is a + domain name prefixed with a single wildcard label (e.g. `*.example.com`). + + Note that as per RFC1035 and RFC1123, a *label* must consist of lower case + alphanumeric characters or '-', and must start and end with an alphanumeric + character. No other punctuation is allowed. + maxLength: 253 + minLength: 1 + pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + maxItems: 16 + type: array + parentRefs: + description: |+ + ParentRefs references the resources (usually Gateways) that a Route wants + to be attached to. Note that the referenced parent resource needs to + allow this for the attachment to be complete. For Gateways, that means + the Gateway needs to allow attachment from Routes of this kind and + namespace. For Services, that means the Service must either be in the same + namespace for a "producer" route, or the mesh implementation must support + and allow "consumer" routes for the referenced Service. ReferenceGrant is + not applicable for governing ParentRefs to Services - it is not possible to + create a "producer" route for a Service in a different namespace from the + Route. + + There are two kinds of parent resources with "Core" support: + + * Gateway (Gateway conformance profile) + * Service (Mesh conformance profile, ClusterIP Services only) + + This API may be extended in the future to support additional kinds of parent + resources. + + ParentRefs must be _distinct_. This means either that: + + * They select different objects. If this is the case, then parentRef + entries are distinct. In terms of fields, this means that the + multi-part key defined by `group`, `kind`, `namespace`, and `name` must + be unique across all parentRef entries in the Route. + * They do not select different objects, but for each optional field used, + each ParentRef that selects the same object must set the same set of + optional fields to different values. If one ParentRef sets a + combination of optional fields, all must set the same combination. + + Some examples: + + * If one ParentRef sets `sectionName`, all ParentRefs referencing the + same object must also set `sectionName`. + * If one ParentRef sets `port`, all ParentRefs referencing the same + object must also set `port`. + * If one ParentRef sets `sectionName` and `port`, all ParentRefs + referencing the same object must also set `sectionName` and `port`. + + It is possible to separately reference multiple distinct objects that may + be collapsed by an implementation. For example, some implementations may + choose to merge compatible Gateway Listeners together. If that is the + case, the list of routes attached to those resources should also be + merged. + + Note that for ParentRefs that cross namespace boundaries, there are specific + rules. Cross-namespace references are only valid if they are explicitly + allowed by something in the namespace they are referring to. For example, + Gateway has the AllowedRoutes field, and ReferenceGrant provides a + generic way to enable other kinds of cross-namespace reference. + + + + + + + items: + description: |- + ParentReference identifies an API object (usually a Gateway) that can be considered + a parent of this resource (usually a route). There are two kinds of parent resources + with "Core" support: + + * Gateway (Gateway conformance profile) + * Service (Mesh conformance profile, ClusterIP Services only) + + This API may be extended in the future to support additional kinds of parent + resources. + + The API object must be valid in the cluster; the Group and Kind must + be registered in the cluster for this reference to be valid. + properties: + group: + default: gateway.networking.k8s.io + description: |- + Group is the group of the referent. + When unspecified, "gateway.networking.k8s.io" is inferred. + To set the core API group (such as for a "Service" kind referent), + Group must be explicitly set to "" (empty string). + + Support: Core + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: |- + Kind is kind of the referent. + + There are two kinds of parent resources with "Core" support: + + * Gateway (Gateway conformance profile) + * Service (Mesh conformance profile, ClusterIP Services only) + + Support for other resources is Implementation-Specific. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: |- + Name is the name of the referent. + + Support: Core + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the referent. When unspecified, this refers + to the local namespace of the Route. + + Note that there are specific rules for ParentRefs which cross namespace + boundaries. Cross-namespace references are only valid if they are explicitly + allowed by something in the namespace they are referring to. For example: + Gateway has the AllowedRoutes field, and ReferenceGrant provides a + generic way to enable any other kind of cross-namespace reference. + + + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port is the network port this Route targets. It can be interpreted + differently based on the type of parent resource. + + When the parent resource is a Gateway, this targets all listeners + listening on the specified port that also support this kind of Route(and + select this Route). It's not recommended to set `Port` unless the + networking behaviors specified in a Route must apply to a specific port + as opposed to a listener(s) whose port(s) may be changed. When both Port + and SectionName are specified, the name and port of the selected listener + must match both specified values. + + + + Implementations MAY choose to support other parent resources. + Implementations supporting other types of parent resources MUST clearly + document how/if Port is interpreted. + + For the purpose of status, an attachment is considered successful as + long as the parent resource accepts it partially. For example, Gateway + listeners can restrict which Routes can attach to them by Route kind, + namespace, or hostname. If 1 of 2 Gateway listeners accept attachment + from the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this Route, + the Route MUST be considered detached from the Gateway. + + Support: Extended + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: |- + SectionName is the name of a section within the target resource. In the + following resources, SectionName is interpreted as the following: + + * Gateway: Listener name. When both Port (experimental) and SectionName + are specified, the name and port of the selected listener must match + both specified values. + * Service: Port name. When both Port (experimental) and SectionName + are specified, the name and port of the selected listener must match + both specified values. + + Implementations MAY choose to support attaching Routes to other resources. + If that is the case, they MUST clearly document how SectionName is + interpreted. + + When unspecified (empty string), this will reference the entire resource. + For the purpose of status, an attachment is considered successful if at + least one section in the parent resource accepts it. For example, Gateway + listeners can restrict which Routes can attach to them by Route kind, + namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from + the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this Route, the + Route MUST be considered detached from the Gateway. + + Support: Core + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + maxItems: 32 + type: array + x-kubernetes-validations: + - message: sectionName must be specified when parentRefs includes + 2 or more references to the same parent + rule: 'self.all(p1, self.all(p2, p1.group == p2.group && p1.kind + == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) + || p1.__namespace__ == '''') && (!has(p2.__namespace__) || p2.__namespace__ + == '''')) || (has(p1.__namespace__) && has(p2.__namespace__) && + p1.__namespace__ == p2.__namespace__ )) ? ((!has(p1.sectionName) + || p1.sectionName == '''') == (!has(p2.sectionName) || p2.sectionName + == '''')) : true))' + - message: sectionName must be unique when parentRefs includes 2 or + more references to the same parent + rule: self.all(p1, self.exists_one(p2, p1.group == p2.group && p1.kind + == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) + || p1.__namespace__ == '') && (!has(p2.__namespace__) || p2.__namespace__ + == '')) || (has(p1.__namespace__) && has(p2.__namespace__) && + p1.__namespace__ == p2.__namespace__ )) && (((!has(p1.sectionName) + || p1.sectionName == '') && (!has(p2.sectionName) || p2.sectionName + == '')) || (has(p1.sectionName) && has(p2.sectionName) && p1.sectionName + == p2.sectionName)))) + rules: + default: + - matches: + - path: + type: PathPrefix + value: / + description: |+ + Rules are a list of HTTP matchers, filters and actions. + + items: + description: |- + HTTPRouteRule defines semantics for matching an HTTP request based on + conditions (matches), processing it (filters), and forwarding the request to + an API object (backendRefs). + properties: + backendRefs: + description: |- + BackendRefs defines the backend(s) where matching requests should be + sent. + + Failure behavior here depends on how many BackendRefs are specified and + how many are invalid. + + If *all* entries in BackendRefs are invalid, and there are also no filters + specified in this route rule, *all* traffic which matches this rule MUST + receive a 500 status code. + + See the HTTPBackendRef definition for the rules about what makes a single + HTTPBackendRef invalid. + + When a HTTPBackendRef is invalid, 500 status codes MUST be returned for + requests that would have otherwise been routed to an invalid backend. If + multiple backends are specified, and some are invalid, the proportion of + requests that would otherwise have been routed to an invalid backend + MUST receive a 500 status code. + + For example, if two backends are specified with equal weights, and one is + invalid, 50 percent of traffic must receive a 500. Implementations may + choose how that 50 percent is determined. + + When a HTTPBackendRef refers to a Service that has no ready endpoints, + implementations SHOULD return a 503 for requests to that backend instead. + If an implementation chooses to do this, all of the above rules for 500 responses + MUST also apply for responses that return a 503. + + Support: Core for Kubernetes Service + + Support: Extended for Kubernetes ServiceImport + + Support: Implementation-specific for any other resource + + Support for weight: Core + items: + description: |- + HTTPBackendRef defines how a HTTPRoute forwards a HTTP request. + + Note that when a namespace different than the local namespace is specified, a + ReferenceGrant object is required in the referent namespace to allow that + namespace's owner to accept the reference. See the ReferenceGrant + documentation for details. + + + + When the BackendRef points to a Kubernetes Service, implementations SHOULD + honor the appProtocol field if it is set for the target Service Port. + + Implementations supporting appProtocol SHOULD recognize the Kubernetes + Standard Application Protocols defined in KEP-3726. + + If a Service appProtocol isn't specified, an implementation MAY infer the + backend protocol through its own means. Implementations MAY infer the + protocol from the Route type referring to the backend Service. + + If a Route is not able to send traffic to the backend using the specified + protocol then the backend is considered invalid. Implementations MUST set the + "ResolvedRefs" condition to "False" with the "UnsupportedProtocol" reason. + + + properties: + filters: + description: |- + Filters defined at this level should be executed if and only if the + request is being forwarded to the backend defined here. + + Support: Implementation-specific (For broader support of filters, use the + Filters field in HTTPRouteRule.) + items: + description: |- + HTTPRouteFilter defines processing steps that must be completed during the + request or response lifecycle. HTTPRouteFilters are meant as an extension + point to express processing that may be done in Gateway implementations. Some + examples include request or response modification, implementing + authentication strategies, rate-limiting, and traffic shaping. API + guarantee/conformance is defined based on the type of the filter. + properties: + extensionRef: + description: |- + ExtensionRef is an optional, implementation-specific extension to the + "filter" behavior. For example, resource "myroutefilter" in group + "networking.example.net"). ExtensionRef MUST NOT be used for core and + extended filters. + + This filter can be used multiple times within the same rule. + + Support: Implementation-specific + properties: + group: + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is kind of the referent. For + example "HTTPRoute" or "Service". + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + - name + type: object + requestHeaderModifier: + description: |- + RequestHeaderModifier defines a schema for a filter that modifies request + headers. + + Support: Core + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. + + Input: + GET /foo HTTP/1.1 + my-header: foo + + Config: + add: + - name: "my-header" + value: "bar,baz" + + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP + Header name and value as defined by RFC + 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP + Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz + + Config: + remove: ["my-header1", "my-header3"] + + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. + + Input: + GET /foo HTTP/1.1 + my-header: foo + + Config: + set: + - name: "my-header" + value: "bar" + + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP + Header name and value as defined by RFC + 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP + Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: + description: |+ + RequestMirror defines a schema for a filter that mirrors requests. + Requests are sent to the specified destination, but responses from + that destination are ignored. + + This filter can be used multiple times within the same rule. Note that + not all implementations will be able to support mirroring to multiple + backends. + + Support: Extended + + properties: + backendRef: + description: |- + BackendRef references a resource where mirrored requests are sent. + + Mirrored requests must be sent only to a single destination endpoint + within this BackendRef, irrespective of how many endpoints are present + within this BackendRef. + + If the referent cannot be found, this BackendRef is invalid and must be + dropped from the Gateway. The controller must ensure the "ResolvedRefs" + condition on the Route status is set to `status: False` and not configure + this backend in the underlying implementation. + + If there is a cross-namespace reference to an *existing* object + that is not allowed by a ReferenceGrant, the controller must ensure the + "ResolvedRefs" condition on the Route is set to `status: False`, + with the "RefNotPermitted" reason and not configure this backend in the + underlying implementation. + + In either error case, the Message of the `ResolvedRefs` Condition + should be used to provide more detail about the problem. + + Support: Extended for Kubernetes Service + + Support: Implementation-specific for any other resource + properties: + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Service + description: |- + Kind is the Kubernetes resource kind of the referent. For example + "Service". + + Defaults to "Service" when not specified. + + ExternalName services can refer to CNAME DNS records that may live + outside of the cluster and as such are difficult to reason about in + terms of conformance. They also may not be safe to forward to (see + CVE-2021-25740 for more information). Implementations SHOULD NOT + support ExternalName Services. + + Support: Core (Services with a type other than ExternalName) + + Support: Implementation-specific (Services with type ExternalName) + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the backend. When unspecified, the local + namespace is inferred. + + Note that when a namespace different than the local namespace is specified, + a ReferenceGrant object is required in the referent namespace to allow that + namespace's owner to accept the reference. See the ReferenceGrant + documentation for details. + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port specifies the destination port number to use for this resource. + Port is required when the referent is a Kubernetes Service. In this + case, the port number is the service port number, not the target port. + For other resources, destination port might be derived from the referent + resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + x-kubernetes-validations: + - message: Must have port for Service reference + rule: '(size(self.group) == 0 && self.kind + == ''Service'') ? has(self.port) : true' + required: + - backendRef + type: object + requestRedirect: + description: |- + RequestRedirect defines a schema for a filter that responds to the + request with an HTTP redirection. + + Support: Core + properties: + hostname: + description: |- + Hostname is the hostname to be used in the value of the `Location` + header in the response. + When empty, the hostname in the `Host` header of the request is used. + + Support: Core + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + path: + description: |- + Path defines parameters used to modify the path of the incoming request. + The modified path is then used to construct the `Location` header. When + empty, the request path is used as-is. + + Support: Extended + properties: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". + + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. + + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. + + Request Path | Prefix Match | Replace Prefix | Modified Path + maxLength: 1024 + type: string + type: + description: |- + Type defines the type of path modifier. Additional types may be + added in a future release of the API. + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + x-kubernetes-validations: + - message: replaceFullPath must be specified + when type is set to 'ReplaceFullPath' + rule: 'self.type == ''ReplaceFullPath'' ? + has(self.replaceFullPath) : true' + - message: type must be 'ReplaceFullPath' when + replaceFullPath is set + rule: 'has(self.replaceFullPath) ? self.type + == ''ReplaceFullPath'' : true' + - message: replacePrefixMatch must be specified + when type is set to 'ReplacePrefixMatch' + rule: 'self.type == ''ReplacePrefixMatch'' + ? has(self.replacePrefixMatch) : true' + - message: type must be 'ReplacePrefixMatch' + when replacePrefixMatch is set + rule: 'has(self.replacePrefixMatch) ? self.type + == ''ReplacePrefixMatch'' : true' + port: + description: |- + Port is the port to be used in the value of the `Location` + header in the response. + + If no port is specified, the redirect port MUST be derived using the + following rules: + + * If redirect scheme is not-empty, the redirect port MUST be the well-known + port associated with the redirect scheme. Specifically "http" to port 80 + and "https" to port 443. If the redirect scheme does not have a + well-known port, the listener port of the Gateway SHOULD be used. + * If redirect scheme is empty, the redirect port MUST be the Gateway + Listener port. + + Implementations SHOULD NOT add the port number in the 'Location' + header in the following cases: + + * A Location header that will use HTTP (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 80. + * A Location header that will use HTTPS (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 443. + + Support: Extended + format: int32 + maximum: 65535 + minimum: 1 + type: integer + scheme: + description: |- + Scheme is the scheme to be used in the value of the `Location` header in + the response. When empty, the scheme of the request is used. + + Scheme redirects can affect the port of the redirect, for more information, + refer to the documentation for the port field of this filter. + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + + Support: Extended + enum: + - http + - https + type: string + statusCode: + default: 302 + description: |- + StatusCode is the HTTP status code to be used in response. + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + + Support: Core + enum: + - 301 + - 302 + type: integer + type: object + responseHeaderModifier: + description: |- + ResponseHeaderModifier defines a schema for a filter that modifies response + headers. + + Support: Extended + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. + + Input: + GET /foo HTTP/1.1 + my-header: foo + + Config: + add: + - name: "my-header" + value: "bar,baz" + + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP + Header name and value as defined by RFC + 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP + Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz + + Config: + remove: ["my-header1", "my-header3"] + + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. + + Input: + GET /foo HTTP/1.1 + my-header: foo + + Config: + set: + - name: "my-header" + value: "bar" + + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP + Header name and value as defined by RFC + 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP + Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: + description: |- + Type identifies the type of filter to apply. As with other API fields, + types are classified into three conformance levels: + + - Core: Filter types and their corresponding configuration defined by + "Support: Core" in this package, e.g. "RequestHeaderModifier". All + implementations must support core filters. + + - Extended: Filter types and their corresponding configuration defined by + "Support: Extended" in this package, e.g. "RequestMirror". Implementers + are encouraged to support extended filters. + + - Implementation-specific: Filters that are defined and supported by + specific vendors. + In the future, filters showing convergence in behavior across multiple + implementations will be considered for inclusion in extended or core + conformance levels. Filter-specific configuration for such filters + is specified using the ExtensionRef field. `Type` should be set to + "ExtensionRef" for custom filters. + + Implementers are encouraged to define custom implementation types to + extend the core API with implementation-specific behavior. + + If a reference to a custom filter type cannot be resolved, the filter + MUST NOT be skipped. Instead, requests that would have been processed by + that filter MUST receive a HTTP error response. + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestMirror + - RequestRedirect + - URLRewrite + - ExtensionRef + type: string + urlRewrite: + description: |- + URLRewrite defines a schema for a filter that modifies a request during forwarding. + + Support: Extended + properties: + hostname: + description: |- + Hostname is the value to be used to replace the Host header value during + forwarding. + + Support: Extended + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + path: + description: |- + Path defines a path rewrite. + + Support: Extended + properties: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". + + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. + + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. + + Request Path | Prefix Match | Replace Prefix | Modified Path + maxLength: 1024 + type: string + type: + description: |- + Type defines the type of path modifier. Additional types may be + added in a future release of the API. + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + x-kubernetes-validations: + - message: replaceFullPath must be specified + when type is set to 'ReplaceFullPath' + rule: 'self.type == ''ReplaceFullPath'' ? + has(self.replaceFullPath) : true' + - message: type must be 'ReplaceFullPath' when + replaceFullPath is set + rule: 'has(self.replaceFullPath) ? self.type + == ''ReplaceFullPath'' : true' + - message: replacePrefixMatch must be specified + when type is set to 'ReplacePrefixMatch' + rule: 'self.type == ''ReplacePrefixMatch'' + ? has(self.replacePrefixMatch) : true' + - message: type must be 'ReplacePrefixMatch' + when replacePrefixMatch is set + rule: 'has(self.replacePrefixMatch) ? self.type + == ''ReplacePrefixMatch'' : true' + type: object + required: + - type + type: object + x-kubernetes-validations: + - message: filter.requestHeaderModifier must be nil + if the filter.type is not RequestHeaderModifier + rule: '!(has(self.requestHeaderModifier) && self.type + != ''RequestHeaderModifier'')' + - message: filter.requestHeaderModifier must be specified + for RequestHeaderModifier filter.type + rule: '!(!has(self.requestHeaderModifier) && self.type + == ''RequestHeaderModifier'')' + - message: filter.responseHeaderModifier must be nil + if the filter.type is not ResponseHeaderModifier + rule: '!(has(self.responseHeaderModifier) && self.type + != ''ResponseHeaderModifier'')' + - message: filter.responseHeaderModifier must be specified + for ResponseHeaderModifier filter.type + rule: '!(!has(self.responseHeaderModifier) && self.type + == ''ResponseHeaderModifier'')' + - message: filter.requestMirror must be nil if the filter.type + is not RequestMirror + rule: '!(has(self.requestMirror) && self.type != ''RequestMirror'')' + - message: filter.requestMirror must be specified for + RequestMirror filter.type + rule: '!(!has(self.requestMirror) && self.type == + ''RequestMirror'')' + - message: filter.requestRedirect must be nil if the + filter.type is not RequestRedirect + rule: '!(has(self.requestRedirect) && self.type != + ''RequestRedirect'')' + - message: filter.requestRedirect must be specified + for RequestRedirect filter.type + rule: '!(!has(self.requestRedirect) && self.type == + ''RequestRedirect'')' + - message: filter.urlRewrite must be nil if the filter.type + is not URLRewrite + rule: '!(has(self.urlRewrite) && self.type != ''URLRewrite'')' + - message: filter.urlRewrite must be specified for URLRewrite + filter.type + rule: '!(!has(self.urlRewrite) && self.type == ''URLRewrite'')' + - message: filter.extensionRef must be nil if the filter.type + is not ExtensionRef + rule: '!(has(self.extensionRef) && self.type != ''ExtensionRef'')' + - message: filter.extensionRef must be specified for + ExtensionRef filter.type + rule: '!(!has(self.extensionRef) && self.type == ''ExtensionRef'')' + maxItems: 16 + type: array + x-kubernetes-validations: + - message: May specify either httpRouteFilterRequestRedirect + or httpRouteFilterRequestRewrite, but not both + rule: '!(self.exists(f, f.type == ''RequestRedirect'') + && self.exists(f, f.type == ''URLRewrite''))' + - message: May specify either httpRouteFilterRequestRedirect + or httpRouteFilterRequestRewrite, but not both + rule: '!(self.exists(f, f.type == ''RequestRedirect'') + && self.exists(f, f.type == ''URLRewrite''))' + - message: RequestHeaderModifier filter cannot be repeated + rule: self.filter(f, f.type == 'RequestHeaderModifier').size() + <= 1 + - message: ResponseHeaderModifier filter cannot be repeated + rule: self.filter(f, f.type == 'ResponseHeaderModifier').size() + <= 1 + - message: RequestRedirect filter cannot be repeated + rule: self.filter(f, f.type == 'RequestRedirect').size() + <= 1 + - message: URLRewrite filter cannot be repeated + rule: self.filter(f, f.type == 'URLRewrite').size() + <= 1 + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Service + description: |- + Kind is the Kubernetes resource kind of the referent. For example + "Service". + + Defaults to "Service" when not specified. + + ExternalName services can refer to CNAME DNS records that may live + outside of the cluster and as such are difficult to reason about in + terms of conformance. They also may not be safe to forward to (see + CVE-2021-25740 for more information). Implementations SHOULD NOT + support ExternalName Services. + + Support: Core (Services with a type other than ExternalName) + + Support: Implementation-specific (Services with type ExternalName) + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the backend. When unspecified, the local + namespace is inferred. + + Note that when a namespace different than the local namespace is specified, + a ReferenceGrant object is required in the referent namespace to allow that + namespace's owner to accept the reference. See the ReferenceGrant + documentation for details. + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port specifies the destination port number to use for this resource. + Port is required when the referent is a Kubernetes Service. In this + case, the port number is the service port number, not the target port. + For other resources, destination port might be derived from the referent + resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + weight: + default: 1 + description: |- + Weight specifies the proportion of requests forwarded to the referenced + backend. This is computed as weight/(sum of all weights in this + BackendRefs list). For non-zero values, there may be some epsilon from + the exact proportion defined here depending on the precision an + implementation supports. Weight is not a percentage and the sum of + weights does not need to equal 100. + + If only one backend is specified and it has a weight greater than 0, 100% + of the traffic is forwarded to that backend. If weight is set to 0, no + traffic should be forwarded for this entry. If unspecified, weight + defaults to 1. + + Support for this field varies based on the context where used. + format: int32 + maximum: 1000000 + minimum: 0 + type: integer + required: + - name + type: object + x-kubernetes-validations: + - message: Must have port for Service reference + rule: '(size(self.group) == 0 && self.kind == ''Service'') + ? has(self.port) : true' + maxItems: 16 + type: array + filters: + description: |- + Filters define the filters that are applied to requests that match + this rule. + + Wherever possible, implementations SHOULD implement filters in the order + they are specified. + + Implementations MAY choose to implement this ordering strictly, rejecting + any combination or order of filters that can not be supported. If implementations + choose a strict interpretation of filter ordering, they MUST clearly document + that behavior. + + To reject an invalid combination or order of filters, implementations SHOULD + consider the Route Rules with this configuration invalid. If all Route Rules + in a Route are invalid, the entire Route would be considered invalid. If only + a portion of Route Rules are invalid, implementations MUST set the + "PartiallyInvalid" condition for the Route. + + Conformance-levels at this level are defined based on the type of filter: + + - ALL core filters MUST be supported by all implementations. + - Implementers are encouraged to support extended filters. + - Implementation-specific custom filters have no API guarantees across + implementations. + + Specifying the same filter multiple times is not supported unless explicitly + indicated in the filter. + + All filters are expected to be compatible with each other except for the + URLRewrite and RequestRedirect filters, which may not be combined. If an + implementation can not support other combinations of filters, they must clearly + document that limitation. In cases where incompatible or unsupported + filters are specified and cause the `Accepted` condition to be set to status + `False`, implementations may use the `IncompatibleFilters` reason to specify + this configuration error. + + Support: Core + items: + description: |- + HTTPRouteFilter defines processing steps that must be completed during the + request or response lifecycle. HTTPRouteFilters are meant as an extension + point to express processing that may be done in Gateway implementations. Some + examples include request or response modification, implementing + authentication strategies, rate-limiting, and traffic shaping. API + guarantee/conformance is defined based on the type of the filter. + properties: + extensionRef: + description: |- + ExtensionRef is an optional, implementation-specific extension to the + "filter" behavior. For example, resource "myroutefilter" in group + "networking.example.net"). ExtensionRef MUST NOT be used for core and + extended filters. + + This filter can be used multiple times within the same rule. + + Support: Implementation-specific + properties: + group: + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is kind of the referent. For example + "HTTPRoute" or "Service". + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + - name + type: object + requestHeaderModifier: + description: |- + RequestHeaderModifier defines a schema for a filter that modifies request + headers. + + Support: Core + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. + + Input: + GET /foo HTTP/1.1 + my-header: foo + + Config: + add: + - name: "my-header" + value: "bar,baz" + + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP Header + name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header + to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz + + Config: + remove: ["my-header1", "my-header3"] + + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. + + Input: + GET /foo HTTP/1.1 + my-header: foo + + Config: + set: + - name: "my-header" + value: "bar" + + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP Header + name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header + to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: + description: |+ + RequestMirror defines a schema for a filter that mirrors requests. + Requests are sent to the specified destination, but responses from + that destination are ignored. + + This filter can be used multiple times within the same rule. Note that + not all implementations will be able to support mirroring to multiple + backends. + + Support: Extended + + properties: + backendRef: + description: |- + BackendRef references a resource where mirrored requests are sent. + + Mirrored requests must be sent only to a single destination endpoint + within this BackendRef, irrespective of how many endpoints are present + within this BackendRef. + + If the referent cannot be found, this BackendRef is invalid and must be + dropped from the Gateway. The controller must ensure the "ResolvedRefs" + condition on the Route status is set to `status: False` and not configure + this backend in the underlying implementation. + + If there is a cross-namespace reference to an *existing* object + that is not allowed by a ReferenceGrant, the controller must ensure the + "ResolvedRefs" condition on the Route is set to `status: False`, + with the "RefNotPermitted" reason and not configure this backend in the + underlying implementation. + + In either error case, the Message of the `ResolvedRefs` Condition + should be used to provide more detail about the problem. + + Support: Extended for Kubernetes Service + + Support: Implementation-specific for any other resource + properties: + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Service + description: |- + Kind is the Kubernetes resource kind of the referent. For example + "Service". + + Defaults to "Service" when not specified. + + ExternalName services can refer to CNAME DNS records that may live + outside of the cluster and as such are difficult to reason about in + terms of conformance. They also may not be safe to forward to (see + CVE-2021-25740 for more information). Implementations SHOULD NOT + support ExternalName Services. + + Support: Core (Services with a type other than ExternalName) + + Support: Implementation-specific (Services with type ExternalName) + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the backend. When unspecified, the local + namespace is inferred. + + Note that when a namespace different than the local namespace is specified, + a ReferenceGrant object is required in the referent namespace to allow that + namespace's owner to accept the reference. See the ReferenceGrant + documentation for details. + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port specifies the destination port number to use for this resource. + Port is required when the referent is a Kubernetes Service. In this + case, the port number is the service port number, not the target port. + For other resources, destination port might be derived from the referent + resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + x-kubernetes-validations: + - message: Must have port for Service reference + rule: '(size(self.group) == 0 && self.kind == ''Service'') + ? has(self.port) : true' + required: + - backendRef + type: object + requestRedirect: + description: |- + RequestRedirect defines a schema for a filter that responds to the + request with an HTTP redirection. + + Support: Core + properties: + hostname: + description: |- + Hostname is the hostname to be used in the value of the `Location` + header in the response. + When empty, the hostname in the `Host` header of the request is used. + + Support: Core + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + path: + description: |- + Path defines parameters used to modify the path of the incoming request. + The modified path is then used to construct the `Location` header. When + empty, the request path is used as-is. + + Support: Extended + properties: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". + + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. + + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. + + Request Path | Prefix Match | Replace Prefix | Modified Path + maxLength: 1024 + type: string + type: + description: |- + Type defines the type of path modifier. Additional types may be + added in a future release of the API. + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + x-kubernetes-validations: + - message: replaceFullPath must be specified when + type is set to 'ReplaceFullPath' + rule: 'self.type == ''ReplaceFullPath'' ? has(self.replaceFullPath) + : true' + - message: type must be 'ReplaceFullPath' when replaceFullPath + is set + rule: 'has(self.replaceFullPath) ? self.type == + ''ReplaceFullPath'' : true' + - message: replacePrefixMatch must be specified when + type is set to 'ReplacePrefixMatch' + rule: 'self.type == ''ReplacePrefixMatch'' ? has(self.replacePrefixMatch) + : true' + - message: type must be 'ReplacePrefixMatch' when + replacePrefixMatch is set + rule: 'has(self.replacePrefixMatch) ? self.type + == ''ReplacePrefixMatch'' : true' + port: + description: |- + Port is the port to be used in the value of the `Location` + header in the response. + + If no port is specified, the redirect port MUST be derived using the + following rules: + + * If redirect scheme is not-empty, the redirect port MUST be the well-known + port associated with the redirect scheme. Specifically "http" to port 80 + and "https" to port 443. If the redirect scheme does not have a + well-known port, the listener port of the Gateway SHOULD be used. + * If redirect scheme is empty, the redirect port MUST be the Gateway + Listener port. + + Implementations SHOULD NOT add the port number in the 'Location' + header in the following cases: + + * A Location header that will use HTTP (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 80. + * A Location header that will use HTTPS (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 443. + + Support: Extended + format: int32 + maximum: 65535 + minimum: 1 + type: integer + scheme: + description: |- + Scheme is the scheme to be used in the value of the `Location` header in + the response. When empty, the scheme of the request is used. + + Scheme redirects can affect the port of the redirect, for more information, + refer to the documentation for the port field of this filter. + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + + Support: Extended + enum: + - http + - https + type: string + statusCode: + default: 302 + description: |- + StatusCode is the HTTP status code to be used in response. + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + + Support: Core + enum: + - 301 + - 302 + type: integer + type: object + responseHeaderModifier: + description: |- + ResponseHeaderModifier defines a schema for a filter that modifies response + headers. + + Support: Extended + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. + + Input: + GET /foo HTTP/1.1 + my-header: foo + + Config: + add: + - name: "my-header" + value: "bar,baz" + + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP Header + name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header + to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz + + Config: + remove: ["my-header1", "my-header3"] + + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. + + Input: + GET /foo HTTP/1.1 + my-header: foo + + Config: + set: + - name: "my-header" + value: "bar" + + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP Header + name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header + to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: + description: |- + Type identifies the type of filter to apply. As with other API fields, + types are classified into three conformance levels: + + - Core: Filter types and their corresponding configuration defined by + "Support: Core" in this package, e.g. "RequestHeaderModifier". All + implementations must support core filters. + + - Extended: Filter types and their corresponding configuration defined by + "Support: Extended" in this package, e.g. "RequestMirror". Implementers + are encouraged to support extended filters. + + - Implementation-specific: Filters that are defined and supported by + specific vendors. + In the future, filters showing convergence in behavior across multiple + implementations will be considered for inclusion in extended or core + conformance levels. Filter-specific configuration for such filters + is specified using the ExtensionRef field. `Type` should be set to + "ExtensionRef" for custom filters. + + Implementers are encouraged to define custom implementation types to + extend the core API with implementation-specific behavior. + + If a reference to a custom filter type cannot be resolved, the filter + MUST NOT be skipped. Instead, requests that would have been processed by + that filter MUST receive a HTTP error response. + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestMirror + - RequestRedirect + - URLRewrite + - ExtensionRef + type: string + urlRewrite: + description: |- + URLRewrite defines a schema for a filter that modifies a request during forwarding. + + Support: Extended + properties: + hostname: + description: |- + Hostname is the value to be used to replace the Host header value during + forwarding. + + Support: Extended + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + path: + description: |- + Path defines a path rewrite. + + Support: Extended + properties: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". + + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. + + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. + + Request Path | Prefix Match | Replace Prefix | Modified Path + maxLength: 1024 + type: string + type: + description: |- + Type defines the type of path modifier. Additional types may be + added in a future release of the API. + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + x-kubernetes-validations: + - message: replaceFullPath must be specified when + type is set to 'ReplaceFullPath' + rule: 'self.type == ''ReplaceFullPath'' ? has(self.replaceFullPath) + : true' + - message: type must be 'ReplaceFullPath' when replaceFullPath + is set + rule: 'has(self.replaceFullPath) ? self.type == + ''ReplaceFullPath'' : true' + - message: replacePrefixMatch must be specified when + type is set to 'ReplacePrefixMatch' + rule: 'self.type == ''ReplacePrefixMatch'' ? has(self.replacePrefixMatch) + : true' + - message: type must be 'ReplacePrefixMatch' when + replacePrefixMatch is set + rule: 'has(self.replacePrefixMatch) ? self.type + == ''ReplacePrefixMatch'' : true' + type: object + required: + - type + type: object + x-kubernetes-validations: + - message: filter.requestHeaderModifier must be nil if the + filter.type is not RequestHeaderModifier + rule: '!(has(self.requestHeaderModifier) && self.type != + ''RequestHeaderModifier'')' + - message: filter.requestHeaderModifier must be specified + for RequestHeaderModifier filter.type + rule: '!(!has(self.requestHeaderModifier) && self.type == + ''RequestHeaderModifier'')' + - message: filter.responseHeaderModifier must be nil if the + filter.type is not ResponseHeaderModifier + rule: '!(has(self.responseHeaderModifier) && self.type != + ''ResponseHeaderModifier'')' + - message: filter.responseHeaderModifier must be specified + for ResponseHeaderModifier filter.type + rule: '!(!has(self.responseHeaderModifier) && self.type + == ''ResponseHeaderModifier'')' + - message: filter.requestMirror must be nil if the filter.type + is not RequestMirror + rule: '!(has(self.requestMirror) && self.type != ''RequestMirror'')' + - message: filter.requestMirror must be specified for RequestMirror + filter.type + rule: '!(!has(self.requestMirror) && self.type == ''RequestMirror'')' + - message: filter.requestRedirect must be nil if the filter.type + is not RequestRedirect + rule: '!(has(self.requestRedirect) && self.type != ''RequestRedirect'')' + - message: filter.requestRedirect must be specified for RequestRedirect + filter.type + rule: '!(!has(self.requestRedirect) && self.type == ''RequestRedirect'')' + - message: filter.urlRewrite must be nil if the filter.type + is not URLRewrite + rule: '!(has(self.urlRewrite) && self.type != ''URLRewrite'')' + - message: filter.urlRewrite must be specified for URLRewrite + filter.type + rule: '!(!has(self.urlRewrite) && self.type == ''URLRewrite'')' + - message: filter.extensionRef must be nil if the filter.type + is not ExtensionRef + rule: '!(has(self.extensionRef) && self.type != ''ExtensionRef'')' + - message: filter.extensionRef must be specified for ExtensionRef + filter.type + rule: '!(!has(self.extensionRef) && self.type == ''ExtensionRef'')' + maxItems: 16 + type: array + x-kubernetes-validations: + - message: May specify either httpRouteFilterRequestRedirect + or httpRouteFilterRequestRewrite, but not both + rule: '!(self.exists(f, f.type == ''RequestRedirect'') && + self.exists(f, f.type == ''URLRewrite''))' + - message: RequestHeaderModifier filter cannot be repeated + rule: self.filter(f, f.type == 'RequestHeaderModifier').size() + <= 1 + - message: ResponseHeaderModifier filter cannot be repeated + rule: self.filter(f, f.type == 'ResponseHeaderModifier').size() + <= 1 + - message: RequestRedirect filter cannot be repeated + rule: self.filter(f, f.type == 'RequestRedirect').size() <= + 1 + - message: URLRewrite filter cannot be repeated + rule: self.filter(f, f.type == 'URLRewrite').size() <= 1 + matches: + default: + - path: + type: PathPrefix + value: / + description: |- + Matches define conditions used for matching the rule against incoming + HTTP requests. Each match is independent, i.e. this rule will be matched + if **any** one of the matches is satisfied. + + For example, take the following matches configuration: + + ``` + matches: + - path: + value: "/foo" + headers: + - name: "version" + value: "v2" + - path: + value: "/v2/foo" + ``` + + For a request to match against this rule, a request must satisfy + EITHER of the two conditions: + + - path prefixed with `/foo` AND contains the header `version: v2` + - path prefix of `/v2/foo` + + See the documentation for HTTPRouteMatch on how to specify multiple + match conditions that should be ANDed together. + + If no matches are specified, the default is a prefix + path match on "/", which has the effect of matching every + HTTP request. + + Proxy or Load Balancer routing configuration generated from HTTPRoutes + MUST prioritize matches based on the following criteria, continuing on + ties. Across all rules specified on applicable Routes, precedence must be + given to the match having: + + * "Exact" path match. + * "Prefix" path match with largest number of characters. + * Method match. + * Largest number of header matches. + * Largest number of query param matches. + + Note: The precedence of RegularExpression path matches are implementation-specific. + + If ties still exist across multiple Routes, matching precedence MUST be + determined in order of the following criteria, continuing on ties: + + * The oldest Route based on creation timestamp. + * The Route appearing first in alphabetical order by + "{namespace}/{name}". + + If ties still exist within an HTTPRoute, matching precedence MUST be granted + to the FIRST matching rule (in list order) with a match meeting the above + criteria. + + When no rules matching a request have been successfully attached to the + parent a request is coming from, a HTTP 404 status code MUST be returned. + items: + description: "HTTPRouteMatch defines the predicate used to + match requests to a given\naction. Multiple match types + are ANDed together, i.e. the match will\nevaluate to true + only if all conditions are satisfied.\n\nFor example, the + match below will match a HTTP request only if its path\nstarts + with `/foo` AND it contains the `version: v1` header:\n\n```\nmatch:\n\n\tpath:\n\t + \ value: \"/foo\"\n\theaders:\n\t- name: \"version\"\n\t + \ value \"v1\"\n\n```" + properties: + headers: + description: |- + Headers specifies HTTP request header matchers. Multiple match values are + ANDed together, meaning, a request must match all the specified headers + to select the route. + items: + description: |- + HTTPHeaderMatch describes how to select a HTTP route by matching HTTP request + headers. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + If multiple entries specify equivalent header names, only the first + entry with an equivalent name MUST be considered for a match. Subsequent + entries with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + + When a header is repeated in an HTTP request, it is + implementation-specific behavior as to how this is represented. + Generally, proxies should follow the guidance from the RFC: + https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 regarding + processing a repeated header, with special handling for "Set-Cookie". + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: |- + Type specifies how to match against the value of the header. + + Support: Core (Exact) + + Support: Implementation-specific (RegularExpression) + + Since RegularExpression HeaderMatchType has implementation-specific + conformance, implementations can support POSIX, PCRE or any other dialects + of regular expressions. Please read the implementation's documentation to + determine the supported dialect. + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP Header to + be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + method: + description: |- + Method specifies HTTP method matcher. + When specified, this route will be matched only if the request has the + specified method. + + Support: Extended + enum: + - GET + - HEAD + - POST + - PUT + - DELETE + - CONNECT + - OPTIONS + - TRACE + - PATCH + type: string + path: + default: + type: PathPrefix + value: / + description: |- + Path specifies a HTTP request path matcher. If this field is not + specified, a default prefix match on the "/" path is provided. + properties: + type: + default: PathPrefix + description: |- + Type specifies how to match against the path Value. + + Support: Core (Exact, PathPrefix) + + Support: Implementation-specific (RegularExpression) + enum: + - Exact + - PathPrefix + - RegularExpression + type: string + value: + default: / + description: Value of the HTTP path to match against. + maxLength: 1024 + type: string + type: object + x-kubernetes-validations: + - message: value must be an absolute path and start with + '/' when type one of ['Exact', 'PathPrefix'] + rule: '(self.type in [''Exact'',''PathPrefix'']) ? self.value.startsWith(''/'') + : true' + - message: must not contain '//' when type one of ['Exact', + 'PathPrefix'] + rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.contains(''//'') + : true' + - message: must not contain '/./' when type one of ['Exact', + 'PathPrefix'] + rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.contains(''/./'') + : true' + - message: must not contain '/../' when type one of ['Exact', + 'PathPrefix'] + rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.contains(''/../'') + : true' + - message: must not contain '%2f' when type one of ['Exact', + 'PathPrefix'] + rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.contains(''%2f'') + : true' + - message: must not contain '%2F' when type one of ['Exact', + 'PathPrefix'] + rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.contains(''%2F'') + : true' + - message: must not contain '#' when type one of ['Exact', + 'PathPrefix'] + rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.contains(''#'') + : true' + - message: must not end with '/..' when type one of ['Exact', + 'PathPrefix'] + rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.endsWith(''/..'') + : true' + - message: must not end with '/.' when type one of ['Exact', + 'PathPrefix'] + rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.endsWith(''/.'') + : true' + - message: type must be one of ['Exact', 'PathPrefix', + 'RegularExpression'] + rule: self.type in ['Exact','PathPrefix'] || self.type + == 'RegularExpression' + - message: must only contain valid characters (matching + ^(?:[-A-Za-z0-9/._~!$&'()*+,;=:@]|[%][0-9a-fA-F]{2})+$) + for types ['Exact', 'PathPrefix'] + rule: '(self.type in [''Exact'',''PathPrefix'']) ? self.value.matches(r"""^(?:[-A-Za-z0-9/._~!$&''()*+,;=:@]|[%][0-9a-fA-F]{2})+$""") + : true' + queryParams: + description: |- + QueryParams specifies HTTP query parameter matchers. Multiple match + values are ANDed together, meaning, a request must match all the + specified query parameters to select the route. + + Support: Extended + items: + description: |- + HTTPQueryParamMatch describes how to select a HTTP route by matching HTTP + query parameters. + properties: + name: + description: |- + Name is the name of the HTTP query param to be matched. This must be an + exact string match. (See + https://tools.ietf.org/html/rfc7230#section-2.7.3). + + If multiple entries specify equivalent query param names, only the first + entry with an equivalent name MUST be considered for a match. Subsequent + entries with an equivalent query param name MUST be ignored. + + If a query param is repeated in an HTTP request, the behavior is + purposely left undefined, since different data planes have different + capabilities. However, it is *recommended* that implementations should + match against the first value of the param if the data plane supports it, + as this behavior is expected in other load balancing contexts outside of + the Gateway API. + + Users SHOULD NOT route traffic based on repeated query params to guard + themselves against potential differences in the implementations. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: |- + Type specifies how to match against the value of the query parameter. + + Support: Extended (Exact) + + Support: Implementation-specific (RegularExpression) + + Since RegularExpression QueryParamMatchType has Implementation-specific + conformance, implementations can support POSIX, PCRE or any other + dialects of regular expressions. Please read the implementation's + documentation to determine the supported dialect. + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP query param + to be matched. + maxLength: 1024 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + maxItems: 64 + type: array + timeouts: + description: |- + Timeouts defines the timeouts that can be configured for an HTTP request. + + Support: Extended + properties: + backendRequest: + description: |- + BackendRequest specifies a timeout for an individual request from the gateway + to a backend. This covers the time from when the request first starts being + sent from the gateway to when the full response has been received from the backend. + + Setting a timeout to the zero duration (e.g. "0s") SHOULD disable the timeout + completely. Implementations that cannot completely disable the timeout MUST + instead interpret the zero duration as the longest possible value to which + the timeout can be set. + + An entire client HTTP transaction with a gateway, covered by the Request timeout, + may result in more than one call from the gateway to the destination backend, + for example, if automatic retries are supported. + + The value of BackendRequest must be a Gateway API Duration string as defined by + GEP-2257. When this field is unspecified, its behavior is implementation-specific; + when specified, the value of BackendRequest must be no more than the value of the + Request timeout (since the Request timeout encompasses the BackendRequest timeout). + + Support: Extended + pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ + type: string + request: + description: |- + Request specifies the maximum duration for a gateway to respond to an HTTP request. + If the gateway has not been able to respond before this deadline is met, the gateway + MUST return a timeout error. + + For example, setting the `rules.timeouts.request` field to the value `10s` in an + `HTTPRoute` will cause a timeout if a client request is taking longer than 10 seconds + to complete. + + Setting a timeout to the zero duration (e.g. "0s") SHOULD disable the timeout + completely. Implementations that cannot completely disable the timeout MUST + instead interpret the zero duration as the longest possible value to which + the timeout can be set. + + This timeout is intended to cover as close to the whole request-response transaction + as possible although an implementation MAY choose to start the timeout after the entire + request stream has been received instead of immediately after the transaction is + initiated by the client. + + The value of Request is a Gateway API Duration string as defined by GEP-2257. When this + field is unspecified, request timeout behavior is implementation-specific. + + Support: Extended + pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ + type: string + type: object + x-kubernetes-validations: + - message: backendRequest timeout cannot be longer than request + timeout + rule: '!(has(self.request) && has(self.backendRequest) && + duration(self.request) != duration(''0s'') && duration(self.backendRequest) + > duration(self.request))' + type: object + x-kubernetes-validations: + - message: RequestRedirect filter must not be used together with + backendRefs + rule: '(has(self.backendRefs) && size(self.backendRefs) > 0) ? + (!has(self.filters) || self.filters.all(f, !has(f.requestRedirect))): + true' + - message: When using RequestRedirect filter with path.replacePrefixMatch, + exactly one PathPrefix match must be specified + rule: '(has(self.filters) && self.filters.exists_one(f, has(f.requestRedirect) + && has(f.requestRedirect.path) && f.requestRedirect.path.type + == ''ReplacePrefixMatch'' && has(f.requestRedirect.path.replacePrefixMatch))) + ? ((size(self.matches) != 1 || !has(self.matches[0].path) || + self.matches[0].path.type != ''PathPrefix'') ? false : true) + : true' + - message: When using URLRewrite filter with path.replacePrefixMatch, + exactly one PathPrefix match must be specified + rule: '(has(self.filters) && self.filters.exists_one(f, has(f.urlRewrite) + && has(f.urlRewrite.path) && f.urlRewrite.path.type == ''ReplacePrefixMatch'' + && has(f.urlRewrite.path.replacePrefixMatch))) ? ((size(self.matches) + != 1 || !has(self.matches[0].path) || self.matches[0].path.type + != ''PathPrefix'') ? false : true) : true' + - message: Within backendRefs, when using RequestRedirect filter + with path.replacePrefixMatch, exactly one PathPrefix match must + be specified + rule: '(has(self.backendRefs) && self.backendRefs.exists_one(b, + (has(b.filters) && b.filters.exists_one(f, has(f.requestRedirect) + && has(f.requestRedirect.path) && f.requestRedirect.path.type + == ''ReplacePrefixMatch'' && has(f.requestRedirect.path.replacePrefixMatch))) + )) ? ((size(self.matches) != 1 || !has(self.matches[0].path) + || self.matches[0].path.type != ''PathPrefix'') ? false : true) + : true' + - message: Within backendRefs, When using URLRewrite filter with + path.replacePrefixMatch, exactly one PathPrefix match must be + specified + rule: '(has(self.backendRefs) && self.backendRefs.exists_one(b, + (has(b.filters) && b.filters.exists_one(f, has(f.urlRewrite) + && has(f.urlRewrite.path) && f.urlRewrite.path.type == ''ReplacePrefixMatch'' + && has(f.urlRewrite.path.replacePrefixMatch))) )) ? ((size(self.matches) + != 1 || !has(self.matches[0].path) || self.matches[0].path.type + != ''PathPrefix'') ? false : true) : true' + maxItems: 16 + type: array + x-kubernetes-validations: + - message: While 16 rules and 64 matches per rule are allowed, the + total number of matches across all rules in a route must be less + than 128 + rule: '(self.size() > 0 ? self[0].matches.size() : 0) + (self.size() + > 1 ? self[1].matches.size() : 0) + (self.size() > 2 ? self[2].matches.size() + : 0) + (self.size() > 3 ? self[3].matches.size() : 0) + (self.size() + > 4 ? self[4].matches.size() : 0) + (self.size() > 5 ? self[5].matches.size() + : 0) + (self.size() > 6 ? self[6].matches.size() : 0) + (self.size() + > 7 ? self[7].matches.size() : 0) + (self.size() > 8 ? self[8].matches.size() + : 0) + (self.size() > 9 ? self[9].matches.size() : 0) + (self.size() + > 10 ? self[10].matches.size() : 0) + (self.size() > 11 ? self[11].matches.size() + : 0) + (self.size() > 12 ? self[12].matches.size() : 0) + (self.size() + > 13 ? self[13].matches.size() : 0) + (self.size() > 14 ? self[14].matches.size() + : 0) + (self.size() > 15 ? self[15].matches.size() : 0) <= 128' + type: object + status: + description: Status defines the current state of HTTPRoute. + properties: + parents: + description: |- + Parents is a list of parent resources (usually Gateways) that are + associated with the route, and the status of the route with respect to + each parent. When this route attaches to a parent, the controller that + manages the parent must add an entry to this list when the controller + first sees the route and should update the entry as appropriate when the + route or gateway is modified. + + Note that parent references that cannot be resolved by an implementation + of this API will not be added to this list. Implementations of this API + can only populate Route status for the Gateways/parent resources they are + responsible for. + + A maximum of 32 Gateways will be represented in this list. An empty list + means the route has not been attached to any Gateway. + items: + description: |- + RouteParentStatus describes the status of a route with respect to an + associated Parent. + properties: + conditions: + description: |- + Conditions describes the status of the route with respect to the Gateway. + Note that the route's availability is also subject to the Gateway's own + status conditions and listener status. + + If the Route's ParentRef specifies an existing Gateway that supports + Routes of this kind AND that Gateway's controller has sufficient access, + then that Gateway's controller MUST set the "Accepted" condition on the + Route, to indicate whether the route has been accepted or rejected by the + Gateway, and why. + + A Route MUST be considered "Accepted" if at least one of the Route's + rules is implemented by the Gateway. + + There are a number of cases where the "Accepted" condition may not be set + due to lack of controller visibility, that includes when: + + * The Route refers to a non-existent parent. + * The Route is of a type that the controller does not support. + * The Route is in a namespace the controller does not have access to. + items: + description: Condition contains details for one aspect of + the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, + Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + controllerName: + description: |- + ControllerName is a domain/path string that indicates the name of the + controller that wrote this status. This corresponds with the + controllerName field on GatewayClass. + + Example: "example.net/gateway-controller". + + The format of this field is DOMAIN "/" PATH, where DOMAIN and PATH are + valid Kubernetes names + (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). + + Controllers MUST populate this field when writing status. Controllers should ensure that + entries to status populated with their ControllerName are cleaned up when they are no + longer necessary. + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + parentRef: + description: |- + ParentRef corresponds with a ParentRef in the spec that this + RouteParentStatus struct describes the status of. + properties: + group: + default: gateway.networking.k8s.io + description: |- + Group is the group of the referent. + When unspecified, "gateway.networking.k8s.io" is inferred. + To set the core API group (such as for a "Service" kind referent), + Group must be explicitly set to "" (empty string). + + Support: Core + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: |- + Kind is kind of the referent. + + There are two kinds of parent resources with "Core" support: + + * Gateway (Gateway conformance profile) + * Service (Mesh conformance profile, ClusterIP Services only) + + Support for other resources is Implementation-Specific. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: |- + Name is the name of the referent. + + Support: Core + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the referent. When unspecified, this refers + to the local namespace of the Route. + + Note that there are specific rules for ParentRefs which cross namespace + boundaries. Cross-namespace references are only valid if they are explicitly + allowed by something in the namespace they are referring to. For example: + Gateway has the AllowedRoutes field, and ReferenceGrant provides a + generic way to enable any other kind of cross-namespace reference. + + + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port is the network port this Route targets. It can be interpreted + differently based on the type of parent resource. + + When the parent resource is a Gateway, this targets all listeners + listening on the specified port that also support this kind of Route(and + select this Route). It's not recommended to set `Port` unless the + networking behaviors specified in a Route must apply to a specific port + as opposed to a listener(s) whose port(s) may be changed. When both Port + and SectionName are specified, the name and port of the selected listener + must match both specified values. + + + + Implementations MAY choose to support other parent resources. + Implementations supporting other types of parent resources MUST clearly + document how/if Port is interpreted. + + For the purpose of status, an attachment is considered successful as + long as the parent resource accepts it partially. For example, Gateway + listeners can restrict which Routes can attach to them by Route kind, + namespace, or hostname. If 1 of 2 Gateway listeners accept attachment + from the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this Route, + the Route MUST be considered detached from the Gateway. + + Support: Extended + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: |- + SectionName is the name of a section within the target resource. In the + following resources, SectionName is interpreted as the following: + + * Gateway: Listener name. When both Port (experimental) and SectionName + are specified, the name and port of the selected listener must match + both specified values. + * Service: Port name. When both Port (experimental) and SectionName + are specified, the name and port of the selected listener must match + both specified values. + + Implementations MAY choose to support attaching Routes to other resources. + If that is the case, they MUST clearly document how SectionName is + interpreted. + + When unspecified (empty string), this will reference the entire resource. + For the purpose of status, an attachment is considered successful if at + least one section in the parent resource accepts it. For example, Gateway + listeners can restrict which Routes can attach to them by Route kind, + namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from + the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this Route, the + Route MUST be considered detached from the Gateway. + + Support: Core + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + required: + - controllerName + - parentRef + type: object + maxItems: 32 + type: array + required: + - parents + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .spec.hostnames + name: Hostnames + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1beta1 + schema: + openAPIV3Schema: + description: |- + HTTPRoute provides a way to route HTTP requests. This includes the capability + to match requests by hostname, path, header, or query param. Filters can be + used to specify additional processing steps. Backends specify where matching + requests should be routed. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of HTTPRoute. + properties: + hostnames: + description: |- + Hostnames defines a set of hostnames that should match against the HTTP Host + header to select a HTTPRoute used to process the request. Implementations + MUST ignore any port value specified in the HTTP Host header while + performing a match and (absent of any applicable header modification + configuration) MUST forward this header unmodified to the backend. + + Valid values for Hostnames are determined by RFC 1123 definition of a + hostname with 2 notable exceptions: + + 1. IPs are not allowed. + 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard + label must appear by itself as the first label. + + If a hostname is specified by both the Listener and HTTPRoute, there + must be at least one intersecting hostname for the HTTPRoute to be + attached to the Listener. For example: + + * A Listener with `test.example.com` as the hostname matches HTTPRoutes + that have either not specified any hostnames, or have specified at + least one of `test.example.com` or `*.example.com`. + * A Listener with `*.example.com` as the hostname matches HTTPRoutes + that have either not specified any hostnames or have specified at least + one hostname that matches the Listener hostname. For example, + `*.example.com`, `test.example.com`, and `foo.test.example.com` would + all match. On the other hand, `example.com` and `test.example.net` would + not match. + + Hostnames that are prefixed with a wildcard label (`*.`) are interpreted + as a suffix match. That means that a match for `*.example.com` would match + both `test.example.com`, and `foo.test.example.com`, but not `example.com`. + + If both the Listener and HTTPRoute have specified hostnames, any + HTTPRoute hostnames that do not match the Listener hostname MUST be + ignored. For example, if a Listener specified `*.example.com`, and the + HTTPRoute specified `test.example.com` and `test.example.net`, + `test.example.net` must not be considered for a match. + + If both the Listener and HTTPRoute have specified hostnames, and none + match with the criteria above, then the HTTPRoute is not accepted. The + implementation must raise an 'Accepted' Condition with a status of + `False` in the corresponding RouteParentStatus. + + In the event that multiple HTTPRoutes specify intersecting hostnames (e.g. + overlapping wildcard matching and exact matching hostnames), precedence must + be given to rules from the HTTPRoute with the largest number of: + + * Characters in a matching non-wildcard hostname. + * Characters in a matching hostname. + + If ties exist across multiple Routes, the matching precedence rules for + HTTPRouteMatches takes over. + + Support: Core + items: + description: |- + Hostname is the fully qualified domain name of a network host. This matches + the RFC 1123 definition of a hostname with 2 notable exceptions: + + 1. IPs are not allowed. + 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard + label must appear by itself as the first label. + + Hostname can be "precise" which is a domain name without the terminating + dot of a network host (e.g. "foo.example.com") or "wildcard", which is a + domain name prefixed with a single wildcard label (e.g. `*.example.com`). + + Note that as per RFC1035 and RFC1123, a *label* must consist of lower case + alphanumeric characters or '-', and must start and end with an alphanumeric + character. No other punctuation is allowed. + maxLength: 253 + minLength: 1 + pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + maxItems: 16 + type: array + parentRefs: + description: |+ + ParentRefs references the resources (usually Gateways) that a Route wants + to be attached to. Note that the referenced parent resource needs to + allow this for the attachment to be complete. For Gateways, that means + the Gateway needs to allow attachment from Routes of this kind and + namespace. For Services, that means the Service must either be in the same + namespace for a "producer" route, or the mesh implementation must support + and allow "consumer" routes for the referenced Service. ReferenceGrant is + not applicable for governing ParentRefs to Services - it is not possible to + create a "producer" route for a Service in a different namespace from the + Route. + + There are two kinds of parent resources with "Core" support: + + * Gateway (Gateway conformance profile) + * Service (Mesh conformance profile, ClusterIP Services only) + + This API may be extended in the future to support additional kinds of parent + resources. + + ParentRefs must be _distinct_. This means either that: + + * They select different objects. If this is the case, then parentRef + entries are distinct. In terms of fields, this means that the + multi-part key defined by `group`, `kind`, `namespace`, and `name` must + be unique across all parentRef entries in the Route. + * They do not select different objects, but for each optional field used, + each ParentRef that selects the same object must set the same set of + optional fields to different values. If one ParentRef sets a + combination of optional fields, all must set the same combination. + + Some examples: + + * If one ParentRef sets `sectionName`, all ParentRefs referencing the + same object must also set `sectionName`. + * If one ParentRef sets `port`, all ParentRefs referencing the same + object must also set `port`. + * If one ParentRef sets `sectionName` and `port`, all ParentRefs + referencing the same object must also set `sectionName` and `port`. + + It is possible to separately reference multiple distinct objects that may + be collapsed by an implementation. For example, some implementations may + choose to merge compatible Gateway Listeners together. If that is the + case, the list of routes attached to those resources should also be + merged. + + Note that for ParentRefs that cross namespace boundaries, there are specific + rules. Cross-namespace references are only valid if they are explicitly + allowed by something in the namespace they are referring to. For example, + Gateway has the AllowedRoutes field, and ReferenceGrant provides a + generic way to enable other kinds of cross-namespace reference. + + + + + + + items: + description: |- + ParentReference identifies an API object (usually a Gateway) that can be considered + a parent of this resource (usually a route). There are two kinds of parent resources + with "Core" support: + + * Gateway (Gateway conformance profile) + * Service (Mesh conformance profile, ClusterIP Services only) + + This API may be extended in the future to support additional kinds of parent + resources. + + The API object must be valid in the cluster; the Group and Kind must + be registered in the cluster for this reference to be valid. + properties: + group: + default: gateway.networking.k8s.io + description: |- + Group is the group of the referent. + When unspecified, "gateway.networking.k8s.io" is inferred. + To set the core API group (such as for a "Service" kind referent), + Group must be explicitly set to "" (empty string). + + Support: Core + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: |- + Kind is kind of the referent. + + There are two kinds of parent resources with "Core" support: + + * Gateway (Gateway conformance profile) + * Service (Mesh conformance profile, ClusterIP Services only) + + Support for other resources is Implementation-Specific. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: |- + Name is the name of the referent. + + Support: Core + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the referent. When unspecified, this refers + to the local namespace of the Route. + + Note that there are specific rules for ParentRefs which cross namespace + boundaries. Cross-namespace references are only valid if they are explicitly + allowed by something in the namespace they are referring to. For example: + Gateway has the AllowedRoutes field, and ReferenceGrant provides a + generic way to enable any other kind of cross-namespace reference. + + + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port is the network port this Route targets. It can be interpreted + differently based on the type of parent resource. + + When the parent resource is a Gateway, this targets all listeners + listening on the specified port that also support this kind of Route(and + select this Route). It's not recommended to set `Port` unless the + networking behaviors specified in a Route must apply to a specific port + as opposed to a listener(s) whose port(s) may be changed. When both Port + and SectionName are specified, the name and port of the selected listener + must match both specified values. + + + + Implementations MAY choose to support other parent resources. + Implementations supporting other types of parent resources MUST clearly + document how/if Port is interpreted. + + For the purpose of status, an attachment is considered successful as + long as the parent resource accepts it partially. For example, Gateway + listeners can restrict which Routes can attach to them by Route kind, + namespace, or hostname. If 1 of 2 Gateway listeners accept attachment + from the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this Route, + the Route MUST be considered detached from the Gateway. + + Support: Extended + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: |- + SectionName is the name of a section within the target resource. In the + following resources, SectionName is interpreted as the following: + + * Gateway: Listener name. When both Port (experimental) and SectionName + are specified, the name and port of the selected listener must match + both specified values. + * Service: Port name. When both Port (experimental) and SectionName + are specified, the name and port of the selected listener must match + both specified values. + + Implementations MAY choose to support attaching Routes to other resources. + If that is the case, they MUST clearly document how SectionName is + interpreted. + + When unspecified (empty string), this will reference the entire resource. + For the purpose of status, an attachment is considered successful if at + least one section in the parent resource accepts it. For example, Gateway + listeners can restrict which Routes can attach to them by Route kind, + namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from + the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this Route, the + Route MUST be considered detached from the Gateway. + + Support: Core + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + maxItems: 32 + type: array + x-kubernetes-validations: + - message: sectionName must be specified when parentRefs includes + 2 or more references to the same parent + rule: 'self.all(p1, self.all(p2, p1.group == p2.group && p1.kind + == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) + || p1.__namespace__ == '''') && (!has(p2.__namespace__) || p2.__namespace__ + == '''')) || (has(p1.__namespace__) && has(p2.__namespace__) && + p1.__namespace__ == p2.__namespace__ )) ? ((!has(p1.sectionName) + || p1.sectionName == '''') == (!has(p2.sectionName) || p2.sectionName + == '''')) : true))' + - message: sectionName must be unique when parentRefs includes 2 or + more references to the same parent + rule: self.all(p1, self.exists_one(p2, p1.group == p2.group && p1.kind + == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) + || p1.__namespace__ == '') && (!has(p2.__namespace__) || p2.__namespace__ + == '')) || (has(p1.__namespace__) && has(p2.__namespace__) && + p1.__namespace__ == p2.__namespace__ )) && (((!has(p1.sectionName) + || p1.sectionName == '') && (!has(p2.sectionName) || p2.sectionName + == '')) || (has(p1.sectionName) && has(p2.sectionName) && p1.sectionName + == p2.sectionName)))) + rules: + default: + - matches: + - path: + type: PathPrefix + value: / + description: |+ + Rules are a list of HTTP matchers, filters and actions. + + items: + description: |- + HTTPRouteRule defines semantics for matching an HTTP request based on + conditions (matches), processing it (filters), and forwarding the request to + an API object (backendRefs). + properties: + backendRefs: + description: |- + BackendRefs defines the backend(s) where matching requests should be + sent. + + Failure behavior here depends on how many BackendRefs are specified and + how many are invalid. + + If *all* entries in BackendRefs are invalid, and there are also no filters + specified in this route rule, *all* traffic which matches this rule MUST + receive a 500 status code. + + See the HTTPBackendRef definition for the rules about what makes a single + HTTPBackendRef invalid. + + When a HTTPBackendRef is invalid, 500 status codes MUST be returned for + requests that would have otherwise been routed to an invalid backend. If + multiple backends are specified, and some are invalid, the proportion of + requests that would otherwise have been routed to an invalid backend + MUST receive a 500 status code. + + For example, if two backends are specified with equal weights, and one is + invalid, 50 percent of traffic must receive a 500. Implementations may + choose how that 50 percent is determined. + + When a HTTPBackendRef refers to a Service that has no ready endpoints, + implementations SHOULD return a 503 for requests to that backend instead. + If an implementation chooses to do this, all of the above rules for 500 responses + MUST also apply for responses that return a 503. + + Support: Core for Kubernetes Service + + Support: Extended for Kubernetes ServiceImport + + Support: Implementation-specific for any other resource + + Support for weight: Core + items: + description: |- + HTTPBackendRef defines how a HTTPRoute forwards a HTTP request. + + Note that when a namespace different than the local namespace is specified, a + ReferenceGrant object is required in the referent namespace to allow that + namespace's owner to accept the reference. See the ReferenceGrant + documentation for details. + + + + When the BackendRef points to a Kubernetes Service, implementations SHOULD + honor the appProtocol field if it is set for the target Service Port. + + Implementations supporting appProtocol SHOULD recognize the Kubernetes + Standard Application Protocols defined in KEP-3726. + + If a Service appProtocol isn't specified, an implementation MAY infer the + backend protocol through its own means. Implementations MAY infer the + protocol from the Route type referring to the backend Service. + + If a Route is not able to send traffic to the backend using the specified + protocol then the backend is considered invalid. Implementations MUST set the + "ResolvedRefs" condition to "False" with the "UnsupportedProtocol" reason. + + + properties: + filters: + description: |- + Filters defined at this level should be executed if and only if the + request is being forwarded to the backend defined here. + + Support: Implementation-specific (For broader support of filters, use the + Filters field in HTTPRouteRule.) + items: + description: |- + HTTPRouteFilter defines processing steps that must be completed during the + request or response lifecycle. HTTPRouteFilters are meant as an extension + point to express processing that may be done in Gateway implementations. Some + examples include request or response modification, implementing + authentication strategies, rate-limiting, and traffic shaping. API + guarantee/conformance is defined based on the type of the filter. + properties: + extensionRef: + description: |- + ExtensionRef is an optional, implementation-specific extension to the + "filter" behavior. For example, resource "myroutefilter" in group + "networking.example.net"). ExtensionRef MUST NOT be used for core and + extended filters. + + This filter can be used multiple times within the same rule. + + Support: Implementation-specific + properties: + group: + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is kind of the referent. For + example "HTTPRoute" or "Service". + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + - name + type: object + requestHeaderModifier: + description: |- + RequestHeaderModifier defines a schema for a filter that modifies request + headers. + + Support: Core + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. + + Input: + GET /foo HTTP/1.1 + my-header: foo + + Config: + add: + - name: "my-header" + value: "bar,baz" + + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP + Header name and value as defined by RFC + 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP + Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz + + Config: + remove: ["my-header1", "my-header3"] + + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. + + Input: + GET /foo HTTP/1.1 + my-header: foo + + Config: + set: + - name: "my-header" + value: "bar" + + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP + Header name and value as defined by RFC + 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP + Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: + description: |+ + RequestMirror defines a schema for a filter that mirrors requests. + Requests are sent to the specified destination, but responses from + that destination are ignored. + + This filter can be used multiple times within the same rule. Note that + not all implementations will be able to support mirroring to multiple + backends. + + Support: Extended + + properties: + backendRef: + description: |- + BackendRef references a resource where mirrored requests are sent. + + Mirrored requests must be sent only to a single destination endpoint + within this BackendRef, irrespective of how many endpoints are present + within this BackendRef. + + If the referent cannot be found, this BackendRef is invalid and must be + dropped from the Gateway. The controller must ensure the "ResolvedRefs" + condition on the Route status is set to `status: False` and not configure + this backend in the underlying implementation. + + If there is a cross-namespace reference to an *existing* object + that is not allowed by a ReferenceGrant, the controller must ensure the + "ResolvedRefs" condition on the Route is set to `status: False`, + with the "RefNotPermitted" reason and not configure this backend in the + underlying implementation. + + In either error case, the Message of the `ResolvedRefs` Condition + should be used to provide more detail about the problem. + + Support: Extended for Kubernetes Service + + Support: Implementation-specific for any other resource + properties: + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Service + description: |- + Kind is the Kubernetes resource kind of the referent. For example + "Service". + + Defaults to "Service" when not specified. + + ExternalName services can refer to CNAME DNS records that may live + outside of the cluster and as such are difficult to reason about in + terms of conformance. They also may not be safe to forward to (see + CVE-2021-25740 for more information). Implementations SHOULD NOT + support ExternalName Services. + + Support: Core (Services with a type other than ExternalName) + + Support: Implementation-specific (Services with type ExternalName) + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the backend. When unspecified, the local + namespace is inferred. + + Note that when a namespace different than the local namespace is specified, + a ReferenceGrant object is required in the referent namespace to allow that + namespace's owner to accept the reference. See the ReferenceGrant + documentation for details. + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port specifies the destination port number to use for this resource. + Port is required when the referent is a Kubernetes Service. In this + case, the port number is the service port number, not the target port. + For other resources, destination port might be derived from the referent + resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + x-kubernetes-validations: + - message: Must have port for Service reference + rule: '(size(self.group) == 0 && self.kind + == ''Service'') ? has(self.port) : true' + required: + - backendRef + type: object + requestRedirect: + description: |- + RequestRedirect defines a schema for a filter that responds to the + request with an HTTP redirection. + + Support: Core + properties: + hostname: + description: |- + Hostname is the hostname to be used in the value of the `Location` + header in the response. + When empty, the hostname in the `Host` header of the request is used. + + Support: Core + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + path: + description: |- + Path defines parameters used to modify the path of the incoming request. + The modified path is then used to construct the `Location` header. When + empty, the request path is used as-is. + + Support: Extended + properties: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". + + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. + + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. + + Request Path | Prefix Match | Replace Prefix | Modified Path + maxLength: 1024 + type: string + type: + description: |- + Type defines the type of path modifier. Additional types may be + added in a future release of the API. + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + x-kubernetes-validations: + - message: replaceFullPath must be specified + when type is set to 'ReplaceFullPath' + rule: 'self.type == ''ReplaceFullPath'' ? + has(self.replaceFullPath) : true' + - message: type must be 'ReplaceFullPath' when + replaceFullPath is set + rule: 'has(self.replaceFullPath) ? self.type + == ''ReplaceFullPath'' : true' + - message: replacePrefixMatch must be specified + when type is set to 'ReplacePrefixMatch' + rule: 'self.type == ''ReplacePrefixMatch'' + ? has(self.replacePrefixMatch) : true' + - message: type must be 'ReplacePrefixMatch' + when replacePrefixMatch is set + rule: 'has(self.replacePrefixMatch) ? self.type + == ''ReplacePrefixMatch'' : true' + port: + description: |- + Port is the port to be used in the value of the `Location` + header in the response. + + If no port is specified, the redirect port MUST be derived using the + following rules: + + * If redirect scheme is not-empty, the redirect port MUST be the well-known + port associated with the redirect scheme. Specifically "http" to port 80 + and "https" to port 443. If the redirect scheme does not have a + well-known port, the listener port of the Gateway SHOULD be used. + * If redirect scheme is empty, the redirect port MUST be the Gateway + Listener port. + + Implementations SHOULD NOT add the port number in the 'Location' + header in the following cases: + + * A Location header that will use HTTP (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 80. + * A Location header that will use HTTPS (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 443. + + Support: Extended + format: int32 + maximum: 65535 + minimum: 1 + type: integer + scheme: + description: |- + Scheme is the scheme to be used in the value of the `Location` header in + the response. When empty, the scheme of the request is used. + + Scheme redirects can affect the port of the redirect, for more information, + refer to the documentation for the port field of this filter. + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + + Support: Extended + enum: + - http + - https + type: string + statusCode: + default: 302 + description: |- + StatusCode is the HTTP status code to be used in response. + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + + Support: Core + enum: + - 301 + - 302 + type: integer + type: object + responseHeaderModifier: + description: |- + ResponseHeaderModifier defines a schema for a filter that modifies response + headers. + + Support: Extended + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. + + Input: + GET /foo HTTP/1.1 + my-header: foo + + Config: + add: + - name: "my-header" + value: "bar,baz" + + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP + Header name and value as defined by RFC + 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP + Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz + + Config: + remove: ["my-header1", "my-header3"] + + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. + + Input: + GET /foo HTTP/1.1 + my-header: foo + + Config: + set: + - name: "my-header" + value: "bar" + + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP + Header name and value as defined by RFC + 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP + Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: + description: |- + Type identifies the type of filter to apply. As with other API fields, + types are classified into three conformance levels: + + - Core: Filter types and their corresponding configuration defined by + "Support: Core" in this package, e.g. "RequestHeaderModifier". All + implementations must support core filters. + + - Extended: Filter types and their corresponding configuration defined by + "Support: Extended" in this package, e.g. "RequestMirror". Implementers + are encouraged to support extended filters. + + - Implementation-specific: Filters that are defined and supported by + specific vendors. + In the future, filters showing convergence in behavior across multiple + implementations will be considered for inclusion in extended or core + conformance levels. Filter-specific configuration for such filters + is specified using the ExtensionRef field. `Type` should be set to + "ExtensionRef" for custom filters. + + Implementers are encouraged to define custom implementation types to + extend the core API with implementation-specific behavior. + + If a reference to a custom filter type cannot be resolved, the filter + MUST NOT be skipped. Instead, requests that would have been processed by + that filter MUST receive a HTTP error response. + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestMirror + - RequestRedirect + - URLRewrite + - ExtensionRef + type: string + urlRewrite: + description: |- + URLRewrite defines a schema for a filter that modifies a request during forwarding. + + Support: Extended + properties: + hostname: + description: |- + Hostname is the value to be used to replace the Host header value during + forwarding. + + Support: Extended + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + path: + description: |- + Path defines a path rewrite. + + Support: Extended + properties: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". + + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. + + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. + + Request Path | Prefix Match | Replace Prefix | Modified Path + maxLength: 1024 + type: string + type: + description: |- + Type defines the type of path modifier. Additional types may be + added in a future release of the API. + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + x-kubernetes-validations: + - message: replaceFullPath must be specified + when type is set to 'ReplaceFullPath' + rule: 'self.type == ''ReplaceFullPath'' ? + has(self.replaceFullPath) : true' + - message: type must be 'ReplaceFullPath' when + replaceFullPath is set + rule: 'has(self.replaceFullPath) ? self.type + == ''ReplaceFullPath'' : true' + - message: replacePrefixMatch must be specified + when type is set to 'ReplacePrefixMatch' + rule: 'self.type == ''ReplacePrefixMatch'' + ? has(self.replacePrefixMatch) : true' + - message: type must be 'ReplacePrefixMatch' + when replacePrefixMatch is set + rule: 'has(self.replacePrefixMatch) ? self.type + == ''ReplacePrefixMatch'' : true' + type: object + required: + - type + type: object + x-kubernetes-validations: + - message: filter.requestHeaderModifier must be nil + if the filter.type is not RequestHeaderModifier + rule: '!(has(self.requestHeaderModifier) && self.type + != ''RequestHeaderModifier'')' + - message: filter.requestHeaderModifier must be specified + for RequestHeaderModifier filter.type + rule: '!(!has(self.requestHeaderModifier) && self.type + == ''RequestHeaderModifier'')' + - message: filter.responseHeaderModifier must be nil + if the filter.type is not ResponseHeaderModifier + rule: '!(has(self.responseHeaderModifier) && self.type + != ''ResponseHeaderModifier'')' + - message: filter.responseHeaderModifier must be specified + for ResponseHeaderModifier filter.type + rule: '!(!has(self.responseHeaderModifier) && self.type + == ''ResponseHeaderModifier'')' + - message: filter.requestMirror must be nil if the filter.type + is not RequestMirror + rule: '!(has(self.requestMirror) && self.type != ''RequestMirror'')' + - message: filter.requestMirror must be specified for + RequestMirror filter.type + rule: '!(!has(self.requestMirror) && self.type == + ''RequestMirror'')' + - message: filter.requestRedirect must be nil if the + filter.type is not RequestRedirect + rule: '!(has(self.requestRedirect) && self.type != + ''RequestRedirect'')' + - message: filter.requestRedirect must be specified + for RequestRedirect filter.type + rule: '!(!has(self.requestRedirect) && self.type == + ''RequestRedirect'')' + - message: filter.urlRewrite must be nil if the filter.type + is not URLRewrite + rule: '!(has(self.urlRewrite) && self.type != ''URLRewrite'')' + - message: filter.urlRewrite must be specified for URLRewrite + filter.type + rule: '!(!has(self.urlRewrite) && self.type == ''URLRewrite'')' + - message: filter.extensionRef must be nil if the filter.type + is not ExtensionRef + rule: '!(has(self.extensionRef) && self.type != ''ExtensionRef'')' + - message: filter.extensionRef must be specified for + ExtensionRef filter.type + rule: '!(!has(self.extensionRef) && self.type == ''ExtensionRef'')' + maxItems: 16 + type: array + x-kubernetes-validations: + - message: May specify either httpRouteFilterRequestRedirect + or httpRouteFilterRequestRewrite, but not both + rule: '!(self.exists(f, f.type == ''RequestRedirect'') + && self.exists(f, f.type == ''URLRewrite''))' + - message: May specify either httpRouteFilterRequestRedirect + or httpRouteFilterRequestRewrite, but not both + rule: '!(self.exists(f, f.type == ''RequestRedirect'') + && self.exists(f, f.type == ''URLRewrite''))' + - message: RequestHeaderModifier filter cannot be repeated + rule: self.filter(f, f.type == 'RequestHeaderModifier').size() + <= 1 + - message: ResponseHeaderModifier filter cannot be repeated + rule: self.filter(f, f.type == 'ResponseHeaderModifier').size() + <= 1 + - message: RequestRedirect filter cannot be repeated + rule: self.filter(f, f.type == 'RequestRedirect').size() + <= 1 + - message: URLRewrite filter cannot be repeated + rule: self.filter(f, f.type == 'URLRewrite').size() + <= 1 + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Service + description: |- + Kind is the Kubernetes resource kind of the referent. For example + "Service". + + Defaults to "Service" when not specified. + + ExternalName services can refer to CNAME DNS records that may live + outside of the cluster and as such are difficult to reason about in + terms of conformance. They also may not be safe to forward to (see + CVE-2021-25740 for more information). Implementations SHOULD NOT + support ExternalName Services. + + Support: Core (Services with a type other than ExternalName) + + Support: Implementation-specific (Services with type ExternalName) + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the backend. When unspecified, the local + namespace is inferred. + + Note that when a namespace different than the local namespace is specified, + a ReferenceGrant object is required in the referent namespace to allow that + namespace's owner to accept the reference. See the ReferenceGrant + documentation for details. + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port specifies the destination port number to use for this resource. + Port is required when the referent is a Kubernetes Service. In this + case, the port number is the service port number, not the target port. + For other resources, destination port might be derived from the referent + resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + weight: + default: 1 + description: |- + Weight specifies the proportion of requests forwarded to the referenced + backend. This is computed as weight/(sum of all weights in this + BackendRefs list). For non-zero values, there may be some epsilon from + the exact proportion defined here depending on the precision an + implementation supports. Weight is not a percentage and the sum of + weights does not need to equal 100. + + If only one backend is specified and it has a weight greater than 0, 100% + of the traffic is forwarded to that backend. If weight is set to 0, no + traffic should be forwarded for this entry. If unspecified, weight + defaults to 1. + + Support for this field varies based on the context where used. + format: int32 + maximum: 1000000 + minimum: 0 + type: integer + required: + - name + type: object + x-kubernetes-validations: + - message: Must have port for Service reference + rule: '(size(self.group) == 0 && self.kind == ''Service'') + ? has(self.port) : true' + maxItems: 16 + type: array + filters: + description: |- + Filters define the filters that are applied to requests that match + this rule. + + Wherever possible, implementations SHOULD implement filters in the order + they are specified. + + Implementations MAY choose to implement this ordering strictly, rejecting + any combination or order of filters that can not be supported. If implementations + choose a strict interpretation of filter ordering, they MUST clearly document + that behavior. + + To reject an invalid combination or order of filters, implementations SHOULD + consider the Route Rules with this configuration invalid. If all Route Rules + in a Route are invalid, the entire Route would be considered invalid. If only + a portion of Route Rules are invalid, implementations MUST set the + "PartiallyInvalid" condition for the Route. + + Conformance-levels at this level are defined based on the type of filter: + + - ALL core filters MUST be supported by all implementations. + - Implementers are encouraged to support extended filters. + - Implementation-specific custom filters have no API guarantees across + implementations. + + Specifying the same filter multiple times is not supported unless explicitly + indicated in the filter. + + All filters are expected to be compatible with each other except for the + URLRewrite and RequestRedirect filters, which may not be combined. If an + implementation can not support other combinations of filters, they must clearly + document that limitation. In cases where incompatible or unsupported + filters are specified and cause the `Accepted` condition to be set to status + `False`, implementations may use the `IncompatibleFilters` reason to specify + this configuration error. + + Support: Core + items: + description: |- + HTTPRouteFilter defines processing steps that must be completed during the + request or response lifecycle. HTTPRouteFilters are meant as an extension + point to express processing that may be done in Gateway implementations. Some + examples include request or response modification, implementing + authentication strategies, rate-limiting, and traffic shaping. API + guarantee/conformance is defined based on the type of the filter. + properties: + extensionRef: + description: |- + ExtensionRef is an optional, implementation-specific extension to the + "filter" behavior. For example, resource "myroutefilter" in group + "networking.example.net"). ExtensionRef MUST NOT be used for core and + extended filters. + + This filter can be used multiple times within the same rule. + + Support: Implementation-specific + properties: + group: + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is kind of the referent. For example + "HTTPRoute" or "Service". + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + - name + type: object + requestHeaderModifier: + description: |- + RequestHeaderModifier defines a schema for a filter that modifies request + headers. + + Support: Core + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. + + Input: + GET /foo HTTP/1.1 + my-header: foo + + Config: + add: + - name: "my-header" + value: "bar,baz" + + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP Header + name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header + to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz + + Config: + remove: ["my-header1", "my-header3"] + + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. + + Input: + GET /foo HTTP/1.1 + my-header: foo + + Config: + set: + - name: "my-header" + value: "bar" + + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP Header + name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header + to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: + description: |+ + RequestMirror defines a schema for a filter that mirrors requests. + Requests are sent to the specified destination, but responses from + that destination are ignored. + + This filter can be used multiple times within the same rule. Note that + not all implementations will be able to support mirroring to multiple + backends. + + Support: Extended + + properties: + backendRef: + description: |- + BackendRef references a resource where mirrored requests are sent. + + Mirrored requests must be sent only to a single destination endpoint + within this BackendRef, irrespective of how many endpoints are present + within this BackendRef. + + If the referent cannot be found, this BackendRef is invalid and must be + dropped from the Gateway. The controller must ensure the "ResolvedRefs" + condition on the Route status is set to `status: False` and not configure + this backend in the underlying implementation. + + If there is a cross-namespace reference to an *existing* object + that is not allowed by a ReferenceGrant, the controller must ensure the + "ResolvedRefs" condition on the Route is set to `status: False`, + with the "RefNotPermitted" reason and not configure this backend in the + underlying implementation. + + In either error case, the Message of the `ResolvedRefs` Condition + should be used to provide more detail about the problem. + + Support: Extended for Kubernetes Service + + Support: Implementation-specific for any other resource + properties: + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Service + description: |- + Kind is the Kubernetes resource kind of the referent. For example + "Service". + + Defaults to "Service" when not specified. + + ExternalName services can refer to CNAME DNS records that may live + outside of the cluster and as such are difficult to reason about in + terms of conformance. They also may not be safe to forward to (see + CVE-2021-25740 for more information). Implementations SHOULD NOT + support ExternalName Services. + + Support: Core (Services with a type other than ExternalName) + + Support: Implementation-specific (Services with type ExternalName) + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the backend. When unspecified, the local + namespace is inferred. + + Note that when a namespace different than the local namespace is specified, + a ReferenceGrant object is required in the referent namespace to allow that + namespace's owner to accept the reference. See the ReferenceGrant + documentation for details. + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port specifies the destination port number to use for this resource. + Port is required when the referent is a Kubernetes Service. In this + case, the port number is the service port number, not the target port. + For other resources, destination port might be derived from the referent + resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + x-kubernetes-validations: + - message: Must have port for Service reference + rule: '(size(self.group) == 0 && self.kind == ''Service'') + ? has(self.port) : true' + required: + - backendRef + type: object + requestRedirect: + description: |- + RequestRedirect defines a schema for a filter that responds to the + request with an HTTP redirection. + + Support: Core + properties: + hostname: + description: |- + Hostname is the hostname to be used in the value of the `Location` + header in the response. + When empty, the hostname in the `Host` header of the request is used. + + Support: Core + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + path: + description: |- + Path defines parameters used to modify the path of the incoming request. + The modified path is then used to construct the `Location` header. When + empty, the request path is used as-is. + + Support: Extended + properties: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". + + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. + + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. + + Request Path | Prefix Match | Replace Prefix | Modified Path + maxLength: 1024 + type: string + type: + description: |- + Type defines the type of path modifier. Additional types may be + added in a future release of the API. + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + x-kubernetes-validations: + - message: replaceFullPath must be specified when + type is set to 'ReplaceFullPath' + rule: 'self.type == ''ReplaceFullPath'' ? has(self.replaceFullPath) + : true' + - message: type must be 'ReplaceFullPath' when replaceFullPath + is set + rule: 'has(self.replaceFullPath) ? self.type == + ''ReplaceFullPath'' : true' + - message: replacePrefixMatch must be specified when + type is set to 'ReplacePrefixMatch' + rule: 'self.type == ''ReplacePrefixMatch'' ? has(self.replacePrefixMatch) + : true' + - message: type must be 'ReplacePrefixMatch' when + replacePrefixMatch is set + rule: 'has(self.replacePrefixMatch) ? self.type + == ''ReplacePrefixMatch'' : true' + port: + description: |- + Port is the port to be used in the value of the `Location` + header in the response. + + If no port is specified, the redirect port MUST be derived using the + following rules: + + * If redirect scheme is not-empty, the redirect port MUST be the well-known + port associated with the redirect scheme. Specifically "http" to port 80 + and "https" to port 443. If the redirect scheme does not have a + well-known port, the listener port of the Gateway SHOULD be used. + * If redirect scheme is empty, the redirect port MUST be the Gateway + Listener port. + + Implementations SHOULD NOT add the port number in the 'Location' + header in the following cases: + + * A Location header that will use HTTP (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 80. + * A Location header that will use HTTPS (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 443. + + Support: Extended + format: int32 + maximum: 65535 + minimum: 1 + type: integer + scheme: + description: |- + Scheme is the scheme to be used in the value of the `Location` header in + the response. When empty, the scheme of the request is used. + + Scheme redirects can affect the port of the redirect, for more information, + refer to the documentation for the port field of this filter. + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + + Support: Extended + enum: + - http + - https + type: string + statusCode: + default: 302 + description: |- + StatusCode is the HTTP status code to be used in response. + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + + Support: Core + enum: + - 301 + - 302 + type: integer + type: object + responseHeaderModifier: + description: |- + ResponseHeaderModifier defines a schema for a filter that modifies response + headers. + + Support: Extended + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. + + Input: + GET /foo HTTP/1.1 + my-header: foo + + Config: + add: + - name: "my-header" + value: "bar,baz" + + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP Header + name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header + to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz + + Config: + remove: ["my-header1", "my-header3"] + + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. + + Input: + GET /foo HTTP/1.1 + my-header: foo + + Config: + set: + - name: "my-header" + value: "bar" + + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP Header + name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header + to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: + description: |- + Type identifies the type of filter to apply. As with other API fields, + types are classified into three conformance levels: + + - Core: Filter types and their corresponding configuration defined by + "Support: Core" in this package, e.g. "RequestHeaderModifier". All + implementations must support core filters. + + - Extended: Filter types and their corresponding configuration defined by + "Support: Extended" in this package, e.g. "RequestMirror". Implementers + are encouraged to support extended filters. + + - Implementation-specific: Filters that are defined and supported by + specific vendors. + In the future, filters showing convergence in behavior across multiple + implementations will be considered for inclusion in extended or core + conformance levels. Filter-specific configuration for such filters + is specified using the ExtensionRef field. `Type` should be set to + "ExtensionRef" for custom filters. + + Implementers are encouraged to define custom implementation types to + extend the core API with implementation-specific behavior. + + If a reference to a custom filter type cannot be resolved, the filter + MUST NOT be skipped. Instead, requests that would have been processed by + that filter MUST receive a HTTP error response. + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestMirror + - RequestRedirect + - URLRewrite + - ExtensionRef + type: string + urlRewrite: + description: |- + URLRewrite defines a schema for a filter that modifies a request during forwarding. + + Support: Extended + properties: + hostname: + description: |- + Hostname is the value to be used to replace the Host header value during + forwarding. + + Support: Extended + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + path: + description: |- + Path defines a path rewrite. + + Support: Extended + properties: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". + + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. + + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. + + Request Path | Prefix Match | Replace Prefix | Modified Path + maxLength: 1024 + type: string + type: + description: |- + Type defines the type of path modifier. Additional types may be + added in a future release of the API. + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + x-kubernetes-validations: + - message: replaceFullPath must be specified when + type is set to 'ReplaceFullPath' + rule: 'self.type == ''ReplaceFullPath'' ? has(self.replaceFullPath) + : true' + - message: type must be 'ReplaceFullPath' when replaceFullPath + is set + rule: 'has(self.replaceFullPath) ? self.type == + ''ReplaceFullPath'' : true' + - message: replacePrefixMatch must be specified when + type is set to 'ReplacePrefixMatch' + rule: 'self.type == ''ReplacePrefixMatch'' ? has(self.replacePrefixMatch) + : true' + - message: type must be 'ReplacePrefixMatch' when + replacePrefixMatch is set + rule: 'has(self.replacePrefixMatch) ? self.type + == ''ReplacePrefixMatch'' : true' + type: object + required: + - type + type: object + x-kubernetes-validations: + - message: filter.requestHeaderModifier must be nil if the + filter.type is not RequestHeaderModifier + rule: '!(has(self.requestHeaderModifier) && self.type != + ''RequestHeaderModifier'')' + - message: filter.requestHeaderModifier must be specified + for RequestHeaderModifier filter.type + rule: '!(!has(self.requestHeaderModifier) && self.type == + ''RequestHeaderModifier'')' + - message: filter.responseHeaderModifier must be nil if the + filter.type is not ResponseHeaderModifier + rule: '!(has(self.responseHeaderModifier) && self.type != + ''ResponseHeaderModifier'')' + - message: filter.responseHeaderModifier must be specified + for ResponseHeaderModifier filter.type + rule: '!(!has(self.responseHeaderModifier) && self.type + == ''ResponseHeaderModifier'')' + - message: filter.requestMirror must be nil if the filter.type + is not RequestMirror + rule: '!(has(self.requestMirror) && self.type != ''RequestMirror'')' + - message: filter.requestMirror must be specified for RequestMirror + filter.type + rule: '!(!has(self.requestMirror) && self.type == ''RequestMirror'')' + - message: filter.requestRedirect must be nil if the filter.type + is not RequestRedirect + rule: '!(has(self.requestRedirect) && self.type != ''RequestRedirect'')' + - message: filter.requestRedirect must be specified for RequestRedirect + filter.type + rule: '!(!has(self.requestRedirect) && self.type == ''RequestRedirect'')' + - message: filter.urlRewrite must be nil if the filter.type + is not URLRewrite + rule: '!(has(self.urlRewrite) && self.type != ''URLRewrite'')' + - message: filter.urlRewrite must be specified for URLRewrite + filter.type + rule: '!(!has(self.urlRewrite) && self.type == ''URLRewrite'')' + - message: filter.extensionRef must be nil if the filter.type + is not ExtensionRef + rule: '!(has(self.extensionRef) && self.type != ''ExtensionRef'')' + - message: filter.extensionRef must be specified for ExtensionRef + filter.type + rule: '!(!has(self.extensionRef) && self.type == ''ExtensionRef'')' + maxItems: 16 + type: array + x-kubernetes-validations: + - message: May specify either httpRouteFilterRequestRedirect + or httpRouteFilterRequestRewrite, but not both + rule: '!(self.exists(f, f.type == ''RequestRedirect'') && + self.exists(f, f.type == ''URLRewrite''))' + - message: RequestHeaderModifier filter cannot be repeated + rule: self.filter(f, f.type == 'RequestHeaderModifier').size() + <= 1 + - message: ResponseHeaderModifier filter cannot be repeated + rule: self.filter(f, f.type == 'ResponseHeaderModifier').size() + <= 1 + - message: RequestRedirect filter cannot be repeated + rule: self.filter(f, f.type == 'RequestRedirect').size() <= + 1 + - message: URLRewrite filter cannot be repeated + rule: self.filter(f, f.type == 'URLRewrite').size() <= 1 + matches: + default: + - path: + type: PathPrefix + value: / + description: |- + Matches define conditions used for matching the rule against incoming + HTTP requests. Each match is independent, i.e. this rule will be matched + if **any** one of the matches is satisfied. + + For example, take the following matches configuration: + + ``` + matches: + - path: + value: "/foo" + headers: + - name: "version" + value: "v2" + - path: + value: "/v2/foo" + ``` + + For a request to match against this rule, a request must satisfy + EITHER of the two conditions: + + - path prefixed with `/foo` AND contains the header `version: v2` + - path prefix of `/v2/foo` + + See the documentation for HTTPRouteMatch on how to specify multiple + match conditions that should be ANDed together. + + If no matches are specified, the default is a prefix + path match on "/", which has the effect of matching every + HTTP request. + + Proxy or Load Balancer routing configuration generated from HTTPRoutes + MUST prioritize matches based on the following criteria, continuing on + ties. Across all rules specified on applicable Routes, precedence must be + given to the match having: + + * "Exact" path match. + * "Prefix" path match with largest number of characters. + * Method match. + * Largest number of header matches. + * Largest number of query param matches. + + Note: The precedence of RegularExpression path matches are implementation-specific. + + If ties still exist across multiple Routes, matching precedence MUST be + determined in order of the following criteria, continuing on ties: + + * The oldest Route based on creation timestamp. + * The Route appearing first in alphabetical order by + "{namespace}/{name}". + + If ties still exist within an HTTPRoute, matching precedence MUST be granted + to the FIRST matching rule (in list order) with a match meeting the above + criteria. + + When no rules matching a request have been successfully attached to the + parent a request is coming from, a HTTP 404 status code MUST be returned. + items: + description: "HTTPRouteMatch defines the predicate used to + match requests to a given\naction. Multiple match types + are ANDed together, i.e. the match will\nevaluate to true + only if all conditions are satisfied.\n\nFor example, the + match below will match a HTTP request only if its path\nstarts + with `/foo` AND it contains the `version: v1` header:\n\n```\nmatch:\n\n\tpath:\n\t + \ value: \"/foo\"\n\theaders:\n\t- name: \"version\"\n\t + \ value \"v1\"\n\n```" + properties: + headers: + description: |- + Headers specifies HTTP request header matchers. Multiple match values are + ANDed together, meaning, a request must match all the specified headers + to select the route. + items: + description: |- + HTTPHeaderMatch describes how to select a HTTP route by matching HTTP request + headers. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + If multiple entries specify equivalent header names, only the first + entry with an equivalent name MUST be considered for a match. Subsequent + entries with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + + When a header is repeated in an HTTP request, it is + implementation-specific behavior as to how this is represented. + Generally, proxies should follow the guidance from the RFC: + https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 regarding + processing a repeated header, with special handling for "Set-Cookie". + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: |- + Type specifies how to match against the value of the header. + + Support: Core (Exact) + + Support: Implementation-specific (RegularExpression) + + Since RegularExpression HeaderMatchType has implementation-specific + conformance, implementations can support POSIX, PCRE or any other dialects + of regular expressions. Please read the implementation's documentation to + determine the supported dialect. + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP Header to + be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + method: + description: |- + Method specifies HTTP method matcher. + When specified, this route will be matched only if the request has the + specified method. + + Support: Extended + enum: + - GET + - HEAD + - POST + - PUT + - DELETE + - CONNECT + - OPTIONS + - TRACE + - PATCH + type: string + path: + default: + type: PathPrefix + value: / + description: |- + Path specifies a HTTP request path matcher. If this field is not + specified, a default prefix match on the "/" path is provided. + properties: + type: + default: PathPrefix + description: |- + Type specifies how to match against the path Value. + + Support: Core (Exact, PathPrefix) + + Support: Implementation-specific (RegularExpression) + enum: + - Exact + - PathPrefix + - RegularExpression + type: string + value: + default: / + description: Value of the HTTP path to match against. + maxLength: 1024 + type: string + type: object + x-kubernetes-validations: + - message: value must be an absolute path and start with + '/' when type one of ['Exact', 'PathPrefix'] + rule: '(self.type in [''Exact'',''PathPrefix'']) ? self.value.startsWith(''/'') + : true' + - message: must not contain '//' when type one of ['Exact', + 'PathPrefix'] + rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.contains(''//'') + : true' + - message: must not contain '/./' when type one of ['Exact', + 'PathPrefix'] + rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.contains(''/./'') + : true' + - message: must not contain '/../' when type one of ['Exact', + 'PathPrefix'] + rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.contains(''/../'') + : true' + - message: must not contain '%2f' when type one of ['Exact', + 'PathPrefix'] + rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.contains(''%2f'') + : true' + - message: must not contain '%2F' when type one of ['Exact', + 'PathPrefix'] + rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.contains(''%2F'') + : true' + - message: must not contain '#' when type one of ['Exact', + 'PathPrefix'] + rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.contains(''#'') + : true' + - message: must not end with '/..' when type one of ['Exact', + 'PathPrefix'] + rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.endsWith(''/..'') + : true' + - message: must not end with '/.' when type one of ['Exact', + 'PathPrefix'] + rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.endsWith(''/.'') + : true' + - message: type must be one of ['Exact', 'PathPrefix', + 'RegularExpression'] + rule: self.type in ['Exact','PathPrefix'] || self.type + == 'RegularExpression' + - message: must only contain valid characters (matching + ^(?:[-A-Za-z0-9/._~!$&'()*+,;=:@]|[%][0-9a-fA-F]{2})+$) + for types ['Exact', 'PathPrefix'] + rule: '(self.type in [''Exact'',''PathPrefix'']) ? self.value.matches(r"""^(?:[-A-Za-z0-9/._~!$&''()*+,;=:@]|[%][0-9a-fA-F]{2})+$""") + : true' + queryParams: + description: |- + QueryParams specifies HTTP query parameter matchers. Multiple match + values are ANDed together, meaning, a request must match all the + specified query parameters to select the route. + + Support: Extended + items: + description: |- + HTTPQueryParamMatch describes how to select a HTTP route by matching HTTP + query parameters. + properties: + name: + description: |- + Name is the name of the HTTP query param to be matched. This must be an + exact string match. (See + https://tools.ietf.org/html/rfc7230#section-2.7.3). + + If multiple entries specify equivalent query param names, only the first + entry with an equivalent name MUST be considered for a match. Subsequent + entries with an equivalent query param name MUST be ignored. + + If a query param is repeated in an HTTP request, the behavior is + purposely left undefined, since different data planes have different + capabilities. However, it is *recommended* that implementations should + match against the first value of the param if the data plane supports it, + as this behavior is expected in other load balancing contexts outside of + the Gateway API. + + Users SHOULD NOT route traffic based on repeated query params to guard + themselves against potential differences in the implementations. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: |- + Type specifies how to match against the value of the query parameter. + + Support: Extended (Exact) + + Support: Implementation-specific (RegularExpression) + + Since RegularExpression QueryParamMatchType has Implementation-specific + conformance, implementations can support POSIX, PCRE or any other + dialects of regular expressions. Please read the implementation's + documentation to determine the supported dialect. + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP query param + to be matched. + maxLength: 1024 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + maxItems: 64 + type: array + timeouts: + description: |- + Timeouts defines the timeouts that can be configured for an HTTP request. + + Support: Extended + properties: + backendRequest: + description: |- + BackendRequest specifies a timeout for an individual request from the gateway + to a backend. This covers the time from when the request first starts being + sent from the gateway to when the full response has been received from the backend. + + Setting a timeout to the zero duration (e.g. "0s") SHOULD disable the timeout + completely. Implementations that cannot completely disable the timeout MUST + instead interpret the zero duration as the longest possible value to which + the timeout can be set. + + An entire client HTTP transaction with a gateway, covered by the Request timeout, + may result in more than one call from the gateway to the destination backend, + for example, if automatic retries are supported. + + The value of BackendRequest must be a Gateway API Duration string as defined by + GEP-2257. When this field is unspecified, its behavior is implementation-specific; + when specified, the value of BackendRequest must be no more than the value of the + Request timeout (since the Request timeout encompasses the BackendRequest timeout). + + Support: Extended + pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ + type: string + request: + description: |- + Request specifies the maximum duration for a gateway to respond to an HTTP request. + If the gateway has not been able to respond before this deadline is met, the gateway + MUST return a timeout error. + + For example, setting the `rules.timeouts.request` field to the value `10s` in an + `HTTPRoute` will cause a timeout if a client request is taking longer than 10 seconds + to complete. + + Setting a timeout to the zero duration (e.g. "0s") SHOULD disable the timeout + completely. Implementations that cannot completely disable the timeout MUST + instead interpret the zero duration as the longest possible value to which + the timeout can be set. + + This timeout is intended to cover as close to the whole request-response transaction + as possible although an implementation MAY choose to start the timeout after the entire + request stream has been received instead of immediately after the transaction is + initiated by the client. + + The value of Request is a Gateway API Duration string as defined by GEP-2257. When this + field is unspecified, request timeout behavior is implementation-specific. + + Support: Extended + pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ + type: string + type: object + x-kubernetes-validations: + - message: backendRequest timeout cannot be longer than request + timeout + rule: '!(has(self.request) && has(self.backendRequest) && + duration(self.request) != duration(''0s'') && duration(self.backendRequest) + > duration(self.request))' + type: object + x-kubernetes-validations: + - message: RequestRedirect filter must not be used together with + backendRefs + rule: '(has(self.backendRefs) && size(self.backendRefs) > 0) ? + (!has(self.filters) || self.filters.all(f, !has(f.requestRedirect))): + true' + - message: When using RequestRedirect filter with path.replacePrefixMatch, + exactly one PathPrefix match must be specified + rule: '(has(self.filters) && self.filters.exists_one(f, has(f.requestRedirect) + && has(f.requestRedirect.path) && f.requestRedirect.path.type + == ''ReplacePrefixMatch'' && has(f.requestRedirect.path.replacePrefixMatch))) + ? ((size(self.matches) != 1 || !has(self.matches[0].path) || + self.matches[0].path.type != ''PathPrefix'') ? false : true) + : true' + - message: When using URLRewrite filter with path.replacePrefixMatch, + exactly one PathPrefix match must be specified + rule: '(has(self.filters) && self.filters.exists_one(f, has(f.urlRewrite) + && has(f.urlRewrite.path) && f.urlRewrite.path.type == ''ReplacePrefixMatch'' + && has(f.urlRewrite.path.replacePrefixMatch))) ? ((size(self.matches) + != 1 || !has(self.matches[0].path) || self.matches[0].path.type + != ''PathPrefix'') ? false : true) : true' + - message: Within backendRefs, when using RequestRedirect filter + with path.replacePrefixMatch, exactly one PathPrefix match must + be specified + rule: '(has(self.backendRefs) && self.backendRefs.exists_one(b, + (has(b.filters) && b.filters.exists_one(f, has(f.requestRedirect) + && has(f.requestRedirect.path) && f.requestRedirect.path.type + == ''ReplacePrefixMatch'' && has(f.requestRedirect.path.replacePrefixMatch))) + )) ? ((size(self.matches) != 1 || !has(self.matches[0].path) + || self.matches[0].path.type != ''PathPrefix'') ? false : true) + : true' + - message: Within backendRefs, When using URLRewrite filter with + path.replacePrefixMatch, exactly one PathPrefix match must be + specified + rule: '(has(self.backendRefs) && self.backendRefs.exists_one(b, + (has(b.filters) && b.filters.exists_one(f, has(f.urlRewrite) + && has(f.urlRewrite.path) && f.urlRewrite.path.type == ''ReplacePrefixMatch'' + && has(f.urlRewrite.path.replacePrefixMatch))) )) ? ((size(self.matches) + != 1 || !has(self.matches[0].path) || self.matches[0].path.type + != ''PathPrefix'') ? false : true) : true' + maxItems: 16 + type: array + x-kubernetes-validations: + - message: While 16 rules and 64 matches per rule are allowed, the + total number of matches across all rules in a route must be less + than 128 + rule: '(self.size() > 0 ? self[0].matches.size() : 0) + (self.size() + > 1 ? self[1].matches.size() : 0) + (self.size() > 2 ? self[2].matches.size() + : 0) + (self.size() > 3 ? self[3].matches.size() : 0) + (self.size() + > 4 ? self[4].matches.size() : 0) + (self.size() > 5 ? self[5].matches.size() + : 0) + (self.size() > 6 ? self[6].matches.size() : 0) + (self.size() + > 7 ? self[7].matches.size() : 0) + (self.size() > 8 ? self[8].matches.size() + : 0) + (self.size() > 9 ? self[9].matches.size() : 0) + (self.size() + > 10 ? self[10].matches.size() : 0) + (self.size() > 11 ? self[11].matches.size() + : 0) + (self.size() > 12 ? self[12].matches.size() : 0) + (self.size() + > 13 ? self[13].matches.size() : 0) + (self.size() > 14 ? self[14].matches.size() + : 0) + (self.size() > 15 ? self[15].matches.size() : 0) <= 128' + type: object + status: + description: Status defines the current state of HTTPRoute. + properties: + parents: + description: |- + Parents is a list of parent resources (usually Gateways) that are + associated with the route, and the status of the route with respect to + each parent. When this route attaches to a parent, the controller that + manages the parent must add an entry to this list when the controller + first sees the route and should update the entry as appropriate when the + route or gateway is modified. + + Note that parent references that cannot be resolved by an implementation + of this API will not be added to this list. Implementations of this API + can only populate Route status for the Gateways/parent resources they are + responsible for. + + A maximum of 32 Gateways will be represented in this list. An empty list + means the route has not been attached to any Gateway. + items: + description: |- + RouteParentStatus describes the status of a route with respect to an + associated Parent. + properties: + conditions: + description: |- + Conditions describes the status of the route with respect to the Gateway. + Note that the route's availability is also subject to the Gateway's own + status conditions and listener status. + + If the Route's ParentRef specifies an existing Gateway that supports + Routes of this kind AND that Gateway's controller has sufficient access, + then that Gateway's controller MUST set the "Accepted" condition on the + Route, to indicate whether the route has been accepted or rejected by the + Gateway, and why. + + A Route MUST be considered "Accepted" if at least one of the Route's + rules is implemented by the Gateway. + + There are a number of cases where the "Accepted" condition may not be set + due to lack of controller visibility, that includes when: + + * The Route refers to a non-existent parent. + * The Route is of a type that the controller does not support. + * The Route is in a namespace the controller does not have access to. + items: + description: Condition contains details for one aspect of + the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, + Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + controllerName: + description: |- + ControllerName is a domain/path string that indicates the name of the + controller that wrote this status. This corresponds with the + controllerName field on GatewayClass. + + Example: "example.net/gateway-controller". + + The format of this field is DOMAIN "/" PATH, where DOMAIN and PATH are + valid Kubernetes names + (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). + + Controllers MUST populate this field when writing status. Controllers should ensure that + entries to status populated with their ControllerName are cleaned up when they are no + longer necessary. + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + parentRef: + description: |- + ParentRef corresponds with a ParentRef in the spec that this + RouteParentStatus struct describes the status of. + properties: + group: + default: gateway.networking.k8s.io + description: |- + Group is the group of the referent. + When unspecified, "gateway.networking.k8s.io" is inferred. + To set the core API group (such as for a "Service" kind referent), + Group must be explicitly set to "" (empty string). + + Support: Core + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: |- + Kind is kind of the referent. + + There are two kinds of parent resources with "Core" support: + + * Gateway (Gateway conformance profile) + * Service (Mesh conformance profile, ClusterIP Services only) + + Support for other resources is Implementation-Specific. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: |- + Name is the name of the referent. + + Support: Core + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the referent. When unspecified, this refers + to the local namespace of the Route. + + Note that there are specific rules for ParentRefs which cross namespace + boundaries. Cross-namespace references are only valid if they are explicitly + allowed by something in the namespace they are referring to. For example: + Gateway has the AllowedRoutes field, and ReferenceGrant provides a + generic way to enable any other kind of cross-namespace reference. + + + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port is the network port this Route targets. It can be interpreted + differently based on the type of parent resource. + + When the parent resource is a Gateway, this targets all listeners + listening on the specified port that also support this kind of Route(and + select this Route). It's not recommended to set `Port` unless the + networking behaviors specified in a Route must apply to a specific port + as opposed to a listener(s) whose port(s) may be changed. When both Port + and SectionName are specified, the name and port of the selected listener + must match both specified values. + + + + Implementations MAY choose to support other parent resources. + Implementations supporting other types of parent resources MUST clearly + document how/if Port is interpreted. + + For the purpose of status, an attachment is considered successful as + long as the parent resource accepts it partially. For example, Gateway + listeners can restrict which Routes can attach to them by Route kind, + namespace, or hostname. If 1 of 2 Gateway listeners accept attachment + from the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this Route, + the Route MUST be considered detached from the Gateway. + + Support: Extended + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: |- + SectionName is the name of a section within the target resource. In the + following resources, SectionName is interpreted as the following: + + * Gateway: Listener name. When both Port (experimental) and SectionName + are specified, the name and port of the selected listener must match + both specified values. + * Service: Port name. When both Port (experimental) and SectionName + are specified, the name and port of the selected listener must match + both specified values. + + Implementations MAY choose to support attaching Routes to other resources. + If that is the case, they MUST clearly document how SectionName is + interpreted. + + When unspecified (empty string), this will reference the entire resource. + For the purpose of status, an attachment is considered successful if at + least one section in the parent resource accepts it. For example, Gateway + listeners can restrict which Routes can attach to them by Route kind, + namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from + the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this Route, the + Route MUST be considered detached from the Gateway. + + Support: Core + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + required: + - controllerName + - parentRef + type: object + maxItems: 32 + type: array + required: + - parents + type: object + required: + - spec + type: object + served: true + storage: false + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: null + storedVersions: null \ No newline at end of file diff --git a/test/crds/serving.kserve.io_inferenceservices.yaml b/test/crds/serving.kserve.io_inferenceservices.yaml index 732fc5bd602..1ba11b7c142 100644 --- a/test/crds/serving.kserve.io_inferenceservices.yaml +++ b/test/crds/serving.kserve.io_inferenceservices.yaml @@ -1001,6 +1001,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -1335,10 +1337,12 @@ spec: diskURI: type: string fsType: + default: ext4 type: string kind: type: string readOnly: + default: false type: boolean required: - diskName @@ -1698,6 +1702,13 @@ spec: required: - path type: object + image: + properties: + pullPolicy: + type: string + reference: + type: string + type: object iscsi: properties: chapAuthDiscovery: @@ -1711,6 +1722,7 @@ spec: iqn: type: string iscsiInterface: + default: default type: string lun: format: int32 @@ -1959,6 +1971,7 @@ spec: image: type: string keyring: + default: /etc/ceph/keyring type: string monitors: items: @@ -1966,6 +1979,7 @@ spec: type: array x-kubernetes-list-type: atomic pool: + default: rbd type: string readOnly: type: boolean @@ -1977,6 +1991,7 @@ spec: type: object x-kubernetes-map-type: atomic user: + default: admin type: string required: - image @@ -1985,6 +2000,7 @@ spec: scaleIO: properties: fsType: + default: xfs type: string gateway: type: string @@ -2002,6 +2018,7 @@ spec: sslEnabled: type: boolean storageMode: + default: ThinProvisioned type: string storagePool: type: string @@ -2963,6 +2980,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -3263,10 +3282,12 @@ spec: diskURI: type: string fsType: + default: ext4 type: string kind: type: string readOnly: + default: false type: boolean required: - diskName @@ -3626,6 +3647,13 @@ spec: required: - path type: object + image: + properties: + pullPolicy: + type: string + reference: + type: string + type: object iscsi: properties: chapAuthDiscovery: @@ -3639,6 +3667,7 @@ spec: iqn: type: string iscsiInterface: + default: default type: string lun: format: int32 @@ -3887,6 +3916,7 @@ spec: image: type: string keyring: + default: /etc/ceph/keyring type: string monitors: items: @@ -3894,6 +3924,7 @@ spec: type: array x-kubernetes-list-type: atomic pool: + default: rbd type: string readOnly: type: boolean @@ -3905,6 +3936,7 @@ spec: type: object x-kubernetes-map-type: atomic user: + default: admin type: string required: - image @@ -3913,6 +3945,7 @@ spec: scaleIO: properties: fsType: + default: xfs type: string gateway: type: string @@ -3930,6 +3963,7 @@ spec: sslEnabled: type: boolean storageMode: + default: ThinProvisioned type: string storagePool: type: string @@ -4487,6 +4521,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -5220,8 +5256,10 @@ spec: type: object type: object maxReplicas: + format: int32 type: integer minReplicas: + format: int32 type: integer nodes: additionalProperties: @@ -5269,6 +5307,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -5301,6 +5341,7 @@ spec: - rps type: string scaleTarget: + format: int32 type: integer timeout: format: int64 @@ -6283,6 +6324,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -6990,6 +7033,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -7746,6 +7791,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -8001,8 +8048,10 @@ spec: type: string type: object maxReplicas: + format: int32 type: integer minReplicas: + format: int32 type: integer nodeName: type: string @@ -8045,13 +8094,10 @@ spec: properties: name: type: string - source: - properties: - resourceClaimName: - type: string - resourceClaimTemplateName: - type: string - type: object + resourceClaimName: + type: string + resourceClaimTemplateName: + type: string required: - name type: object @@ -8059,6 +8105,39 @@ spec: x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object restartPolicy: type: string runtimeClassName: @@ -8071,6 +8150,7 @@ spec: - rps type: string scaleTarget: + format: int32 type: integer schedulerName: type: string @@ -8110,6 +8190,8 @@ spec: runAsUser: format: int64 type: integer + seLinuxChangePolicy: + type: string seLinuxOptions: properties: level: @@ -8136,6 +8218,8 @@ spec: type: integer type: array x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + type: string sysctls: items: properties: @@ -8277,10 +8361,12 @@ spec: diskURI: type: string fsType: + default: ext4 type: string kind: type: string readOnly: + default: false type: boolean required: - diskName @@ -8640,6 +8726,13 @@ spec: required: - path type: object + image: + properties: + pullPolicy: + type: string + reference: + type: string + type: object iscsi: properties: chapAuthDiscovery: @@ -8653,6 +8746,7 @@ spec: iqn: type: string iscsiInterface: + default: default type: string lun: format: int32 @@ -8901,6 +8995,7 @@ spec: image: type: string keyring: + default: /etc/ceph/keyring type: string monitors: items: @@ -8908,6 +9003,7 @@ spec: type: array x-kubernetes-list-type: atomic pool: + default: rbd type: string readOnly: type: boolean @@ -8919,6 +9015,7 @@ spec: type: object x-kubernetes-map-type: atomic user: + default: admin type: string required: - image @@ -8927,6 +9024,7 @@ spec: scaleIO: properties: fsType: + default: xfs type: string gateway: type: string @@ -8944,6 +9042,7 @@ spec: sslEnabled: type: boolean storageMode: + default: ThinProvisioned type: string storagePool: type: string @@ -9922,6 +10021,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -10662,6 +10763,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -11363,6 +11466,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -12038,6 +12143,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -12303,8 +12410,10 @@ spec: type: string type: object maxReplicas: + format: int32 type: integer minReplicas: + format: int32 type: integer model: properties: @@ -12751,6 +12860,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -13445,6 +13556,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -14143,6 +14256,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -14828,6 +14943,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -15520,6 +15637,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -15783,13 +15902,10 @@ spec: properties: name: type: string - source: - properties: - resourceClaimName: - type: string - resourceClaimTemplateName: - type: string - type: object + resourceClaimName: + type: string + resourceClaimTemplateName: + type: string required: - name type: object @@ -15797,6 +15913,39 @@ spec: x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object restartPolicy: type: string runtimeClassName: @@ -15809,6 +15958,7 @@ spec: - rps type: string scaleTarget: + format: int32 type: integer schedulerName: type: string @@ -15848,6 +15998,8 @@ spec: runAsUser: format: int64 type: integer + seLinuxChangePolicy: + type: string seLinuxOptions: properties: level: @@ -15874,6 +16026,8 @@ spec: type: integer type: array x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + type: string sysctls: items: properties: @@ -16343,6 +16497,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -17030,6 +17186,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -17795,6 +17953,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -18070,10 +18230,12 @@ spec: diskURI: type: string fsType: + default: ext4 type: string kind: type: string readOnly: + default: false type: boolean required: - diskName @@ -18433,6 +18595,13 @@ spec: required: - path type: object + image: + properties: + pullPolicy: + type: string + reference: + type: string + type: object iscsi: properties: chapAuthDiscovery: @@ -18446,6 +18615,7 @@ spec: iqn: type: string iscsiInterface: + default: default type: string lun: format: int32 @@ -18694,6 +18864,7 @@ spec: image: type: string keyring: + default: /etc/ceph/keyring type: string monitors: items: @@ -18701,6 +18872,7 @@ spec: type: array x-kubernetes-list-type: atomic pool: + default: rbd type: string readOnly: type: boolean @@ -18712,6 +18884,7 @@ spec: type: object x-kubernetes-map-type: atomic user: + default: admin type: string required: - image @@ -18720,6 +18893,7 @@ spec: scaleIO: properties: fsType: + default: xfs type: string gateway: type: string @@ -18737,6 +18911,7 @@ spec: sslEnabled: type: boolean storageMode: + default: ThinProvisioned type: string storagePool: type: string @@ -19699,6 +19874,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -20404,6 +20581,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -21117,6 +21296,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -21397,13 +21578,10 @@ spec: properties: name: type: string - source: - properties: - resourceClaimName: - type: string - resourceClaimTemplateName: - type: string - type: object + resourceClaimName: + type: string + resourceClaimTemplateName: + type: string required: - name type: object @@ -21411,6 +21589,39 @@ spec: x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object restartPolicy: type: string runtimeClassName: @@ -21453,6 +21664,8 @@ spec: runAsUser: format: int64 type: integer + seLinuxChangePolicy: + type: string seLinuxOptions: properties: level: @@ -21479,6 +21692,8 @@ spec: type: integer type: array x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + type: string sysctls: items: properties: @@ -21619,10 +21834,12 @@ spec: diskURI: type: string fsType: + default: ext4 type: string kind: type: string readOnly: + default: false type: boolean required: - diskName @@ -21982,6 +22199,13 @@ spec: required: - path type: object + image: + properties: + pullPolicy: + type: string + reference: + type: string + type: object iscsi: properties: chapAuthDiscovery: @@ -21995,6 +22219,7 @@ spec: iqn: type: string iscsiInterface: + default: default type: string lun: format: int32 @@ -22243,6 +22468,7 @@ spec: image: type: string keyring: + default: /etc/ceph/keyring type: string monitors: items: @@ -22250,6 +22476,7 @@ spec: type: array x-kubernetes-list-type: atomic pool: + default: rbd type: string readOnly: type: boolean @@ -22261,6 +22488,7 @@ spec: type: object x-kubernetes-map-type: atomic user: + default: admin type: string required: - image @@ -22269,6 +22497,7 @@ spec: scaleIO: properties: fsType: + default: xfs type: string gateway: type: string @@ -22286,6 +22515,7 @@ spec: sslEnabled: type: boolean storageMode: + default: ThinProvisioned type: string storagePool: type: string @@ -22796,6 +23026,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -23950,6 +24182,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -24706,6 +24940,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -24961,8 +25197,10 @@ spec: type: string type: object maxReplicas: + format: int32 type: integer minReplicas: + format: int32 type: integer nodeName: type: string @@ -25005,13 +25243,10 @@ spec: properties: name: type: string - source: - properties: - resourceClaimName: - type: string - resourceClaimTemplateName: - type: string - type: object + resourceClaimName: + type: string + resourceClaimTemplateName: + type: string required: - name type: object @@ -25019,6 +25254,39 @@ spec: x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object restartPolicy: type: string runtimeClassName: @@ -25031,6 +25299,7 @@ spec: - rps type: string scaleTarget: + format: int32 type: integer schedulerName: type: string @@ -25070,6 +25339,8 @@ spec: runAsUser: format: int64 type: integer + seLinuxChangePolicy: + type: string seLinuxOptions: properties: level: @@ -25096,6 +25367,8 @@ spec: type: integer type: array x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + type: string sysctls: items: properties: @@ -25237,10 +25510,12 @@ spec: diskURI: type: string fsType: + default: ext4 type: string kind: type: string readOnly: + default: false type: boolean required: - diskName @@ -25600,6 +25875,13 @@ spec: required: - path type: object + image: + properties: + pullPolicy: + type: string + reference: + type: string + type: object iscsi: properties: chapAuthDiscovery: @@ -25613,6 +25895,7 @@ spec: iqn: type: string iscsiInterface: + default: default type: string lun: format: int32 @@ -25861,6 +26144,7 @@ spec: image: type: string keyring: + default: /etc/ceph/keyring type: string monitors: items: @@ -25868,6 +26152,7 @@ spec: type: array x-kubernetes-list-type: atomic pool: + default: rbd type: string readOnly: type: boolean @@ -25879,6 +26164,7 @@ spec: type: object x-kubernetes-map-type: atomic user: + default: admin type: string required: - image @@ -25887,6 +26173,7 @@ spec: scaleIO: properties: fsType: + default: xfs type: string gateway: type: string @@ -25904,6 +26191,7 @@ spec: sslEnabled: type: boolean storageMode: + default: ThinProvisioned type: string storagePool: type: string @@ -26188,7 +26476,6 @@ spec: nodeGroups: items: type: string - maxItems: 1 minItems: 1 type: array sourceModelUri: @@ -26384,10 +26671,12 @@ spec: diskURI: type: string fsType: + default: ext4 type: string kind: type: string readOnly: + default: false type: boolean required: - diskName @@ -26636,6 +26925,7 @@ spec: iqn: type: string iscsiInterface: + default: default type: string lun: format: int32 @@ -26787,6 +27077,7 @@ spec: image: type: string keyring: + default: /etc/ceph/keyring type: string monitors: items: @@ -26794,6 +27085,7 @@ spec: type: array x-kubernetes-list-type: atomic pool: + default: rbd type: string readOnly: type: boolean @@ -26806,6 +27098,7 @@ spec: type: object x-kubernetes-map-type: atomic user: + default: admin type: string required: - image @@ -26814,6 +27107,7 @@ spec: scaleIO: properties: fsType: + default: xfs type: string gateway: type: string @@ -26832,6 +27126,7 @@ spec: sslEnabled: type: boolean storageMode: + default: ThinProvisioned type: string storagePool: type: string @@ -27987,6 +28282,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -28321,10 +28618,12 @@ spec: diskURI: type: string fsType: + default: ext4 type: string kind: type: string readOnly: + default: false type: boolean required: - diskName @@ -28684,6 +28983,13 @@ spec: required: - path type: object + image: + properties: + pullPolicy: + type: string + reference: + type: string + type: object iscsi: properties: chapAuthDiscovery: @@ -28697,6 +29003,7 @@ spec: iqn: type: string iscsiInterface: + default: default type: string lun: format: int32 @@ -28945,6 +29252,7 @@ spec: image: type: string keyring: + default: /etc/ceph/keyring type: string monitors: items: @@ -28952,6 +29260,7 @@ spec: type: array x-kubernetes-list-type: atomic pool: + default: rbd type: string readOnly: type: boolean @@ -28963,6 +29272,7 @@ spec: type: object x-kubernetes-map-type: atomic user: + default: admin type: string required: - image @@ -28971,6 +29281,7 @@ spec: scaleIO: properties: fsType: + default: xfs type: string gateway: type: string @@ -28988,6 +29299,7 @@ spec: sslEnabled: type: boolean storageMode: + default: ThinProvisioned type: string storagePool: type: string @@ -29949,6 +30261,8 @@ spec: properties: name: type: string + request: + type: string required: - name type: object @@ -30249,10 +30563,12 @@ spec: diskURI: type: string fsType: + default: ext4 type: string kind: type: string readOnly: + default: false type: boolean required: - diskName @@ -30612,6 +30928,13 @@ spec: required: - path type: object + image: + properties: + pullPolicy: + type: string + reference: + type: string + type: object iscsi: properties: chapAuthDiscovery: @@ -30625,6 +30948,7 @@ spec: iqn: type: string iscsiInterface: + default: default type: string lun: format: int32 @@ -30873,6 +31197,7 @@ spec: image: type: string keyring: + default: /etc/ceph/keyring type: string monitors: items: @@ -30880,6 +31205,7 @@ spec: type: array x-kubernetes-list-type: atomic pool: + default: rbd type: string readOnly: type: boolean @@ -30891,6 +31217,7 @@ spec: type: object x-kubernetes-map-type: atomic user: + default: admin type: string required: - image @@ -30899,6 +31226,7 @@ spec: scaleIO: properties: fsType: + default: xfs type: string gateway: type: string @@ -30916,6 +31244,7 @@ spec: sslEnabled: type: boolean storageMode: + default: ThinProvisioned type: string storagePool: type: string diff --git a/test/e2e/batcher/test_batcher.py b/test/e2e/batcher/test_batcher.py index a076e1e0658..5c3a4084593 100644 --- a/test/e2e/batcher/test_batcher.py +++ b/test/e2e/batcher/test_batcher.py @@ -50,7 +50,7 @@ async def test_batcher(rest_v1_client): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), diff --git a/test/e2e/batcher/test_batcher_custom_port.py b/test/e2e/batcher/test_batcher_custom_port.py index 270b6de7913..0b7b1f90553 100644 --- a/test/e2e/batcher/test_batcher_custom_port.py +++ b/test/e2e/batcher/test_batcher_custom_port.py @@ -54,7 +54,7 @@ async def test_batcher_custom_port(rest_v1_client): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), diff --git a/test/e2e/batcher/test_raw_batcher.py b/test/e2e/batcher/test_raw_batcher.py index aeda31a7536..05b946d07e2 100644 --- a/test/e2e/batcher/test_raw_batcher.py +++ b/test/e2e/batcher/test_raw_batcher.py @@ -11,6 +11,7 @@ # limitations under the License. import os +import uuid from kubernetes import client from kserve import KServeClient @@ -31,8 +32,9 @@ @pytest.mark.raw @pytest.mark.asyncio(scope="session") -async def test_batcher_raw(rest_v1_client): - service_name = "isvc-raw-sklearn-batcher" +async def test_batcher_raw(rest_v1_client, network_layer): + suffix = str(uuid.uuid4())[1:6] + service_name = "isvc-raw-sklearn-batcher-" + suffix annotations = dict() annotations["serving.kserve.io/deploymentMode"] = "RawDeployment" @@ -54,7 +56,7 @@ async def test_batcher_raw(rest_v1_client): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE, @@ -83,7 +85,11 @@ async def test_batcher_raw(rest_v1_client): print(pod) raise e results = await predict_isvc( - rest_v1_client, service_name, "./data/iris_batch_input.json", is_batch=True + rest_v1_client, + service_name, + "./data/iris_batch_input.json", + is_batch=True, + network_layer=network_layer, ) assert all(x == results[0] for x in results) kserve_client.delete(service_name, KSERVE_TEST_NAMESPACE) diff --git a/test/e2e/common/utils.py b/test/e2e/common/utils.py index 2f9a8324b9c..72eaca81df9 100644 --- a/test/e2e/common/utils.py +++ b/test/e2e/common/utils.py @@ -39,8 +39,7 @@ STORAGE_URI_ENV = "STORAGE_URI" -def grpc_client(host): - cluster_ip = get_cluster_ip() +def grpc_client(host, cluster_ip): if ":" not in cluster_ip: cluster_ip = cluster_ip + ":80" logger.info("Cluster IP: %s", cluster_ip) @@ -62,6 +61,7 @@ async def predict_isvc( version=constants.KSERVE_V1BETA1_VERSION, model_name=None, is_batch=False, + network_layer: str = "istio", ) -> Union[InferResponse, Dict, List[Union[Dict, InferResponse]]]: kfs_client = KServeClient( config_file=os.environ.get("KUBECONFIG", "~/.kube/config") @@ -71,7 +71,7 @@ async def predict_isvc( namespace=KSERVE_TEST_NAMESPACE, version=version, ) - scheme, cluster_ip, host, path = get_isvc_endpoint(isvc) + scheme, cluster_ip, host, path = get_isvc_endpoint(isvc, network_layer) if model_name is None: model_name = service_name base_url = f"{scheme}://{cluster_ip}{path}" @@ -158,6 +158,7 @@ async def predict_ig( ig_name, input_path, version=constants.KSERVE_V1ALPHA1_VERSION, + network_layer: str = "istio", ) -> Union[InferResponse, Dict]: kserve_client = KServeClient( config_file=os.environ.get("KUBECONFIG", "~/.kube/config") @@ -167,22 +168,23 @@ async def predict_ig( namespace=KSERVE_TEST_NAMESPACE, version=version, ) - scheme, cluster_ip, host, _ = get_isvc_endpoint(ig) + scheme, cluster_ip, host, _ = get_isvc_endpoint(ig, network_layer) url = f"{scheme}://{cluster_ip}" return await predict(client, url, host, input_path, is_graph=True) -async def explain(service_name, input_path): - res = await explain_response(service_name, input_path) - return res["data"]["precision"] - - -async def explain_art(client, service_name, input_path): - res = await explain_response(client, service_name, input_path) +async def explain_art( + client, service_name, input_path, network_layer: str = "istio" +) -> Dict: + res = await explain_response( + client, service_name, input_path, network_layer=network_layer + ) return res["explanations"]["adversarial_prediction"] -async def explain_response(client, service_name, input_path) -> Dict: +async def explain_response( + client, service_name, input_path, network_layer: str +) -> Dict: kfs_client = KServeClient( config_file=os.environ.get("KUBECONFIG", "~/.kube/config") ) @@ -191,7 +193,7 @@ async def explain_response(client, service_name, input_path) -> Dict: namespace=KSERVE_TEST_NAMESPACE, version=constants.KSERVE_V1BETA1_VERSION, ) - scheme, cluster_ip, host, path = get_isvc_endpoint(isvc) + scheme, cluster_ip, host, path = get_isvc_endpoint(isvc, network_layer) url = f"{scheme}://{cluster_ip}{path}" headers = {"Host": host} with open(input_path) as json_file: @@ -227,11 +229,24 @@ async def explain_response(client, service_name, input_path) -> Dict: return response -def get_cluster_ip(name="istio-ingressgateway", namespace="istio-system"): +def get_cluster_ip(namespace="istio-system", labels: dict = None): cluster_ip = os.environ.get("KSERVE_INGRESS_HOST_PORT") if cluster_ip is None: api_instance = k8s_client.CoreV1Api(k8s_client.ApiClient()) - service = api_instance.read_namespaced_service(name, namespace) + if labels is None: + labels = { + "app": "istio-ingressgateway", + "istio": "ingressgateway", + } + label_selector = ",".join([f"{key}={value}" for key, value in labels.items()]) + services = api_instance.list_namespaced_service( + namespace, label_selector=label_selector + ) + if services.items: + service = services.items[0] + else: + raise RuntimeError(f"No service found with labels: {labels}") + if service.status.load_balancer.ingress is None: cluster_ip = service.spec.cluster_ip else: @@ -248,6 +263,7 @@ async def predict_grpc( parameters=None, version=constants.KSERVE_V1BETA1_VERSION, model_name=None, + network_layer: str = "istio", ) -> InferResponse: kfs_client = KServeClient( config_file=os.environ.get("KUBECONFIG", "~/.kube/config") @@ -257,11 +273,11 @@ async def predict_grpc( namespace=KSERVE_TEST_NAMESPACE, version=version, ) - _, _, host, _ = get_isvc_endpoint(isvc) + _, cluster_ip, host, _ = get_isvc_endpoint(isvc, network_layer) if model_name is None: model_name = service_name - client = grpc_client(host) + client = grpc_client(host, cluster_ip) response = await client.infer( InferRequest.from_grpc( @@ -292,14 +308,26 @@ async def predict_modelmesh( return response -def get_isvc_endpoint(isvc): +def get_isvc_endpoint(isvc, network_layer: str = "istio"): scheme = urlparse(isvc["status"]["url"]).scheme host = urlparse(isvc["status"]["url"]).netloc path = urlparse(isvc["status"]["url"]).path if os.environ.get("CI_USE_ISVC_HOST") == "1": cluster_ip = host - else: + elif network_layer == "istio" or network_layer == "istio-ingress": cluster_ip = get_cluster_ip() + elif network_layer == "envoy-gatewayapi": + cluster_ip = get_cluster_ip( + namespace="envoy-gateway-system", + labels={"serving.kserve.io/gateway": "kserve-ingress-gateway"}, + ) + elif network_layer == "istio-gatewayapi": + cluster_ip = get_cluster_ip( + namespace="kserve", + labels={"serving.kserve.io/gateway": "kserve-ingress-gateway"}, + ) + else: + raise ValueError(f"Unknown network layer {network_layer}") return scheme, cluster_ip, host, path @@ -308,6 +336,24 @@ def generate( input_json, version=constants.KSERVE_V1BETA1_VERSION, chat_completions=True, +): + url_suffix = "v1/chat/completions" if chat_completions else "v1/completions" + return _openai_request(service_name, input_json, version, url_suffix) + + +def embed( + service_name, + input_json, + version=constants.KSERVE_V1BETA1_VERSION, +): + return _openai_request(service_name, input_json, version, "v1/embeddings") + + +def _openai_request( + service_name, + input_json, + version=constants.KSERVE_V1BETA1_VERSION, + url_suffix="", ): with open(input_json) as json_file: data = json.load(json_file) @@ -323,10 +369,7 @@ def generate( scheme, cluster_ip, host, path = get_isvc_endpoint(isvc) headers = {"Host": host, "Content-Type": "application/json"} - if chat_completions: - url = f"{scheme}://{cluster_ip}{path}/openai/v1/chat/completions" - else: - url = f"{scheme}://{cluster_ip}{path}/openai/v1/completions" + url = f"{scheme}://{cluster_ip}{path}/openai/{url_suffix}" logger.info("Sending Header = %s", headers) logger.info("Sending url = %s", url) logger.info("Sending request data: %s", data) @@ -344,7 +387,11 @@ def generate( def is_model_ready( - rest_client, service_name, model_name, version=constants.KSERVE_V1BETA1_VERSION + rest_client, + service_name, + model_name, + version=constants.KSERVE_V1BETA1_VERSION, + network_layer: str = "istio", ): kfs_client = KServeClient( config_file=os.environ.get("KUBECONFIG", "~/.kube/config") @@ -354,7 +401,7 @@ def is_model_ready( namespace=KSERVE_TEST_NAMESPACE, version=version, ) - scheme, cluster_ip, host, path = get_isvc_endpoint(isvc) + scheme, cluster_ip, host, path = get_isvc_endpoint(isvc, network_layer) if model_name is None: model_name = service_name base_url = f"{scheme}://{cluster_ip}{path}" diff --git a/test/e2e/conftest.py b/test/e2e/conftest.py index 4ab42d06cb4..2ede547ed21 100644 --- a/test/e2e/conftest.py +++ b/test/e2e/conftest.py @@ -60,3 +60,17 @@ async def rest_v2_client(): ) yield v2_client await v2_client.close() + + +def pytest_addoption(parser): + parser.addoption( + "--network-layer", + default="istio", + type=str, + help="Network layer to used for testing. Default is istio. Allowed values are istio-ingress, envoy-gatewayapi, istio-gatewayapi", + ) + + +@pytest.fixture(scope="session") +def network_layer(request): + return request.config.getoption("--network-layer") diff --git a/test/e2e/custom/test_custom_model_grpc.py b/test/e2e/custom/test_custom_model_grpc.py index 400a3dd5fd4..379a505b582 100644 --- a/test/e2e/custom/test_custom_model_grpc.py +++ b/test/e2e/custom/test_custom_model_grpc.py @@ -14,6 +14,7 @@ import base64 import json import os +import uuid import numpy as np import pytest @@ -64,7 +65,7 @@ async def test_custom_model_grpc(): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), @@ -144,7 +145,7 @@ async def test_predictor_grpc_with_transformer_grpc(): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), @@ -226,7 +227,7 @@ async def test_predictor_grpc_with_transformer_http(rest_v2_client): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), @@ -320,7 +321,7 @@ async def test_predictor_rest_with_transformer_rest(rest_v2_client): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), @@ -368,3 +369,92 @@ async def test_predictor_rest_with_transformer_rest(rest_v2_client): points = ["%.3f" % point for point in list(res.outputs[0].data)] assert points == ["14.976", "14.037", "13.966", "12.252", "12.086"] kserve_client.delete(service_name, KSERVE_TEST_NAMESPACE) + + +@pytest.mark.raw +@pytest.mark.asyncio(scope="session") +async def test_predictor_grpc_with_transformer_grpc_raw(network_layer): + suffix = str(uuid.uuid4())[1:6] + service_name = "model-grpc-trans-grpc-raw-" + suffix + model_name = "custom-model" + + predictor = V1beta1PredictorSpec( + containers=[ + V1Container( + name="kserve-container", + image="kserve/custom-model-grpc:" + os.environ.get("GITHUB_SHA"), + resources=V1ResourceRequirements( + requests={"cpu": "50m", "memory": "128Mi"}, + limits={"cpu": "100m", "memory": "1Gi"}, + ), + ports=[ + V1ContainerPort( + container_port=8081, name="h2c-port", protocol="TCP" + ) + ], + args=["--model_name", model_name], + ) + ] + ) + + transformer = V1beta1TransformerSpec( + containers=[ + V1Container( + name="kserve-container", + image="kserve/custom-image-transformer-grpc:" + + os.environ.get("GITHUB_SHA"), + resources=V1ResourceRequirements( + requests={"cpu": "50m", "memory": "128Mi"}, + limits={"cpu": "100m", "memory": "1Gi"}, + ), + ports=[ + V1ContainerPort( + container_port=8081, name="grpc-port", protocol="TCP" + ) + ], + args=["--model_name", model_name, "--predictor_protocol", "grpc-v2"], + ) + ] + ) + + isvc = V1beta1InferenceService( + api_version=constants.KSERVE_V1BETA1, + kind=constants.KSERVE_KIND_INFERENCESERVICE, + metadata=client.V1ObjectMeta( + name=service_name, + namespace=KSERVE_TEST_NAMESPACE, + annotations={"serving.kserve.io/deploymentMode": "RawDeployment"}, + ), + spec=V1beta1InferenceServiceSpec(predictor=predictor, transformer=transformer), + ) + + kserve_client = KServeClient( + config_file=os.environ.get("KUBECONFIG", "~/.kube/config") + ) + kserve_client.create(isvc) + kserve_client.wait_isvc_ready(service_name, namespace=KSERVE_TEST_NAMESPACE) + + json_file = open("./data/custom_model_input.json") + data = json.load(json_file) + payload = [ + { + "name": "input-0", + "shape": [], + "datatype": "BYTES", + "contents": { + "bytes_contents": [ + base64.b64decode(data["instances"][0]["image"]["b64"]) + ] + }, + } + ] + response = await predict_grpc( + service_name=service_name, + payload=payload, + model_name=model_name, + network_layer=network_layer, + ) + fields = response.outputs[0].data + points = ["%.3f" % (point) for point in list(fields)] + assert points == ["14.976", "14.037", "13.966", "12.252", "12.086"] + kserve_client.delete(service_name, KSERVE_TEST_NAMESPACE) diff --git a/test/e2e/custom/test_ray.py b/test/e2e/custom/test_ray.py index 6e243982c24..915d0e50735 100644 --- a/test/e2e/custom/test_ray.py +++ b/test/e2e/custom/test_ray.py @@ -51,7 +51,7 @@ async def test_custom_model_http_ray(rest_v1_client): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), diff --git a/test/e2e/data/mm_sklearn_input.json b/test/e2e/data/mm_sklearn_input.json deleted file mode 100644 index 8f41ef50f7c..00000000000 --- a/test/e2e/data/mm_sklearn_input.json +++ /dev/null @@ -1,78 +0,0 @@ -{ - "inputs": [ - { - "name": "predict", - "shape": [ - 1, - 64 - ], - "datatype": "FP32", - "data": [ - 0.0, - 0.0, - 1.0, - 11.0, - 14.0, - 15.0, - 3.0, - 0.0, - 0.0, - 1.0, - 13.0, - 16.0, - 12.0, - 16.0, - 8.0, - 0.0, - 0.0, - 8.0, - 16.0, - 4.0, - 6.0, - 16.0, - 5.0, - 0.0, - 0.0, - 5.0, - 15.0, - 11.0, - 13.0, - 14.0, - 0.0, - 0.0, - 0.0, - 0.0, - 2.0, - 12.0, - 16.0, - 13.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 13.0, - 16.0, - 16.0, - 6.0, - 0.0, - 0.0, - 0.0, - 0.0, - 16.0, - 16.0, - 16.0, - 7.0, - 0.0, - 0.0, - 0.0, - 0.0, - 11.0, - 13.0, - 12.0, - 1.0, - 0.0 - ] - } - ] -} diff --git a/test/e2e/data/text_embedding_input_openai_base64.json b/test/e2e/data/text_embedding_input_openai_base64.json new file mode 100644 index 00000000000..e0febd4120e --- /dev/null +++ b/test/e2e/data/text_embedding_input_openai_base64.json @@ -0,0 +1,5 @@ +{ + "input": ["This is an example sentence."], + "model": "hf-text-embedding-openai", + "encoding_format": "base64" +} diff --git a/test/e2e/data/text_embedding_input_openai_float.json b/test/e2e/data/text_embedding_input_openai_float.json new file mode 100644 index 00000000000..4eb7fb177bb --- /dev/null +++ b/test/e2e/data/text_embedding_input_openai_float.json @@ -0,0 +1,5 @@ +{ + "input": "This is an example sentence.", + "model": "hf-text-embedding-openai", + "encoding_format": "float" +} diff --git a/test/e2e/explainer/test_art_explainer.py b/test/e2e/explainer/test_art_explainer.py index f83b1ac905c..d92d1e45eb3 100644 --- a/test/e2e/explainer/test_art_explainer.py +++ b/test/e2e/explainer/test_art_explainer.py @@ -14,8 +14,11 @@ import logging import os +import uuid from kubernetes import client +from kubernetes.client import V1ResourceRequirements +import pytest from kserve import KServeClient from kserve import constants @@ -25,8 +28,6 @@ from kserve import V1beta1SKLearnSpec from kserve import V1beta1ARTExplainerSpec from kserve import V1beta1InferenceService -from kubernetes.client import V1ResourceRequirements -import pytest from ..common.utils import predict_isvc from ..common.utils import explain_art @@ -42,7 +43,7 @@ async def test_tabular_explainer(rest_v1_client): service_name = "art-explainer" isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), @@ -54,7 +55,8 @@ async def test_tabular_explainer(rest_v1_client): requests={"cpu": "10m", "memory": "128Mi"}, limits={"cpu": "100m", "memory": "256Mi"}, ), - ) + ), + timeout=180, ), explainer=V1beta1ExplainerSpec( min_replicas=1, @@ -67,6 +69,7 @@ async def test_tabular_explainer(rest_v1_client): ), config={"nb_classes": "10"}, ), + timeout=180, ), ), ) @@ -104,3 +107,84 @@ async def test_tabular_explainer(rest_v1_client): ) assert adv_prediction != 3 kserve_client.delete(service_name, KSERVE_TEST_NAMESPACE) + + +@pytest.mark.raw +@pytest.mark.asyncio(scope="session") +async def test_raw_tabular_explainer(rest_v1_client, network_layer): + suffix = str(uuid.uuid4())[1:6] + service_name = "art-explainer-raw-" + suffix + isvc = V1beta1InferenceService( + api_version=constants.KSERVE_V1BETA1, + kind=constants.KSERVE_KIND_INFERENCESERVICE, + metadata=client.V1ObjectMeta( + name=service_name, + namespace=KSERVE_TEST_NAMESPACE, + annotations={"serving.kserve.io/deploymentMode": "RawDeployment"}, + ), + spec=V1beta1InferenceServiceSpec( + predictor=V1beta1PredictorSpec( + sklearn=V1beta1SKLearnSpec( + storage_uri="gs://kfserving-examples/models/sklearn/mnist/art", + resources=V1ResourceRequirements( + requests={"cpu": "10m", "memory": "128Mi"}, + limits={"cpu": "100m", "memory": "256Mi"}, + ), + ), + timeout=180, + ), + explainer=V1beta1ExplainerSpec( + min_replicas=1, + art=V1beta1ARTExplainerSpec( + type="SquareAttack", + name="explainer", + resources=V1ResourceRequirements( + requests={"cpu": "10m", "memory": "256Mi"}, + limits={"cpu": "100m", "memory": "512Mi"}, + ), + config={"nb_classes": "10"}, + ), + timeout=180, + ), + ), + ) + + kserve_client.create(isvc) + try: + kserve_client.wait_isvc_ready( + service_name, namespace=KSERVE_TEST_NAMESPACE, timeout_seconds=720 + ) + except RuntimeError as e: + logging.info( + kserve_client.api_instance.get_namespaced_custom_object( + "serving.knative.dev", + "v1", + KSERVE_TEST_NAMESPACE, + "services", + service_name + "-predictor", + ) + ) + pods = kserve_client.core_api.list_namespaced_pod( + KSERVE_TEST_NAMESPACE, + label_selector="serving.kserve.io/inferenceservice={}".format(service_name), + ) + for pod in pods.items: + logging.info(pod) + raise e + + res = await predict_isvc( + rest_v1_client, + service_name, + "./data/mnist_input_bw_flat.json", + network_layer=network_layer, + ) + assert res["predictions"] == [3] + + adv_prediction = await explain_art( + rest_v1_client, + service_name, + "./data/mnist_input_bw.json", + network_layer=network_layer, + ) + assert adv_prediction != 3 + kserve_client.delete(service_name, KSERVE_TEST_NAMESPACE) diff --git a/test/e2e/graph/test_inference_graph.py b/test/e2e/graph/test_inference_graph.py index 5a39a075517..68e5c723888 100644 --- a/test/e2e/graph/test_inference_graph.py +++ b/test/e2e/graph/test_inference_graph.py @@ -64,7 +64,7 @@ async def test_inference_graph(rest_v1_client): ) sklearn_isvc_1 = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=sklearn_name_1, namespace=KSERVE_TEST_NAMESPACE ), @@ -72,7 +72,7 @@ async def test_inference_graph(rest_v1_client): ) sklearn_isvc_2 = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=sklearn_name_2, namespace=KSERVE_TEST_NAMESPACE ), @@ -91,7 +91,7 @@ async def test_inference_graph(rest_v1_client): ) xgb_isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta(name=xgb_name, namespace=KSERVE_TEST_NAMESPACE), spec=V1beta1InferenceServiceSpec(predictor=xgb_predictor), ) @@ -187,7 +187,7 @@ def construct_isvc_to_submit(service_name, image, model_name): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), @@ -924,11 +924,12 @@ async def test_ig_scenario10(rest_v1_client): @pytest.mark.raw @pytest.mark.asyncio(scope="session") -async def test_inference_graph_raw_mode(rest_v1_client): +async def test_inference_graph_raw_mode(rest_v1_client, network_layer): logger.info("Starting test test_inference_graph_raw_mode") - sklearn_name = "isvc-sklearn-graph-raw" - xgb_name = "isvc-xgboost-graph-raw" - graph_name = "model-chainer-raw" + suffix = str(uuid.uuid4())[1:6] + sklearn_name = "isvc-sklearn-graph-raw-" + suffix + xgb_name = "isvc-xgboost-graph-raw-" + suffix + graph_name = "model-chainer-raw-" + suffix annotations = dict() annotations["serving.kserve.io/deploymentMode"] = "RawDeployment" @@ -945,7 +946,7 @@ async def test_inference_graph_raw_mode(rest_v1_client): ) sklearn_isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=sklearn_name, namespace=KSERVE_TEST_NAMESPACE, @@ -966,7 +967,7 @@ async def test_inference_graph_raw_mode(rest_v1_client): ) xgb_isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=xgb_name, namespace=KSERVE_TEST_NAMESPACE, @@ -1068,6 +1069,7 @@ async def test_inference_graph_raw_mode(rest_v1_client): # rest_v1_client, # graph_name, # os.path.join(IG_TEST_RESOURCES_BASE_LOCATION, "iris_input.json"), + # network_layer=network_layer, # ) # assert res["predictions"] == [1, 1] @@ -1078,11 +1080,12 @@ async def test_inference_graph_raw_mode(rest_v1_client): @pytest.mark.raw @pytest.mark.asyncio(scope="session") -async def test_inference_graph_raw_mode_with_hpa(rest_v1_client): +async def test_inference_graph_raw_mode_with_hpa(rest_v1_client, network_layer): logger.info("Starting test test_inference_graph_raw_mode_with_hpa") - sklearn_name = "isvc-sklearn-graph-raw-hpa" - xgb_name = "isvc-xgboost-graph-raw-hpa" - graph_name = "model-chainer-raw-hpa" + suffix = str(uuid.uuid4())[1:6] + sklearn_name = "isvc-sklearn-graph-raw-hpa-" + suffix + xgb_name = "isvc-xgboost-graph-raw-hpa-" + suffix + graph_name = "model-chainer-raw-hpa-" + suffix annotations = dict() annotations["serving.kserve.io/deploymentMode"] = "RawDeployment" @@ -1103,7 +1106,7 @@ async def test_inference_graph_raw_mode_with_hpa(rest_v1_client): ) sklearn_isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=sklearn_name, namespace=KSERVE_TEST_NAMESPACE, @@ -1124,7 +1127,7 @@ async def test_inference_graph_raw_mode_with_hpa(rest_v1_client): ) xgb_isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=xgb_name, namespace=KSERVE_TEST_NAMESPACE, @@ -1231,6 +1234,7 @@ async def test_inference_graph_raw_mode_with_hpa(rest_v1_client): # rest_v1_client, # graph_name, # os.path.join(IG_TEST_RESOURCES_BASE_LOCATION, "iris_input.json"), + # network_layer=network_layer, # ) # assert res["predictions"] == [1, 1] diff --git a/test/e2e/helm/test_kserve_sklearn.py b/test/e2e/helm/test_kserve_sklearn.py index 22acd46725f..da12395a32d 100644 --- a/test/e2e/helm/test_kserve_sklearn.py +++ b/test/e2e/helm/test_kserve_sklearn.py @@ -61,7 +61,7 @@ async def test_sklearn_kserve(rest_v2_client): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), diff --git a/test/e2e/helm/test_model_mesh_sklearn.py b/test/e2e/helm/test_model_mesh_sklearn.py deleted file mode 100644 index a4ab83b0348..00000000000 --- a/test/e2e/helm/test_model_mesh_sklearn.py +++ /dev/null @@ -1,81 +0,0 @@ -# Copyright 2022 The KServe Authors. -# -# Licensed 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. - -import os - -import pytest -from kubernetes import client -from kubernetes.client import V1ResourceRequirements - -from kserve import ( - KServeClient, - V1beta1InferenceService, - V1beta1InferenceServiceSpec, - V1beta1ModelFormat, - V1beta1ModelSpec, - V1beta1PredictorSpec, - V1beta1StorageSpec, - constants, -) -from ..common.utils import predict_modelmesh - - -# TODO: Enable e2e test post ingress implementation in model mesh serving -# https://github.com/kserve/modelmesh-serving/issues/295 -# @pytest.mark.helm -@pytest.mark.skip -@pytest.mark.asyncio(scope="session") -async def test_sklearn_modelmesh(rest_v1_client): - service_name = "isvc-sklearn-modelmesh" - annotations = dict() - annotations["serving.kserve.io/deploymentMode"] = "ModelMesh" - predictor = V1beta1PredictorSpec( - min_replicas=1, - model=V1beta1ModelSpec( - model_format=V1beta1ModelFormat( - name="sklearn", - ), - resources=V1ResourceRequirements( - requests={"cpu": "50m", "memory": "128Mi"}, - limits={"cpu": "100m", "memory": "512Mi"}, - ), - storage=V1beta1StorageSpec( - key="localMinIO", path="sklearn/mnist-svm.joblib" - ), - ), - ) - - isvc = V1beta1InferenceService( - api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, - metadata=client.V1ObjectMeta(name=service_name, annotations=annotations), - spec=V1beta1InferenceServiceSpec(predictor=predictor), - ) - - kserve_client = KServeClient( - config_file=os.environ.get("KUBECONFIG", "~/.kube/config") - ) - kserve_client.create(isvc) - kserve_client.wait_isvc_ready(service_name) - pods = kserve_client.core_api.list_namespaced_pod( - "default", label_selector="name=modelmesh-serving-mlserver-1.x" - ) - - pod_name = pods.items[0].metadata.name - res = await predict_modelmesh( - rest_v1_client, service_name, "./data/mm_sklearn_input.json", pod_name - ) - assert res.outputs[0].data == [8] - - kserve_client.delete(service_name) diff --git a/test/e2e/logger/test_logger.py b/test/e2e/logger/test_logger.py index f0ebf253ef9..8c55e0eb8dc 100644 --- a/test/e2e/logger/test_logger.py +++ b/test/e2e/logger/test_logger.py @@ -52,7 +52,7 @@ async def test_kserve_logger(rest_v1_client): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta(name=msg_dumper, namespace=KSERVE_TEST_NAMESPACE), spec=V1beta1InferenceServiceSpec(predictor=predictor), ) @@ -78,7 +78,7 @@ async def test_kserve_logger(rest_v1_client): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), diff --git a/test/e2e/logger/test_raw_logger.py b/test/e2e/logger/test_raw_logger.py index e8c8fa06fc0..e347aec5e46 100644 --- a/test/e2e/logger/test_raw_logger.py +++ b/test/e2e/logger/test_raw_logger.py @@ -13,6 +13,7 @@ import asyncio import os +import uuid from kubernetes import client from kserve import ( @@ -37,11 +38,12 @@ @pytest.mark.raw @pytest.mark.asyncio(scope="session") -async def test_kserve_logger(rest_v1_client): - msg_dumper = "message-dumper-raw" +async def test_kserve_logger(rest_v1_client, network_layer): + suffix = str(uuid.uuid4())[1:6] + msg_dumper = "message-dumper-raw-" + suffix before(msg_dumper) - service_name = "isvc-logger-raw" + service_name = "isvc-logger-raw-" + suffix predictor = V1beta1PredictorSpec( min_replicas=1, logger=V1beta1LoggerSpec( @@ -61,11 +63,11 @@ async def test_kserve_logger(rest_v1_client): ), ), ) - base_test(msg_dumper, service_name, predictor, rest_v1_client) + await base_test(msg_dumper, service_name, predictor, rest_v1_client, network_layer) @pytest.mark.rawcipn -async def test_kserve_logger_cipn(rest_v1_client): +async def test_kserve_logger_cipn(rest_v1_client, network_layer): msg_dumper = "message-dumper-raw-cipn" before(msg_dumper) @@ -89,7 +91,7 @@ async def test_kserve_logger_cipn(rest_v1_client): ), ), ) - await base_test(msg_dumper, service_name, predictor, rest_v1_client) + await base_test(msg_dumper, service_name, predictor, rest_v1_client, network_layer) def before(msg_dumper): @@ -109,7 +111,7 @@ def before(msg_dumper): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=msg_dumper, namespace=KSERVE_TEST_NAMESPACE, annotations=annotations ), @@ -120,10 +122,10 @@ def before(msg_dumper): kserve_client.wait_isvc_ready(msg_dumper, namespace=KSERVE_TEST_NAMESPACE) -async def base_test(msg_dumper, service_name, predictor, rest_v1_client): +async def base_test(msg_dumper, service_name, predictor, rest_v1_client, network_layer): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE, annotations=annotations ), @@ -141,7 +143,12 @@ async def base_test(msg_dumper, service_name, predictor, rest_v1_client): for pod in pods.items: print(pod) - res = await predict_isvc(rest_v1_client, service_name, "./data/iris_input.json") + res = await predict_isvc( + rest_v1_client, + service_name, + "./data/iris_input.json", + network_layer=network_layer, + ) assert res["predictions"] == [1, 1] pods = kserve_client.core_api.list_namespaced_pod( KSERVE_TEST_NAMESPACE, diff --git a/test/e2e/modelcache/__init__.py b/test/e2e/modelcache/__init__.py new file mode 100644 index 00000000000..0cdb835ca3f --- /dev/null +++ b/test/e2e/modelcache/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2024 The KServe Authors. +# +# Licensed 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. diff --git a/test/e2e/modelcache/test_localmodelcache.py b/test/e2e/modelcache/test_localmodelcache.py new file mode 100644 index 00000000000..8291aa3c14f --- /dev/null +++ b/test/e2e/modelcache/test_localmodelcache.py @@ -0,0 +1,188 @@ +# Copyright 2024 The KServe Authors. +# +# Licensed 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. + +import asyncio +import os + +import pytest +from kubernetes import client +from kubernetes.client import ( + V1ResourceRequirements, + V1PersistentVolumeSpec, + V1LocalVolumeSource, + V1VolumeNodeAffinity, + V1PersistentVolumeClaimSpec, +) +from kubernetes.client.exceptions import ApiException + +from kserve import constants +from kserve.api.kserve_client import KServeClient +from kserve.models.v1alpha1_local_model_node_group import V1alpha1LocalModelNodeGroup +from kserve.models.v1alpha1_local_model_node_group_spec import ( + V1alpha1LocalModelNodeGroupSpec, +) +from kserve.models.v1alpha1_local_model_cache import V1alpha1LocalModelCache +from kserve.models.v1alpha1_local_model_cache_spec import V1alpha1LocalModelCacheSpec +from kserve.models.v1beta1_inference_service import V1beta1InferenceService +from kserve.models.v1beta1_inference_service_spec import V1beta1InferenceServiceSpec +from kserve.models.v1beta1_predictor_spec import V1beta1PredictorSpec +from kserve.models.v1beta1_model_spec import V1beta1ModelSpec +from kserve.models.v1beta1_model_format import V1beta1ModelFormat +from ..common.utils import KSERVE_TEST_NAMESPACE, generate + + +@pytest.mark.modelcache +@pytest.mark.asyncio(scope="session") +async def test_vllm_modelcache(): + service_name = "opt-125m-chat-modelcache-worker1" + storage_uri = "hf://facebook/opt-125m" + nodes = ["minikube-m02", "minikube-m03"] + + pv_spec = V1PersistentVolumeSpec( + access_modes=["ReadWriteOnce"], + storage_class_name="standard", + capacity={"storage": "1Gi"}, + local=V1LocalVolumeSource(path="/models"), + persistent_volume_reclaim_policy="Delete", + node_affinity=V1VolumeNodeAffinity( + required=client.V1NodeSelector( + node_selector_terms=[ + client.V1NodeSelectorTerm( + match_expressions=[ + client.V1NodeSelectorRequirement( + key="kubernetes.io/hostname", + operator="In", + values=nodes, + ) + ] + ) + ] + ) + ), + ) + pvc_spec = V1PersistentVolumeClaimSpec( + access_modes=["ReadWriteOnce"], + resources=V1ResourceRequirements(requests={"storage": "1Gi"}), + storage_class_name="standard", + ) + + node_group = V1alpha1LocalModelNodeGroup( + api_version=constants.KSERVE_V1ALPHA1, + kind=constants.KSERVE_KIND_LOCALMODELNODEGROUP, + metadata=client.V1ObjectMeta( + name="opt-125m-nodegroup", + ), + spec=V1alpha1LocalModelNodeGroupSpec( + storage_limit="1Gi", + persistent_volume_spec=pv_spec, + persistent_volume_claim_spec=pvc_spec, + ), + ) + + model_cache = V1alpha1LocalModelCache( + api_version=constants.KSERVE_V1ALPHA1, + kind=constants.KSERVE_KIND_LOCALMODELCACHE, + metadata=client.V1ObjectMeta( + name="opt-125m", + ), + spec=V1alpha1LocalModelCacheSpec( + model_size="251Mi", + node_groups=[node_group.metadata.name], + source_model_uri=storage_uri, + ), + ) + + predictor = V1beta1PredictorSpec( + min_replicas=1, + model=V1beta1ModelSpec( + model_format=V1beta1ModelFormat( + name="huggingface", + ), + args=[ + "--model_name", + "hf-opt-125m-chat", + "--tokenizer_revision", + "27dcfa74d334bc871f3234de431e71c6eeba5dd6", + "--max_model_len", + "512", + ], + resources=V1ResourceRequirements( + requests={"cpu": "2", "memory": "6Gi"}, + limits={"cpu": "2", "memory": "6Gi"}, + ), + storage_uri=storage_uri, + ), + node_selector={"kubernetes.io/hostname": nodes[0]}, + ) + + isvc = V1beta1InferenceService( + api_version=constants.KSERVE_V1BETA1, + kind=constants.KSERVE_KIND_INFERENCESERVICE, + metadata=client.V1ObjectMeta( + name=service_name, namespace=KSERVE_TEST_NAMESPACE + ), + spec=V1beta1InferenceServiceSpec(predictor=predictor), + ) + + kserve_client = KServeClient( + config_file=os.environ.get("KUBECONFIG", "~/.kube/config") + ) + kserve_client.create_local_model_node_group(node_group) + kserve_client.create_local_model_cache(model_cache) + kserve_client.wait_local_model_cache_ready(model_cache.metadata.name, nodes=nodes) + kserve_client.create(isvc) + kserve_client.wait_isvc_ready(service_name, namespace=KSERVE_TEST_NAMESPACE) + k8s_client = kserve_client.api_instance + + # Test the model is cached on the correct nodes + worker_node_1_cache = k8s_client.get_cluster_custom_object( + constants.KSERVE_GROUP, + constants.KSERVE_V1ALPHA1_VERSION, + constants.KSERVE_PLURAL_LOCALMODELNODE, + "minikube-m02", + ) + worker_node_2_cache = k8s_client.get_cluster_custom_object( + constants.KSERVE_GROUP, + constants.KSERVE_V1ALPHA1_VERSION, + constants.KSERVE_PLURAL_LOCALMODELNODE, + "minikube-m03", + ) + assert ( + worker_node_1_cache["status"]["modelStatus"][model_cache.metadata.name] + == "ModelDownloaded" + ) + assert ( + worker_node_2_cache["status"]["modelStatus"][model_cache.metadata.name] + == "ModelDownloaded" + ) + + # Test the model is not cached on the controller node + with pytest.raises(ApiException): + k8s_client.get_cluster_custom_object( + constants.KSERVE_GROUP, + constants.KSERVE_V1ALPHA1_VERSION, + constants.KSERVE_PLURAL_LOCALMODELNODE, + "minikube", + ) + + res = generate(service_name, "./data/opt_125m_input_generate.json") + assert ( + res["choices"][0]["message"]["content"] + == "I'm not sure if this is a good idea, but I'm not sure if I should be" + ) + kserve_client.delete(service_name, KSERVE_TEST_NAMESPACE) + # Wait for the isvc to be deleted to avoid modelcache still in use error when deleting the model cache + await asyncio.sleep(30) + kserve_client.delete_local_model_cache(model_cache.metadata.name) + kserve_client.delete_local_model_node_group(node_group.metadata.name) diff --git a/test/e2e/predictor/test_autoscaling.py b/test/e2e/predictor/test_autoscaling.py index 0331b279ac1..0f1a29be7d6 100644 --- a/test/e2e/predictor/test_autoscaling.py +++ b/test/e2e/predictor/test_autoscaling.py @@ -13,6 +13,7 @@ # limitations under the License. import os +import uuid import pytest from kubernetes import client @@ -53,7 +54,7 @@ async def test_sklearn_kserve_concurrency(rest_v1_client): ) isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), @@ -98,7 +99,7 @@ async def test_sklearn_kserve_rps(rest_v1_client): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), @@ -146,7 +147,7 @@ async def test_sklearn_kserve_cpu(rest_v1_client): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE, annotations=annotations ), @@ -174,8 +175,9 @@ async def test_sklearn_kserve_cpu(rest_v1_client): @pytest.mark.raw @pytest.mark.asyncio(scope="session") -async def test_sklearn_scale_raw(rest_v1_client): - service_name = "isvc-sklearn-scale-raw" +async def test_sklearn_scale_raw(rest_v1_client, network_layer): + suffix = str(uuid.uuid4())[1:6] + service_name = "isvc-sklearn-scale-raw-" + suffix predictor = V1beta1PredictorSpec( min_replicas=1, scale_metric="cpu", @@ -194,7 +196,7 @@ async def test_sklearn_scale_raw(rest_v1_client): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE, annotations=annotations ), @@ -216,7 +218,9 @@ async def test_sklearn_scale_raw(rest_v1_client): ) assert hpa_resp["items"][0]["spec"]["targetCPUUtilizationPercentage"] == 50 - res = await predict_isvc(rest_v1_client, service_name, INPUT) + res = await predict_isvc( + rest_v1_client, service_name, INPUT, network_layer=network_layer + ) assert res["predictions"] == [1, 1] kserve_client.delete(service_name, KSERVE_TEST_NAMESPACE) @@ -224,7 +228,8 @@ async def test_sklearn_scale_raw(rest_v1_client): @pytest.mark.raw @pytest.mark.asyncio(scope="session") async def test_sklearn_rolling_update(): - service_name = "isvc-sklearn-rolling-update" + suffix = str(uuid.uuid4())[1:6] + service_name = "isvc-sklearn-rolling-update-" + suffix min_replicas = 4 predictor = V1beta1PredictorSpec( min_replicas=min_replicas, @@ -244,7 +249,7 @@ async def test_sklearn_rolling_update(): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE, @@ -260,7 +265,7 @@ async def test_sklearn_rolling_update(): updated_isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE, diff --git a/test/e2e/predictor/test_canary.py b/test/e2e/predictor/test_canary.py index 90db1d891f3..249c2b97cf2 100644 --- a/test/e2e/predictor/test_canary.py +++ b/test/e2e/predictor/test_canary.py @@ -50,7 +50,7 @@ def test_canary_rollout(): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), @@ -75,7 +75,7 @@ def test_canary_rollout(): ) isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), @@ -116,7 +116,7 @@ def test_canary_rollout_runtime(): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), @@ -144,7 +144,7 @@ def test_canary_rollout_runtime(): ) isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), diff --git a/test/e2e/predictor/test_grpc.py b/test/e2e/predictor/test_grpc.py index b173f136340..425e7533d28 100644 --- a/test/e2e/predictor/test_grpc.py +++ b/test/e2e/predictor/test_grpc.py @@ -56,7 +56,7 @@ async def test_custom_model_grpc(): logger_isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta(name=msg_dumper, namespace=KSERVE_TEST_NAMESPACE), spec=V1beta1InferenceServiceSpec(predictor=logger_predictor), ) @@ -90,7 +90,7 @@ async def test_custom_model_grpc(): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), diff --git a/test/e2e/predictor/test_huggingface.py b/test/e2e/predictor/test_huggingface.py index 545efbd7af9..f569813638e 100644 --- a/test/e2e/predictor/test_huggingface.py +++ b/test/e2e/predictor/test_huggingface.py @@ -11,13 +11,14 @@ # 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. - +import base64 import os import ast import pytest from kubernetes import client from kubernetes.client import V1ResourceRequirements +import numpy as np from kserve import ( V1beta1PredictorSpec, @@ -28,7 +29,7 @@ KServeClient, ) from kserve.constants import constants -from ..common.utils import KSERVE_TEST_NAMESPACE, generate, predict_isvc +from ..common.utils import KSERVE_TEST_NAMESPACE, generate, embed, predict_isvc from .test_output import ( huggingface_text_embedding_expected_output, huggingface_sequence_classification_with_probabilities_expected_output, @@ -65,7 +66,7 @@ def test_huggingface_openai_chat_completions(): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), @@ -118,7 +119,7 @@ async def test_huggingface_v2_sequence_classification(rest_v2_client): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), @@ -166,7 +167,7 @@ async def test_huggingface_v1_fill_mask(rest_v1_client): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), @@ -219,7 +220,7 @@ async def test_huggingface_v2_token_classification(rest_v2_client): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), @@ -268,7 +269,7 @@ def test_huggingface_openai_text_2_text(): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), @@ -324,7 +325,7 @@ async def test_huggingface_v2_text_embedding(rest_v2_client): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), @@ -345,6 +346,71 @@ async def test_huggingface_v2_text_embedding(rest_v2_client): kserve_client.delete(service_name, KSERVE_TEST_NAMESPACE) +@pytest.mark.llm +@pytest.mark.asyncio(scope="session") +async def test_huggingface_openai_text_embedding(): + service_name = "hf-text-embedding-openai" + predictor = V1beta1PredictorSpec( + min_replicas=1, + model=V1beta1ModelSpec( + model_format=V1beta1ModelFormat( + name="huggingface", + ), + args=[ + "--model_id", + "sentence-transformers/all-MiniLM-L6-v2", + "--model_revision", + "8b3219a92973c328a8e22fadcfa821b5dc75636a", + "--tokenizer_revision", + "8b3219a92973c328a8e22fadcfa821b5dc75636a", + "--task", + "text_embedding", + "--backend", + "huggingface", + ], + resources=V1ResourceRequirements( + requests={"cpu": "1", "memory": "2Gi"}, + limits={"cpu": "1", "memory": "4Gi"}, + ), + ), + ) + + isvc = V1beta1InferenceService( + api_version=constants.KSERVE_V1BETA1, + kind=constants.KSERVE_KIND_INFERENCESERVICE, + metadata=client.V1ObjectMeta( + name=service_name, namespace=KSERVE_TEST_NAMESPACE + ), + spec=V1beta1InferenceServiceSpec(predictor=predictor), + ) + + kserve_client = KServeClient( + config_file=os.environ.get("KUBECONFIG", "~/.kube/config") + ) + kserve_client.create(isvc) + kserve_client.wait_isvc_ready(service_name, namespace=KSERVE_TEST_NAMESPACE) + + # Validate float output + res = embed(service_name, "./data/text_embedding_input_openai_float.json") + assert len(res["data"]) == 1 + assert res["data"][0]["embedding"] == huggingface_text_embedding_expected_output + + # Validate base64 output. Decoded as the OpenAI library: + # https://github.com/openai/openai-python/blob/v1.59.7/src/openai/resources/embeddings.py#L118-L120 + res = embed(service_name, "./data/text_embedding_input_openai_base64.json") + embedding = np.frombuffer( + base64.b64decode(res["data"][0]["embedding"]), dtype="float32" + ).tolist() + assert len(res["data"]) == 1 + assert embedding == huggingface_text_embedding_expected_output + + # Validate Token count + assert res["usage"]["prompt_tokens"] == 8 + assert res["usage"]["total_tokens"] == 8 + + kserve_client.delete(service_name, KSERVE_TEST_NAMESPACE) + + @pytest.mark.llm @pytest.mark.asyncio(scope="session") async def test_huggingface_v2_sequence_classification_with_probabilities( @@ -379,7 +445,7 @@ async def test_huggingface_v2_sequence_classification_with_probabilities( isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), diff --git a/test/e2e/predictor/test_huggingface_vllm_openvino.py b/test/e2e/predictor/test_huggingface_vllm_openvino.py index c9bffe5399d..365376478c1 100644 --- a/test/e2e/predictor/test_huggingface_vllm_openvino.py +++ b/test/e2e/predictor/test_huggingface_vllm_openvino.py @@ -58,7 +58,7 @@ def test_huggingface_vllm_openvino_openai_chat_completions(): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), @@ -108,7 +108,7 @@ def test_huggingface_vllm_openvino_openai_completions(): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), diff --git a/test/e2e/predictor/test_lightgbm.py b/test/e2e/predictor/test_lightgbm.py index e0ffdfebe0b..693824b7638 100644 --- a/test/e2e/predictor/test_lightgbm.py +++ b/test/e2e/predictor/test_lightgbm.py @@ -52,7 +52,7 @@ async def test_lightgbm_kserve(rest_v1_client): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), @@ -91,7 +91,7 @@ async def test_lightgbm_runtime_kserve(rest_v1_client): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), @@ -146,7 +146,7 @@ async def test_lightgbm_v2_runtime_mlserver(rest_v2_client): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), @@ -199,7 +199,7 @@ async def test_lightgbm_v2_kserve(rest_v2_client): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), @@ -254,7 +254,7 @@ async def test_lightgbm_v2_grpc(rest_v2_client): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), diff --git a/test/e2e/predictor/test_mlflow.py b/test/e2e/predictor/test_mlflow.py index 05fe483ca7a..4b4c2c01769 100644 --- a/test/e2e/predictor/test_mlflow.py +++ b/test/e2e/predictor/test_mlflow.py @@ -59,7 +59,7 @@ async def test_mlflow_v2_runtime_kserve(rest_v2_client): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), diff --git a/test/e2e/predictor/test_multi_model_serving.py b/test/e2e/predictor/test_multi_model_serving.py index 58626375b54..516f0a2fc9d 100644 --- a/test/e2e/predictor/test_multi_model_serving.py +++ b/test/e2e/predictor/test_multi_model_serving.py @@ -66,7 +66,7 @@ async def test_mms_sklearn_kserve( isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), @@ -182,7 +182,7 @@ async def test_mms_xgboost_kserve( isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), diff --git a/test/e2e/predictor/test_paddle.py b/test/e2e/predictor/test_paddle.py index 2b02882fc19..19178d3cb54 100644 --- a/test/e2e/predictor/test_paddle.py +++ b/test/e2e/predictor/test_paddle.py @@ -51,7 +51,7 @@ async def test_paddle(rest_v1_client): service_name = "isvc-paddle" isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=V1ObjectMeta(name=service_name, namespace=KSERVE_TEST_NAMESPACE), spec=V1beta1InferenceServiceSpec(predictor=predictor), ) @@ -99,7 +99,7 @@ async def test_paddle_runtime(rest_v1_client): service_name = "isvc-paddle-runtime" isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=V1ObjectMeta(name=service_name, namespace=KSERVE_TEST_NAMESPACE), spec=V1beta1InferenceServiceSpec(predictor=predictor), ) @@ -148,7 +148,7 @@ async def test_paddle_v2_kserve(rest_v2_client): service_name = "isvc-paddle-v2-kserve" isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=V1ObjectMeta(name=service_name, namespace=KSERVE_TEST_NAMESPACE), spec=V1beta1InferenceServiceSpec(predictor=predictor), ) @@ -204,7 +204,7 @@ async def test_paddle_v2_grpc(): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=V1ObjectMeta(name=service_name, namespace=KSERVE_TEST_NAMESPACE), spec=V1beta1InferenceServiceSpec(predictor=predictor), ) diff --git a/test/e2e/predictor/test_pmml.py b/test/e2e/predictor/test_pmml.py index 819c65d424a..33671e6d607 100644 --- a/test/e2e/predictor/test_pmml.py +++ b/test/e2e/predictor/test_pmml.py @@ -49,7 +49,7 @@ async def test_pmml_kserve(rest_v1_client): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), @@ -98,7 +98,7 @@ async def test_pmml_runtime_kserve(rest_v1_client): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), @@ -144,7 +144,7 @@ async def test_pmml_v2_kserve(rest_v2_client): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), @@ -222,7 +222,7 @@ async def test_pmml_v2_grpc(): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), diff --git a/test/e2e/predictor/test_raw_deployment.py b/test/e2e/predictor/test_raw_deployment.py index 89cda1ef818..800a5580212 100644 --- a/test/e2e/predictor/test_raw_deployment.py +++ b/test/e2e/predictor/test_raw_deployment.py @@ -15,6 +15,7 @@ import base64 import json import os +import uuid from kubernetes import client from kubernetes.client import ( V1ResourceRequirements, @@ -41,8 +42,9 @@ @pytest.mark.raw @pytest.mark.asyncio(scope="session") -async def test_raw_deployment_kserve(rest_v1_client): - service_name = "raw-sklearn" +async def test_raw_deployment_kserve(rest_v1_client, network_layer): + suffix = str(uuid.uuid4())[1:6] + service_name = "raw-sklearn-" + suffix annotations = dict() annotations["serving.kserve.io/deploymentMode"] = "RawDeployment" @@ -59,7 +61,7 @@ async def test_raw_deployment_kserve(rest_v1_client): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE, @@ -73,15 +75,21 @@ async def test_raw_deployment_kserve(rest_v1_client): ) kserve_client.create(isvc) kserve_client.wait_isvc_ready(service_name, namespace=KSERVE_TEST_NAMESPACE) - res = await predict_isvc(rest_v1_client, service_name, "./data/iris_input.json") + res = await predict_isvc( + rest_v1_client, + service_name, + "./data/iris_input.json", + network_layer=network_layer, + ) assert res["predictions"] == [1, 1] kserve_client.delete(service_name, KSERVE_TEST_NAMESPACE) @pytest.mark.raw @pytest.mark.asyncio(scope="session") -async def test_raw_deployment_runtime_kserve(rest_v1_client): - service_name = "raw-sklearn-runtime" +async def test_raw_deployment_runtime_kserve(rest_v1_client, network_layer): + suffix = str(uuid.uuid4())[1:6] + service_name = "raw-sklearn-runtime-" + suffix annotations = dict() annotations["serving.kserve.io/deploymentMode"] = "RawDeployment" @@ -101,7 +109,7 @@ async def test_raw_deployment_runtime_kserve(rest_v1_client): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE, @@ -115,7 +123,12 @@ async def test_raw_deployment_runtime_kserve(rest_v1_client): ) kserve_client.create(isvc) kserve_client.wait_isvc_ready(service_name, namespace=KSERVE_TEST_NAMESPACE) - res = await predict_isvc(rest_v1_client, service_name, "./data/iris_input.json") + res = await predict_isvc( + rest_v1_client, + service_name, + "./data/iris_input.json", + network_layer=network_layer, + ) assert res["predictions"] == [1, 1] kserve_client.delete(service_name, KSERVE_TEST_NAMESPACE) @@ -123,8 +136,9 @@ async def test_raw_deployment_runtime_kserve(rest_v1_client): @pytest.mark.grpc @pytest.mark.raw @pytest.mark.asyncio(scope="session") -async def test_isvc_with_multiple_container_port(): - service_name = "raw-multiport-custom-model" +async def test_isvc_with_multiple_container_port(network_layer): + suffix = str(uuid.uuid4())[1:6] + service_name = "raw-multiport-custom-model-" + suffix model_name = "custom-model" predictor = V1beta1PredictorSpec( @@ -150,7 +164,7 @@ async def test_isvc_with_multiple_container_port(): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE, @@ -181,7 +195,10 @@ async def test_isvc_with_multiple_container_port(): ] expected_output = ["14.976", "14.037", "13.966", "12.252", "12.086"] grpc_response = await predict_grpc( - service_name=service_name, payload=payload, model_name=model_name + service_name=service_name, + payload=payload, + model_name=model_name, + network_layer=network_layer, ) fields = grpc_response.outputs[0].data grpc_output = ["%.3f" % value for value in fields] diff --git a/test/e2e/predictor/test_response_headers.py b/test/e2e/predictor/test_response_headers.py index 8a9c05dbac1..46e4cf7b815 100644 --- a/test/e2e/predictor/test_response_headers.py +++ b/test/e2e/predictor/test_response_headers.py @@ -62,7 +62,7 @@ def test_predictor_headers_v1(): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), @@ -148,7 +148,7 @@ def test_predictor_headers_v2(): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), diff --git a/test/e2e/predictor/test_sklearn.py b/test/e2e/predictor/test_sklearn.py index 5af9058aac3..8b39ebdb419 100644 --- a/test/e2e/predictor/test_sklearn.py +++ b/test/e2e/predictor/test_sklearn.py @@ -51,7 +51,7 @@ async def test_sklearn_kserve(rest_v1_client): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), @@ -93,7 +93,7 @@ async def test_sklearn_v2_mlserver(rest_v2_client): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), @@ -138,7 +138,7 @@ async def test_sklearn_runtime_kserve(rest_v1_client): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), @@ -185,7 +185,7 @@ async def test_sklearn_v2_runtime_mlserver(rest_v2_client): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), @@ -230,7 +230,7 @@ async def test_sklearn_v2(rest_v2_client): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), @@ -291,7 +291,7 @@ async def test_sklearn_v2_grpc(): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), @@ -337,7 +337,7 @@ async def test_sklearn_v2_mixed(rest_v2_client): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), @@ -384,7 +384,7 @@ async def test_sklearn_v2_mixed_grpc(): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), diff --git a/test/e2e/predictor/test_tensorflow.py b/test/e2e/predictor/test_tensorflow.py index ae6da88326c..ace2d316e5d 100644 --- a/test/e2e/predictor/test_tensorflow.py +++ b/test/e2e/predictor/test_tensorflow.py @@ -46,7 +46,7 @@ async def test_tensorflow_kserve(rest_v1_client): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), @@ -85,7 +85,7 @@ async def test_tensorflow_runtime_kserve(rest_v1_client): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), diff --git a/test/e2e/predictor/test_torchserve.py b/test/e2e/predictor/test_torchserve.py index 48c6d39daf3..cd8ec61ea1e 100644 --- a/test/e2e/predictor/test_torchserve.py +++ b/test/e2e/predictor/test_torchserve.py @@ -50,7 +50,7 @@ async def test_torchserve_kserve(rest_v1_client): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), @@ -88,7 +88,7 @@ async def test_torchserve_v2_kserve(rest_v2_client): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), @@ -131,7 +131,7 @@ async def test_torchserve_grpc_v2(): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), @@ -185,7 +185,7 @@ async def test_torchserve_runtime_kserve(rest_v1_client): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), diff --git a/test/e2e/predictor/test_triton.py b/test/e2e/predictor/test_triton.py index 2bed36879c1..07ee858583f 100644 --- a/test/e2e/predictor/test_triton.py +++ b/test/e2e/predictor/test_triton.py @@ -48,7 +48,7 @@ async def test_triton(rest_v2_client): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), @@ -127,7 +127,7 @@ async def test_triton_runtime_with_transformer(rest_v1_client): ) isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), diff --git a/test/e2e/predictor/test_xgboost.py b/test/e2e/predictor/test_xgboost.py index 7299865c2e2..5e2b1a4966d 100644 --- a/test/e2e/predictor/test_xgboost.py +++ b/test/e2e/predictor/test_xgboost.py @@ -51,7 +51,7 @@ async def test_xgboost_kserve(rest_v1_client): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), @@ -96,7 +96,7 @@ async def test_xgboost_v2_mlserver(rest_v2_client): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), @@ -147,7 +147,7 @@ async def test_xgboost_single_model_file(rest_v2_client): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), @@ -191,7 +191,7 @@ async def test_xgboost_runtime_kserve(rest_v1_client): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), @@ -239,7 +239,7 @@ async def test_xgboost_v2_runtime_mlserver(rest_v2_client): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), @@ -284,7 +284,7 @@ async def test_xgboost_v2(rest_v2_client): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), @@ -332,7 +332,7 @@ async def test_xgboost_v2_grpc(): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), diff --git a/test/e2e/pytest.ini b/test/e2e/pytest.ini index 18c5d0120e7..3407bbfbc27 100644 --- a/test/e2e/pytest.ini +++ b/test/e2e/pytest.ini @@ -16,4 +16,5 @@ markers = predictor: predictor e2e tests including grpc path_based_routing: e2e tests for path based routing llm: e2e tests for huggingface runtime - vllm: e2e tests for huggingface runtime with vllm-openvino backend \ No newline at end of file + vllm: e2e tests for huggingface runtime with vllm-openvino backend + modelcache: e2e tests for model caching \ No newline at end of file diff --git a/test/e2e/qpext/test_qpext.py b/test/e2e/qpext/test_qpext.py index 6d4c39ff3a6..dfeb4a569ad 100644 --- a/test/e2e/qpext/test_qpext.py +++ b/test/e2e/qpext/test_qpext.py @@ -63,7 +63,7 @@ async def test_qpext_kserve(rest_v2_client): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE, diff --git a/test/e2e/storagespec/test_s3_storagespec.py b/test/e2e/storagespec/test_s3_storagespec.py index 12e3229b5a3..9619dc59038 100644 --- a/test/e2e/storagespec/test_s3_storagespec.py +++ b/test/e2e/storagespec/test_s3_storagespec.py @@ -51,7 +51,7 @@ async def test_sklearn_s3_storagespec_kserve(rest_v1_client): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), diff --git a/test/e2e/transformer/test_collocation.py b/test/e2e/transformer/test_collocation.py index 9c01f104b24..354d91c390d 100644 --- a/test/e2e/transformer/test_collocation.py +++ b/test/e2e/transformer/test_collocation.py @@ -14,6 +14,7 @@ import os +import uuid from kubernetes import client from kserve import KServeClient @@ -90,7 +91,7 @@ async def test_transformer_collocation(rest_v1_client): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), @@ -131,8 +132,9 @@ async def test_transformer_collocation(rest_v1_client): @pytest.mark.raw @pytest.mark.asyncio(scope="session") -async def test_raw_transformer_collocation(rest_v1_client): - service_name = "raw-custom-model-collocation" +async def test_raw_transformer_collocation(rest_v1_client, network_layer): + suffix = str(uuid.uuid4())[1:6] + service_name = "raw-custom-model-collocation-" + suffix model_name = "mnist" predictor = V1beta1PredictorSpec( min_replicas=1, @@ -182,7 +184,7 @@ async def test_raw_transformer_collocation(rest_v1_client): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE, @@ -214,10 +216,19 @@ async def test_raw_transformer_collocation(rest_v1_client): for pod in pods.items: print(pod) raise e - is_ready = await is_model_ready(rest_v1_client, service_name, model_name) is True + is_ready = ( + await is_model_ready( + rest_v1_client, service_name, model_name, network_layer=network_layer + ) + is True + ) assert is_ready is True res = await predict_isvc( - rest_v1_client, service_name, "./data/transformer.json", model_name=model_name + rest_v1_client, + service_name, + "./data/transformer.json", + model_name=model_name, + network_layer=network_layer, ) assert res["predictions"][0] == 2 kserve_client.delete(service_name, KSERVE_TEST_NAMESPACE) diff --git a/test/e2e/transformer/test_raw_transformer.py b/test/e2e/transformer/test_raw_transformer.py index ae9b2aba2e7..09fc11ae694 100644 --- a/test/e2e/transformer/test_raw_transformer.py +++ b/test/e2e/transformer/test_raw_transformer.py @@ -12,7 +12,13 @@ # limitations under the License. import os +import uuid + from kubernetes import client +from kubernetes.client import V1ResourceRequirements +from kubernetes.client import V1Container +from kubernetes.client import V1EnvVar +import pytest from kserve import KServeClient from kserve import constants @@ -21,18 +27,16 @@ from kserve import V1beta1TorchServeSpec from kserve import V1beta1InferenceServiceSpec from kserve import V1beta1InferenceService -from kubernetes.client import V1ResourceRequirements -from kubernetes.client import V1Container -from kubernetes.client import V1EnvVar -import pytest + from ..common.utils import predict_isvc from ..common.utils import KSERVE_TEST_NAMESPACE @pytest.mark.raw @pytest.mark.asyncio(scope="session") -async def test_transformer(rest_v1_client): - service_name = "raw-transformer" +async def test_transformer(rest_v1_client, network_layer): + suffix = str(uuid.uuid4())[1:6] + service_name = "raw-transformer-" + suffix predictor = V1beta1PredictorSpec( min_replicas=1, pytorch=V1beta1TorchServeSpec( @@ -68,7 +72,7 @@ async def test_transformer(rest_v1_client): annotations["serving.kserve.io/deploymentMode"] = "RawDeployment" isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE, annotations=annotations ), @@ -94,7 +98,11 @@ async def test_transformer(rest_v1_client): raise e res = await predict_isvc( - rest_v1_client, service_name, "./data/transformer.json", model_name="mnist" + rest_v1_client, + service_name, + "./data/transformer.json", + model_name="mnist", + network_layer=network_layer, ) assert res["predictions"][0] == 2 kserve_client.delete(service_name, KSERVE_TEST_NAMESPACE) diff --git a/test/e2e/transformer/test_transformer.py b/test/e2e/transformer/test_transformer.py index 0de464188fc..19463bc37e4 100644 --- a/test/e2e/transformer/test_transformer.py +++ b/test/e2e/transformer/test_transformer.py @@ -67,7 +67,7 @@ async def test_transformer(rest_v1_client): isvc = V1beta1InferenceService( api_version=constants.KSERVE_V1BETA1, - kind=constants.KSERVE_KIND, + kind=constants.KSERVE_KIND_INFERENCESERVICE, metadata=client.V1ObjectMeta( name=service_name, namespace=KSERVE_TEST_NAMESPACE ), diff --git a/test/overlays/knative/knative-serving-istio.yaml b/test/overlays/knative/knative-serving-istio.yaml index 8dc6d08802d..952abaad4ac 100644 --- a/test/overlays/knative/knative-serving-istio.yaml +++ b/test/overlays/knative/knative-serving-istio.yaml @@ -9,7 +9,7 @@ metadata: name: knative-serving namespace: knative-serving spec: - version: "1.13.1" + version: "1.15.2" config: deployment: # Skip tag resolution for certain domains diff --git a/test/overlays/knative/knative-serving-kourier.yaml b/test/overlays/knative/knative-serving-kourier.yaml index 97268aba452..0208f8f6e74 100644 --- a/test/overlays/knative/knative-serving-kourier.yaml +++ b/test/overlays/knative/knative-serving-kourier.yaml @@ -9,7 +9,7 @@ metadata: name: knative-serving namespace: knative-serving spec: - version: "1.13.1" + version: "1.15.2" ingress: kourier: enabled: true diff --git a/test/scripts/gh-actions/build-images.sh b/test/scripts/gh-actions/build-images.sh index 19f8873734e..6b5212cfeec 100755 --- a/test/scripts/gh-actions/build-images.sh +++ b/test/scripts/gh-actions/build-images.sh @@ -25,6 +25,7 @@ set -o pipefail echo "Github SHA ${GITHUB_SHA}" CONTROLLER_IMG_TAG=${DOCKER_REPO}/${CONTROLLER_IMG}:${GITHUB_SHA} LOCALMODEL_CONTROLLER_IMG_TAG=${DOCKER_REPO}/${LOCALMODEL_CONTROLLER_IMG}:${GITHUB_SHA} +LOCALMODEL_AGENT_IMG_TAG=${DOCKER_REPO}/${LOCALMODEL_AGENT_IMG}:${GITHUB_SHA} STORAGE_INIT_IMG_TAG=${DOCKER_REPO}/${STORAGE_INIT_IMG}:${GITHUB_SHA} AGENT_IMG_TAG=${DOCKER_REPO}/${AGENT_IMG}:${GITHUB_SHA} ROUTER_IMG_TAG=${DOCKER_REPO}/${ROUTER_IMG}:${GITHUB_SHA} @@ -37,6 +38,10 @@ echo "Building localmodel controller image" docker buildx build -f localmodel.Dockerfile . -t "${LOCALMODEL_CONTROLLER_IMG_TAG}" \ -o type=docker,dest="${DOCKER_IMAGES_PATH}/${LOCALMODEL_CONTROLLER_IMG}-${GITHUB_SHA}",compression-level=0 +echo "Building localmodel agent image" +docker buildx build -f localmodel-agent.Dockerfile . -t "${LOCALMODEL_AGENT_IMG_TAG}" \ + -o type=docker,dest="${DOCKER_IMAGES_PATH}/${LOCALMODEL_AGENT_IMG}-${GITHUB_SHA}",compression-level=0 + echo "Building agent image" docker buildx build -f agent.Dockerfile . -t "${AGENT_IMG_TAG}" \ -o type=docker,dest="${DOCKER_IMAGES_PATH}/${AGENT_IMG}-${GITHUB_SHA}",compression-level=0 diff --git a/test/scripts/gh-actions/build-server-runtimes.sh b/test/scripts/gh-actions/build-server-runtimes.sh index b21d626af5f..c2af2e47498 100755 --- a/test/scripts/gh-actions/build-server-runtimes.sh +++ b/test/scripts/gh-actions/build-server-runtimes.sh @@ -74,10 +74,12 @@ pushd python >/dev/null echo "Building image transformer gRPC image" docker buildx build -t "${CUSTOM_TRANSFORMER_GRPC_IMG_TAG}" -f custom_transformer_grpc.Dockerfile \ -o type=docker,dest="${DOCKER_IMAGES_PATH}/${CUSTOM_TRANSFORMER_GRPC_IMG}-${GITHUB_SHA}",compression-level=0 . + echo "Disk usage after Building image transformer gRPC image:" + df -hT echo "Building Huggingface CPU Openvino image" docker buildx build -t "${HUGGINGFACE_CPU_OPENVINO_IMG_TAG}" -f huggingface_server_cpu_openvino.Dockerfile \ -o type=docker,dest="${DOCKER_IMAGES_PATH}/${HUGGINGFACE_IMG}-${GITHUB_SHA}",compression-level=0 . - echo "Disk usage after Building image transformer gRPC image:" + echo "Disk usage after Building Huggingface CPU Openvino image:" df -hT fi diff --git a/test/scripts/gh-actions/install-knative-operator.sh b/test/scripts/gh-actions/install-knative-operator.sh index e75052cb796..793610d6538 100755 --- a/test/scripts/gh-actions/install-knative-operator.sh +++ b/test/scripts/gh-actions/install-knative-operator.sh @@ -20,7 +20,7 @@ set -o errexit set -o nounset set -o pipefail -KNATIVE_OPERATOR_VERSION="v1.14.5" +KNATIVE_OPERATOR_VERSION="v1.16.0" echo "Installing Knative Operator ..." helm install knative-operator --namespace knative-operator --create-namespace --wait \ diff --git a/test/scripts/gh-actions/run-e2e-tests.sh b/test/scripts/gh-actions/run-e2e-tests.sh index e3d460fb9f0..88ab0ea2308 100755 --- a/test/scripts/gh-actions/run-e2e-tests.sh +++ b/test/scripts/gh-actions/run-e2e-tests.sh @@ -15,6 +15,7 @@ # limitations under the License. # The script is used to deploy knative and kserve, and run e2e tests. +# Usage: run-e2e-tests.sh $MARKER $PARALLELISM $NETWORK_LAYER set -o errexit set -o nounset @@ -27,8 +28,16 @@ else echo "No parallelism requested for pytest. Will use default value of 1" fi +MARKER="${1}" PARALLELISM="${2:-1}" +NETWORK_LAYER="${3:-'istio'}" + source python/kserve/.venv/bin/activate pushd test/e2e >/dev/null - pytest -m "$1" --ignore=qpext --log-cli-level=INFO -n $PARALLELISM --dist worksteal + if [[ $MARKER == "raw" && $NETWORK_LAYER == "istio-ingress" ]]; then + echo "Skipping explainer tests for raw deployment with ingress" + pytest -m "$MARKER" --ignore=qpext --log-cli-level=INFO -n $PARALLELISM --dist worksteal --network-layer $NETWORK_LAYER --ignore=explainer/ + else + pytest -m "$MARKER" --ignore=qpext --log-cli-level=INFO -n $PARALLELISM --dist worksteal --network-layer $NETWORK_LAYER + fi popd diff --git a/test/scripts/gh-actions/setup-deps.sh b/test/scripts/gh-actions/setup-deps.sh index b187897ff46..6f751027d69 100755 --- a/test/scripts/gh-actions/setup-deps.sh +++ b/test/scripts/gh-actions/setup-deps.sh @@ -16,51 +16,76 @@ # The script will install KServe dependencies in the GH Actions environment. # (Istio, Knative, cert-manager, kustomize, yq) +# Usage: setup-deps.sh $DEPLOYMENT_MODE $NETWORK_LAYER set -o errexit set -o nounset set -o pipefail -SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]:-$0}"; )" &> /dev/null && pwd 2> /dev/null; )"; +SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]:-$0}")" &>/dev/null && pwd 2>/dev/null)" DEPLOYMENT_MODE="${1:-'serverless'}" +NETWORK_LAYER="${2:-'istio'}" -ISTIO_VERSION="1.20.4" -CERT_MANAGER_VERSION="v1.15.1" +ISTIO_VERSION="1.23.2" +CERT_MANAGER_VERSION="v1.16.1" YQ_VERSION="v4.28.1" +GATEWAY_API_VERSION="v1.2.1" +ENVOY_GATEWAY_VERSION="v1.2.2" echo "Installing yq ..." wget https://github.com/mikefarah/yq/releases/download/${YQ_VERSION}/yq_linux_amd64 -O /usr/local/bin/yq && chmod +x /usr/local/bin/yq -echo "Installing Istio ..." -mkdir istio_tmp -pushd istio_tmp >/dev/null +if [[ $NETWORK_LAYER == "istio-gatewayapi" || $NETWORK_LAYER == "envoy-gatewayapi" ]]; then + echo "Installing Gateway CRDs ..." + kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/${GATEWAY_API_VERSION}/standard-install.yaml +fi + +if [[ $NETWORK_LAYER == "istio-ingress" || $NETWORK_LAYER == "istio-gatewayapi" || $NETWORK_LAYER == "istio" ]]; then + echo "Installing Istio ..." + mkdir istio_tmp + pushd istio_tmp >/dev/null curl -L https://istio.io/downloadIstio | ISTIO_VERSION=${ISTIO_VERSION} sh - cd istio-${ISTIO_VERSION} export PATH=$PWD/bin:$PATH - istioctl manifest generate --set meshConfig.accessLogFile=/dev/stdout > ${SCRIPT_DIR}/../../overlays/istio/generated-manifest.yaml -popd + istioctl manifest generate --set meshConfig.accessLogFile=/dev/stdout >${SCRIPT_DIR}/../../overlays/istio/generated-manifest.yaml + popd + kubectl create ns istio-system + for i in {1..3}; do kubectl apply -k test/overlays/istio && break || sleep 15; done -kubectl create ns istio-system -for i in 1 2 3 ; do kubectl apply -k test/overlays/istio && break || sleep 15; done + echo "Waiting for Istio to be ready ..." + kubectl wait --for=condition=Ready pods --all --timeout=240s -n istio-system +elif [[ $NETWORK_LAYER == "envoy-gatewayapi" ]]; then + echo "Installing Envoy Gateway ..." + helm install eg oci://docker.io/envoyproxy/gateway-helm --version ${ENVOY_GATEWAY_VERSION} -n envoy-gateway-system --create-namespace --wait + kubectl wait --timeout=5m -n envoy-gateway-system deployment/envoy-gateway --for=condition=Available -echo "Waiting for Istio to be ready ..." -kubectl wait --for=condition=Ready pods --all --timeout=240s -n istio-system + echo "Creating envoy GatewayClass ..." + cat </dev/null diff --git a/test/scripts/gh-actions/setup-kserve.sh b/test/scripts/gh-actions/setup-kserve.sh index 16a3f18db20..7dd13282249 100755 --- a/test/scripts/gh-actions/setup-kserve.sh +++ b/test/scripts/gh-actions/setup-kserve.sh @@ -16,12 +16,13 @@ # The script will install KServe dependencies in the GH Actions environment. # (Istio, Knative, cert-manager, kustomize, yq) -# Usage: setup-kserve.sh $DEPLOYMENT_MODE +# Usage: setup-kserve.sh $DEPLOYMENT_MODE $NETWORK_LAYER set -o errexit set -o nounset set -o pipefail DEPLOYMENT_MODE="${1:-'serverless'}" +NETWORK_LAYER="${2:-'istio'}" make deploy-ci @@ -29,8 +30,25 @@ shopt -s nocasematch if [[ $DEPLOYMENT_MODE == "raw" ]];then echo "Patching default deployment mode to raw deployment" kubectl patch cm -n kserve inferenceservice-config --patch='{"data": {"deploy": "{\"defaultDeploymentMode\": \"RawDeployment\"}"}}' + + if [[ $NETWORK_LAYER == "envoy-gatewayapi" ]]; then + echo "Creating Envoy Gateway ..." + kubectl apply -f config/overlays/test/gateway/ingress_gateway.yaml + sleep 10 + echo "Waiting for envoy gateway to be ready ..." + kubectl wait --timeout=5m -n envoy-gateway-system pod -l serving.kserve.io/gateway=kserve-ingress-gateway --for=condition=Ready + elif [[ $NETWORK_LAYER == "istio-gatewayapi" ]]; then + echo "Creating Istio Gateway ..." + # Replace gatewayclass name + sed -i 's/envoy/istio/g' config/overlays/test/gateway/ingress_gateway.yaml + kubectl apply -f config/overlays/test/gateway/ingress_gateway.yaml + sleep 10 + echo "Waiting for istio gateway to be ready ..." + kubectl wait --timeout=5m -n kserve pod -l serving.kserve.io/gateway=kserve-ingress-gateway --for=condition=Ready + fi fi shopt -u nocasematch + echo "Waiting for KServe started ..." kubectl wait --for=condition=Ready pods --all --timeout=180s -n kserve kubectl get events -A diff --git a/test/scripts/gh-actions/setup-modelmesh-dep.sh b/test/scripts/gh-actions/setup-modelmesh-dep.sh deleted file mode 100755 index f518bb56e98..00000000000 --- a/test/scripts/gh-actions/setup-modelmesh-dep.sh +++ /dev/null @@ -1,148 +0,0 @@ -#!/bin/bash - -# Copyright 2022 The KServe Authors. -# -# Licensed 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 "Creating etcd dependencies" -cat <<-EOF | kubectl apply -f - -apiVersion: v1 -kind: Service -metadata: - name: etcd -spec: - ports: - - name: etcd-client-port - port: 2379 - protocol: TCP - targetPort: 2379 - selector: - app: etcd -EOF - -echo "Creating etcd service" -cat <<-EOF | kubectl apply -f - -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - app: etcd - name: etcd -spec: - replicas: 1 - selector: - matchLabels: - app: etcd - template: - metadata: - labels: - app: etcd - spec: - containers: - - command: - - etcd - - --data-dir - - /tmp/etcd.data - - --listen-client-urls - - http://0.0.0.0:2379 - - --advertise-client-urls - - http://0.0.0.0:2379 - image: quay.io/coreos/etcd:v3.5.4 - name: etcd - ports: - - containerPort: 2379 - name: client - protocol: TCP - - containerPort: 2380 - name: server - protocol: TCP -EOF - -cat <<-EOF | kubectl apply -f - -apiVersion: v1 -kind: Secret -metadata: - name: model-serving-etcd -stringData: - etcd_connection: | - { - "endpoints": "http://etcd.default:2379", - "root_prefix": "modelmesh-serving" - } -EOF - -echo "Creating minio dependencies" -cat <<-EOF | kubectl apply -f - -apiVersion: v1 -kind: Service -metadata: - name: minio -spec: - ports: - - name: minio-client-port - port: 9000 - protocol: TCP - targetPort: 9000 - selector: - app: minio -EOF - -cat <<-EOF | kubectl apply -f - -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - app: minio - name: minio -spec: - replicas: 1 - selector: - matchLabels: - app: minio - template: - metadata: - labels: - app: minio - spec: - containers: - - args: - - server - # - /data - - /data1 - env: - - name: MINIO_ACCESS_KEY - value: AKIAIOSFODNN7EXAMPLE - - name: MINIO_SECRET_KEY - value: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY - # image: quay.io/cloudservices/minio:latest - image: kserve/modelmesh-minio-examples:latest - name: minio -EOF - -cat <<-EOF | kubectl apply -f - -apiVersion: v1 -kind: Secret -metadata: - name: storage-config -stringData: - localMinIO: | - { - "type": "s3", - "access_key_id": "AKIAIOSFODNN7EXAMPLE", - "secret_access_key": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", - "endpoint_url": "http://minio:9000", - "default_bucket": "modelmesh-example-models", - "region": "us-south" - } -EOF diff --git a/test/scripts/gh-actions/status-check.sh b/test/scripts/gh-actions/status-check.sh index e7f7116d54c..6a7f4f3fc39 100755 --- a/test/scripts/gh-actions/status-check.sh +++ b/test/scripts/gh-actions/status-check.sh @@ -1,9 +1,5 @@ #!/bin/bash -# For debugging purpose: The KServe webhook is failing randomly in the GH Actions environment. This is to check if the webhook cert is ready at -# the time of failure. Once the issue is resolved, the following line can be removed. -kubectl describe cert -n kserve serving-cert - sleep 10 echo "::group::Free Space" df -hT @@ -30,10 +26,26 @@ echo "::group::K8s Events in kserve-ci-e2e-test namespace" kubectl get events -n kserve-ci-e2e-test echo "::endgroup::" +echo "::group::K8s Events in kserve-localmodel-jobs namespace" +kubectl get events -n kserve-localmodel-jobs +echo "::endgroup::" + echo "::group::Kserve Controller Logs" kubectl logs -l control-plane=kserve-controller-manager -n kserve -c manager --tail -1 echo "::endgroup::" +echo "::group::Kserve ModelCache Controller Logs" +kubectl logs -l control-plane=kserve-localmodel-controller-manager -n kserve -c manager --tail -1 +echo "::endgroup::" + +echo "::group::Kserve ModelCache Agent Logs" +for pod in $(kubectl get pods -l control-plane=kserve-localmodelnode-agent -o jsonpath='{.items[*].metadata.name}' -n kserve); do + echo "===================================== Logs for modelcache agent: $pod =========================================" + kubectl logs "$pod" -c manager -n kserve --tail -1 + echo "================================================================================================================" +done +echo "::endgroup::" + echo "::group::Predictor Pod logs" for pod in $(kubectl get pods -l 'component in (predictor)' -o jsonpath='{.items[*].metadata.name}' -n kserve-ci-e2e-test); do echo "===================================== Logs for Predictor Pod: $pod =========================================" @@ -66,6 +78,16 @@ for pod in $(kubectl get pods -l 'serving.kserve.io/inferencegraph=model-chainer done echo "::endgroup::" +echo "::group::envoy gateway" +kubectl describe pods -l serving.kserve.io/gateway=kserve-ingress-gateway -n envoy-gateway-system +kubectl describe svc -l gateway.envoyproxy.io/owning-gateway-name=kserve-ingress-gateway -n envoy-gateway-system +echo "::endgroup::" + +echo "::group::istio gateway" +kubectl describe pods -l serving.kserve.io/gateway=kserve-ingress-gateway -n kserve +kubectl describe svc -l serving.kserve.io/gateway=kserve-ingress-gateway -n kserve +echo "::endgroup::" + shopt -s nocasematch if [[ $# -eq 1 && "$1" == "kourier" ]]; then echo "::group::Kourier Gateway Pod logs" diff --git a/test/scripts/gh-actions/update-test-overlays.sh b/test/scripts/gh-actions/update-test-overlays.sh index f3a574d9919..7341d470ba9 100755 --- a/test/scripts/gh-actions/update-test-overlays.sh +++ b/test/scripts/gh-actions/update-test-overlays.sh @@ -29,3 +29,6 @@ sed -i -e "s/latest/${GITHUB_SHA}/g" config/overlays/test/manager_image_patch.ya # Update localmodel controller image tag sed -i -e "s/latest/${GITHUB_SHA}/g" config/overlays/test/localmodel_manager_image_patch.yaml + +# Update localmodel agent image tag +sed -i -e "s/latest/${GITHUB_SHA}/g" config/overlays/test/localmodelnode_agent_image_patch.yaml \ No newline at end of file diff --git a/tools/tf2openapi/Dockerfile b/tools/tf2openapi/Dockerfile index 8bdce89078b..95da73f54fd 100644 --- a/tools/tf2openapi/Dockerfile +++ b/tools/tf2openapi/Dockerfile @@ -1,5 +1,5 @@ # Build the manager binary -FROM golang:1.22 AS builder +FROM golang:1.23 AS builder # Copy in the go src WORKDIR /go/src/github.com/kserve/kserve