diff --git a/.circleci/config.yml b/.circleci/config.yml index ad6c457f72..1a3dab8a99 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -47,9 +47,9 @@ jobs: pip install opentelemetry-api==1.25.0 pip install opentelemetry-sdk==1.25.0 pip install opentelemetry-exporter-otlp==1.25.0 - pip install openai==1.54.0 - pip install prisma==0.11.0 - pip install "detect_secrets==1.5.0" + pip install openai==1.54.0 + pip install prisma==0.11.0 + pip install "detect_secrets==1.5.0" pip install "httpx==0.24.1" pip install "respx==0.21.1" pip install fastapi @@ -165,9 +165,9 @@ jobs: pip install opentelemetry-api==1.25.0 pip install opentelemetry-sdk==1.25.0 pip install opentelemetry-exporter-otlp==1.25.0 - pip install openai==1.54.0 - pip install prisma==0.11.0 - pip install "detect_secrets==1.5.0" + pip install openai==1.54.0 + pip install prisma==0.11.0 + pip install "detect_secrets==1.5.0" pip install "httpx==0.24.1" pip install "respx==0.21.1" pip install fastapi @@ -264,9 +264,9 @@ jobs: pip install opentelemetry-api==1.25.0 pip install opentelemetry-sdk==1.25.0 pip install opentelemetry-exporter-otlp==1.25.0 - pip install openai==1.54.0 - pip install prisma==0.11.0 - pip install "detect_secrets==1.5.0" + pip install openai==1.54.0 + pip install prisma==0.11.0 + pip install "detect_secrets==1.5.0" pip install "httpx==0.24.1" pip install "respx==0.21.1" pip install fastapi @@ -367,7 +367,7 @@ jobs: # Store test results - store_test_results: path: test-results - + - persist_to_workspace: root: . paths: @@ -375,10 +375,10 @@ jobs: - auth_ui_unit_tests_coverage litellm_router_testing: # Runs all tests with the "router" keyword docker: - - image: cimg/python:3.11 - auth: - username: ${DOCKERHUB_USERNAME} - password: ${DOCKERHUB_PASSWORD} + - image: cimg/python:3.11 + auth: + username: ${DOCKERHUB_USERNAME} + password: ${DOCKERHUB_PASSWORD} working_directory: ~/project steps: @@ -417,10 +417,10 @@ jobs: - litellm_router_coverage litellm_proxy_unit_testing: # Runs all tests with the "proxy", "key", "jwt" filenames docker: - - image: cimg/python:3.11 - auth: - username: ${DOCKERHUB_USERNAME} - password: ${DOCKERHUB_PASSWORD} + - image: cimg/python:3.11 + auth: + username: ${DOCKERHUB_USERNAME} + password: ${DOCKERHUB_PASSWORD} working_directory: ~/project steps: - checkout @@ -458,9 +458,9 @@ jobs: pip install opentelemetry-api==1.25.0 pip install opentelemetry-sdk==1.25.0 pip install opentelemetry-exporter-otlp==1.25.0 - pip install openai==1.54.0 - pip install prisma==0.11.0 - pip install "detect_secrets==1.5.0" + pip install openai==1.54.0 + pip install prisma==0.11.0 + pip install "detect_secrets==1.5.0" pip install "httpx==0.24.1" pip install "respx==0.21.1" pip install fastapi @@ -514,10 +514,10 @@ jobs: - litellm_proxy_unit_tests_coverage litellm_assistants_api_testing: # Runs all tests with the "assistants" keyword docker: - - image: cimg/python:3.11 - auth: - username: ${DOCKERHUB_USERNAME} - password: ${DOCKERHUB_PASSWORD} + - image: cimg/python:3.11 + auth: + username: ${DOCKERHUB_USERNAME} + password: ${DOCKERHUB_PASSWORD} working_directory: ~/project steps: @@ -616,7 +616,7 @@ jobs: command: | mv coverage.xml llm_translation_coverage.xml mv .coverage llm_translation_coverage - + # Store test results - store_test_results: path: test-results @@ -660,7 +660,7 @@ jobs: command: | mv coverage.xml batches_coverage.xml mv .coverage batches_coverage - + # Store test results - store_test_results: path: test-results @@ -704,7 +704,7 @@ jobs: command: | mv coverage.xml secret_manager_coverage.xml mv .coverage secret_manager_coverage - + # Store test results - store_test_results: path: test-results @@ -747,7 +747,7 @@ jobs: command: | mv coverage.xml pass_through_unit_tests_coverage.xml mv .coverage pass_through_unit_tests_coverage - + # Store test results - store_test_results: path: test-results @@ -789,7 +789,7 @@ jobs: command: | mv coverage.xml image_gen_coverage.xml mv .coverage image_gen_coverage - + # Store test results - store_test_results: path: test-results @@ -835,7 +835,7 @@ jobs: command: | mv coverage.xml logging_coverage.xml mv .coverage logging_coverage - + # Store test results - store_test_results: path: test-results @@ -875,7 +875,7 @@ jobs: pwd ls python -m pytest -vv tests/local_testing/test_basic_python_version.py - + installing_litellm_on_python_3_13: docker: - image: cimg/python:3.13.1 @@ -1039,7 +1039,7 @@ jobs: cat docker_output.log exit 1 fi - + build_and_test: machine: image: ubuntu-2204:2023.10.1 @@ -1085,9 +1085,9 @@ jobs: pip install "langfuse>=2.0.0" pip install "logfire==0.29.0" pip install numpydoc - pip install prisma - pip install fastapi - pip install jsonschema + pip install prisma + pip install fastapi + pip install jsonschema pip install "httpx==0.24.1" pip install "gunicorn==21.2.0" pip install "anyio==3.7.1" @@ -1203,9 +1203,9 @@ jobs: pip install "langfuse>=2.0.0" pip install "logfire==0.29.0" pip install numpydoc - pip install prisma - pip install fastapi - pip install jsonschema + pip install prisma + pip install fastapi + pip install jsonschema pip install "httpx==0.24.1" pip install "gunicorn==21.2.0" pip install "anyio==3.7.1" @@ -1322,9 +1322,9 @@ jobs: pip install "langfuse>=2.0.0" pip install "logfire==0.29.0" pip install numpydoc - pip install prisma - pip install fastapi - pip install jsonschema + pip install prisma + pip install fastapi + pip install jsonschema pip install "httpx==0.24.1" pip install "gunicorn==21.2.0" pip install "anyio==3.7.1" @@ -1387,8 +1387,9 @@ jobs: pwd ls python -m pytest -vv tests/otel_tests -x --junitxml=test-results/junit.xml --durations=5 - no_output_timeout: 120m - # Clean up first container + no_output_timeout: + 120m + # Clean up first container - run: name: Stop and remove first container command: | @@ -1524,8 +1525,9 @@ jobs: name: Run tests command: | python -m pytest -vv tests/basic_proxy_startup_tests -x --junitxml=test-results/junit-2.xml --durations=5 - no_output_timeout: 120m - # Clean up first container + no_output_timeout: + 120m + # Clean up first container - run: name: Stop and remove first container command: | @@ -1572,9 +1574,9 @@ jobs: pip install mypy pip install pyarrow pip install numpydoc - pip install prisma - pip install fastapi - pip install jsonschema + pip install prisma + pip install fastapi + pip install jsonschema pip install "httpx==0.24.1" pip install "anyio==3.7.1" pip install "asyncio==3.4.3" @@ -1676,7 +1678,6 @@ jobs: - codecov/upload: file: ./coverage.xml - publish_to_pypi: docker: - image: cimg/python:3.8 @@ -1703,7 +1704,6 @@ jobs: circleci step halt fi - - run: name: Checkout code command: git checkout $CIRCLE_SHA1 @@ -1792,9 +1792,9 @@ jobs: pip install mypy pip install pyarrow pip install numpydoc - pip install prisma - pip install fastapi - pip install jsonschema + pip install prisma + pip install fastapi + pip install jsonschema pip install "httpx==0.24.1" pip install "anyio==3.7.1" pip install "asyncio==3.4.3" @@ -1845,6 +1845,28 @@ jobs: - store_test_results: path: test-results + test_nonroot_image: + machine: + image: ubuntu-2204:2023.10.1 + resource_class: xlarge + working_directory: ~/project + steps: + - checkout + - run: + name: Build Docker image + command: | + docker build -t non_root_image:latest . -f ./docker/Dockerfile.non_root + - run: + name: Install Container Structure Test + command: | + curl -LO https://github.com/GoogleContainerTools/container-structure-test/releases/download/v1.19.3/container-structure-test-linux-amd64 + chmod +x container-structure-test-linux-amd64 + sudo mv container-structure-test-linux-amd64 /usr/local/bin/container-structure-test + - run: + name: Run Container Structure Test + command: | + container-structure-test test --image non_root_image:latest --config docker/tests/nonroot.yaml + test_bad_database_url: machine: image: ubuntu-2204:2023.10.1 @@ -1910,10 +1932,10 @@ workflows: - /litellm_.*/ - litellm_assistants_api_testing: filters: - branches: - only: - - main - - /litellm_.*/ + branches: + only: + - main + - /litellm_.*/ - litellm_router_testing: filters: branches: @@ -2086,4 +2108,3 @@ workflows: branches: only: - main - diff --git a/.dockerignore b/.dockerignore index 929eace5e3..89c3c34bd7 100644 --- a/.dockerignore +++ b/.dockerignore @@ -9,3 +9,4 @@ tests .devcontainer *.tgz log.txt +docker/Dockerfile.* diff --git a/docker/Dockerfile.non_root b/docker/Dockerfile.non_root index f89e133877..3a4cdb59d5 100644 --- a/docker/Dockerfile.non_root +++ b/docker/Dockerfile.non_root @@ -9,13 +9,16 @@ FROM $LITELLM_BUILD_IMAGE AS builder # Set the working directory to /app WORKDIR /app +# Set the shell to bash +SHELL ["/bin/bash", "-o", "pipefail", "-c"] + # Install build dependencies RUN apt-get clean && apt-get update && \ apt-get install -y gcc python3-dev && \ rm -rf /var/lib/apt/lists/* -RUN pip install --upgrade pip && \ - pip install build +RUN pip install --no-cache-dir --upgrade pip && \ + pip install --no-cache-dir build # Copy the current directory contents into the container at /app COPY . . @@ -39,7 +42,7 @@ RUN pip wheel --no-cache-dir --wheel-dir=/wheels/ -r requirements.txt FROM $LITELLM_RUNTIME_IMAGE AS runtime # Update dependencies and clean up - handles debian security issue -RUN apt-get update && apt-get upgrade -y && rm -rf /var/lib/apt/lists/* +RUN apt-get update && apt-get upgrade -y && rm -rf /var/lib/apt/lists/* WORKDIR /app # Copy the current directory contents into the container at /app @@ -53,34 +56,42 @@ COPY --from=builder /wheels/ /wheels/ # Install the built wheel using pip; again using a wildcard if it's the only file RUN pip install *.whl /wheels/* --no-index --find-links=/wheels/ && rm -f *.whl && rm -rf /wheels -# install semantic-cache [Experimental]- we need this here and not in requirements.txt because redisvl pins to pydantic 1.0 -RUN pip install redisvl==0.0.7 --no-deps - +# install semantic-cache [Experimental]- we need this here and not in requirements.txt because redisvl pins to pydantic 1.0 # ensure pyjwt is used, not jwt -RUN pip uninstall jwt -y -RUN pip uninstall PyJWT -y -RUN pip install PyJWT==2.9.0 --no-cache-dir +RUN pip install redisvl==0.0.7 --no-deps --no-cache-dir && \ + pip uninstall jwt -y && \ + pip uninstall PyJWT -y && \ + pip install PyJWT==2.9.0 --no-cache-dir # Build Admin UI RUN chmod +x docker/build_admin_ui.sh && ./docker/build_admin_ui.sh -# Generate prisma client -ENV PRISMA_BINARY_CACHE_DIR=/app/prisma -RUN mkdir -p /.cache -RUN chmod -R 777 /.cache -RUN pip install nodejs-bin -RUN pip install prisma -RUN prisma generate +### Prisma Handling for Non-Root ################################################# +# Prisma allows you to specify the binary cache directory to use +ENV PRISMA_BINARY_CACHE_DIR=/nonexistent + +RUN pip install --no-cache-dir nodejs-bin prisma + +# Make a /non-existent folder and assign chown to nobody +RUN mkdir -p /nonexistent && \ + chown -R nobody:nogroup /app && \ + chown -R nobody:nogroup /nonexistent && \ + chown -R nobody:nogroup /usr/local/lib/python3.13/site-packages/prisma/ RUN chmod +x docker/entrypoint.sh RUN chmod +x docker/prod_entrypoint.sh +# Run Prisma generate as user = nobody +USER nobody + +RUN prisma generate +### End of Prisma Handling for Non-Root ######################################### + EXPOSE 4000/tcp # # Set your entrypoint and command - ENTRYPOINT ["docker/prod_entrypoint.sh"] -# Append "--detailed_debug" to the end of CMD to view detailed debug logs +# Append "--detailed_debug" to the end of CMD to view detailed debug logs # CMD ["--port", "4000", "--detailed_debug"] CMD ["--port", "4000"] diff --git a/docker/tests/nonroot.yaml b/docker/tests/nonroot.yaml new file mode 100644 index 0000000000..821b1a105a --- /dev/null +++ b/docker/tests/nonroot.yaml @@ -0,0 +1,18 @@ +schemaVersion: 2.0.0 + +metadataTest: + entrypoint: ["docker/prod_entrypoint.sh"] + user: "nobody" + workdir: "/app" + +fileExistenceTests: + - name: "Prisma Folder" + path: "/usr/local/lib/python3.13/site-packages/prisma/" + shouldExist: true + uid: 65534 + gid: 65534 + - name: "Prisma Schema" + path: "/usr/local/lib/python3.13/site-packages/prisma/schema.prisma" + shouldExist: true + uid: 65534 + gid: 65534