Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
6e04691
Consumption tests fix
gavin-aguiar Jul 11, 2025
37f7a90
Merge branch 'dev' of github.com:Azure/azure-functions-python-worker …
gavin-aguiar Jul 11, 2025
570988b
Merge branch 'dev' into gaaguiar/consumption_test_fix
gavin-aguiar Jul 23, 2025
63b4c80
Updated blob name
gavin-aguiar Jul 23, 2025
5d948d9
Merge branch 'gaaguiar/consumption_test_fix' of github.com:Azure/azur…
gavin-aguiar Jul 23, 2025
670663e
Uncommenting LC tests
gavin-aguiar Jul 23, 2025
d605af3
Updated imports
gavin-aguiar Jul 23, 2025
7b52f98
Updated tests to run on an emulator
gavin-aguiar Jul 28, 2025
08a9f59
Added sas token
gavin-aguiar Jul 28, 2025
402b3e2
Fixed syntax error
gavin-aguiar Jul 28, 2025
03af218
upload one zip, verify, run one test
hallvictoria Jul 29, 2025
8fc6158
logging variables
hallvictoria Jul 29, 2025
57042a6
change base url
hallvictoria Jul 29, 2025
18f8641
run all tests
hallvictoria Jul 29, 2025
ee22e1f
only run 3.13, test with loader.install()
hallvictoria Jul 30, 2025
daad041
Removed protobuf tests
gavin-aguiar Jul 31, 2025
840d000
Merge branch 'gaaguiar/consumption_test_fix' of github.com:Azure/azur…
gavin-aguiar Jul 31, 2025
6eaeee9
Removing debugging changes
gavin-aguiar Aug 1, 2025
ebff4a1
Updating pipeline to run tests on all versions
gavin-aguiar Aug 1, 2025
ea1664c
Merge branch 'dev' into gaaguiar/consumption_test_fix
gavin-aguiar Aug 1, 2025
b0818b5
Merge branch 'dev' into gaaguiar/consumption_test_fix
gavin-aguiar Aug 1, 2025
aa439f1
Merge branch 'dev' into gaaguiar/consumption_test_fix
gavin-aguiar Aug 4, 2025
3091aa0
Skipping 3.13 tests
gavin-aguiar Aug 5, 2025
201c366
Flake8 fixes
gavin-aguiar Aug 5, 2025
a3318ab
Removed debug logs
gavin-aguiar Aug 6, 2025
7271be3
Renamed function app dir
gavin-aguiar Aug 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions eng/ci/integration-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,7 @@ extends:
dependsOn: []
jobs:
- template: /eng/templates/official/jobs/ci-e2e-tests.yml@self
# Skipping consumption tests till pipeline is fixed
# - stage: RunLCTests
# dependsOn: []
# jobs:
# - template: /eng/templates/official/jobs/ci-lc-tests.yml@self
- stage: RunLCTests
dependsOn: []
jobs:
- template: /eng/templates/official/jobs/ci-lc-tests.yml@self
99 changes: 85 additions & 14 deletions eng/templates/official/jobs/ci-lc-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,34 @@ jobs:

strategy:
matrix:
Python37:
PYTHON_VERSION: '3.7'
Python38:
PYTHON_VERSION: '3.8'
Python39:
PYTHON_VERSION: '3.9'
Python310:
PYTHON_VERSION: '3.10'
Python311:
PYTHON_VERSION: '3.11'

Python39:
PYTHON_VERSION: '3.9'
Python310:
PYTHON_VERSION: '3.10'
Python311:
PYTHON_VERSION: '3.11'
Python312:
PYTHON_VERSION: '3.12'
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: $(PYTHON_VERSION)

- bash: |
# Start Azurite storage emulator in the background
docker run -d -p 10000:10000 -p 10001:10001 -p 10002:10002 \
--name azurite-storage \
mcr.microsoft.com/azure-storage/azurite:latest \
azurite --blobHost 0.0.0.0 --queueHost 0.0.0.0 --tableHost 0.0.0.0

# Wait for Azurite to be ready
sleep 5

# Verify Azurite is running
docker ps | grep azurite-storage
displayName: 'Start Azurite Storage Emulator'
condition: and(eq(variables.isSdkRelease, false), eq(variables.isExtensionsRelease, false), eq(variables['USETESTPYTHONSDK'], false), eq(variables['USETESTPYTHONEXTENSIONS'], false))

- bash: |
python -m pip install --upgrade pip
python -m pip install -U -e ${{ parameters.PROJECT_DIRECTORY }}/[dev]
Expand All @@ -36,11 +49,69 @@ jobs:
displayName: 'Install dependencies and the worker'
# Skip the installation stage for SDK and Extensions release branches. This stage will fail because pyproject.toml contains the updated (and unreleased) library version
condition: and(eq(variables.isSdkRelease, false), eq(variables.isExtensionsRelease, false), eq(variables['USETESTPYTHONSDK'], false), eq(variables['USETESTPYTHONEXTENSIONS'], false))

- bash: |
# Install Azure CLI (if not already present)
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash

# Create the apps container in Azurite
az storage container create \
--name apps \
--connection-string "$(AZURE_STORAGE_CONNECTION_STRING)"

# Upload all function app packages to the container
FUNCTION_APPS_DIR="$(Build.SourcesDirectory)/${{ parameters.PROJECT_DIRECTORY }}/tests/consumption_tests/function_app_zips"
for zipfile in "$FUNCTION_APPS_DIR"/*.zip; do
filename=$(basename "$zipfile")
echo "Uploading $filename..."
az storage blob upload \
--container-name apps \
--name "$filename" \
--file "$zipfile" \
--connection-string "$(AZURE_STORAGE_CONNECTION_STRING)" \
--overwrite
done

# Generate a container-level SAS token valid for 1 day
SAS_TOKEN=$(az storage container generate-sas \
--name apps \
--permissions r \
--expiry $(date -u -d '+1 day' +%Y-%m-%dT%H:%M:%SZ) \
--connection-string "$(AZURE_STORAGE_CONNECTION_STRING)" \
--output tsv)

echo "##vso[task.setvariable variable=CONTAINER_SAS_TOKEN]$SAS_TOKEN"

# List blobs in the container to verify uploads
echo "Verifying uploaded blobs in 'apps' container..."
az storage blob list \
--container-name apps \
--connection-string "$(AZURE_STORAGE_CONNECTION_STRING)" \
--output table

env:
AZURE_STORAGE_CONNECTION_STRING: $(AZURE_STORAGE_CONNECTION_STRING)

displayName: 'Setup Function App Packages in Azurite'
condition: and(eq(variables.isSdkRelease, false), eq(variables.isExtensionsRelease, false), eq(variables['USETESTPYTHONSDK'], false), eq(variables['USETESTPYTHONEXTENSIONS'], false))

- powershell: |
Write-Host "CONTAINER_SAS_TOKEN: $(CONTAINER_SAS_TOKEN)"
displayName: 'Display CONTAINER_SAS_TOKEN variable'

- bash: |
python -m pytest -n auto --dist loadfile -vv --reruns 4 --instafail tests/consumption_tests
python -m pytest -n auto --dist loadfile -vv --instafail tests/consumption_tests
env:
AzureWebJobsStorage: $(LinuxStorageConnectionString312)
AzureWebJobsStorage: $(AZURE_STORAGE_CONNECTION_STRING)
_DUMMY_CONT_KEY: $(_DUMMY_CONT_KEY)
CONTAINER_SAS_TOKEN: $(CONTAINER_SAS_TOKEN)
displayName: "Running $(PYTHON_VERSION) Linux Consumption tests"
workingDirectory: $(Build.SourcesDirectory)/${{ parameters.PROJECT_DIRECTORY }}
condition: and(eq(variables.isSdkRelease, false), eq(variables.isExtensionsRelease, false), eq(variables['USETESTPYTHONSDK'], false), eq(variables['USETESTPYTHONEXTENSIONS'], false))
condition: and(eq(variables.isSdkRelease, false), eq(variables.isExtensionsRelease, false), eq(variables['USETESTPYTHONSDK'], false), eq(variables['USETESTPYTHONEXTENSIONS'], false))

- bash: |
# Cleanup: Stop and remove Azurite container
docker stop azurite-storage || true
docker rm azurite-storage || true
displayName: 'Cleanup Azurite Storage Emulator'
condition: always()
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
135 changes: 13 additions & 122 deletions workers/tests/consumption_tests/test_linux_consumption.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,21 @@
import os
import sys
from time import sleep
from unittest import TestCase, skipIf

from requests import Request
from tests.utils.testutils_lc import LinuxConsumptionWebHostController
from unittest import TestCase, skip, skipIf

from azure_functions_worker.constants import (
PYTHON_ENABLE_DEBUG_LOGGING,
PYTHON_ENABLE_INIT_INDEXING,
PYTHON_ENABLE_WORKER_EXTENSIONS,
PYTHON_ISOLATE_WORKER_DEPENDENCIES,
)
from requests import Request
from tests.utils.testutils_lc import LinuxConsumptionWebHostController

_DEFAULT_HOST_VERSION = "4"


class TestLinuxConsumption(TestCase):
"""Test worker behaviors on specific scenarios.

SCM_RUN_FROM_PACKAGE: built function apps are acquired from
-> "Simple Batch" Subscription
-> "AzureFunctionsPythonWorkerCILinuxDevOps" Resource Group
-> "pythonworker<python_major><python_minor>sa" Storage Account
-> "python-worker-lc-apps" Blob Container

For a list of scenario names:
https://pythonworker39sa.blob.core.windows.net/python-worker-lc-apps?restype=container&comp=list
"""

@classmethod
def setUpClass(cls):
Expand Down Expand Up @@ -65,6 +53,8 @@ def test_http_no_auth(self):
resp = ctrl.send_request(req)
self.assertEqual(resp.status_code, 200)

@skipIf(sys.version_info.minor != 11,
"Uploaded common libraries are only supported for Python 3.11")
def test_common_libraries(self):
"""A function app with the following requirements.txt:

Expand Down Expand Up @@ -95,66 +85,6 @@ def test_common_libraries(self):
self.assertIn('pyodbc', content)
self.assertIn('requests', content)

@skipIf(sys.version_info.minor in (10, 11),
"Protobuf pinning fails during remote build")
def test_new_protobuf(self):
"""A function app with the following requirements.txt:

azure-functions==1.7.0
protobuf==3.15.8
grpcio==1.33.2

should return 200 after importing all libraries.
"""
with LinuxConsumptionWebHostController(_DEFAULT_HOST_VERSION,
self._py_version) as ctrl:
ctrl.assign_container(env={
"AzureWebJobsStorage": self._storage,
"SCM_RUN_FROM_PACKAGE": self._get_blob_url("NewProtobuf"),
PYTHON_ISOLATE_WORKER_DEPENDENCIES: "1"
})
req = Request('GET', f'{ctrl.url}/api/HttpTrigger')
resp = ctrl.send_request(req)
self.assertEqual(resp.status_code, 200)

content = resp.json()

# Worker always picks up the SDK version bundled with the image
# Version of the packages are inconsistent due to isolation's bug
self.assertEqual(content['azure.functions'], '1.7.0')
self.assertEqual(content['google.protobuf'], '3.15.8')
self.assertEqual(content['grpc'], '1.33.2')

@skipIf(sys.version_info.minor in (10, 11),
"Protobuf pinning fails during remote build")
def test_old_protobuf(self):
"""A function app with the following requirements.txt:

azure-functions==1.5.0
protobuf==3.8.0
grpcio==1.27.1

should return 200 after importing all libraries.
"""
with LinuxConsumptionWebHostController(_DEFAULT_HOST_VERSION,
self._py_version) as ctrl:
ctrl.assign_container(env={
"AzureWebJobsStorage": self._storage,
"SCM_RUN_FROM_PACKAGE": self._get_blob_url("OldProtobuf"),
PYTHON_ISOLATE_WORKER_DEPENDENCIES: "1"
})
req = Request('GET', f'{ctrl.url}/api/HttpTrigger')
resp = ctrl.send_request(req)
self.assertEqual(resp.status_code, 200)

content = resp.json()

# Worker always picks up the SDK version bundled with the image
# Version of the packages are inconsistent due to isolation's bug
self.assertIn(content['azure.functions'], '1.5.0')
self.assertIn(content['google.protobuf'], '3.8.0')
self.assertIn(content['grpc'], '1.27.1')

def test_debug_logging_disabled(self):
"""An HttpTrigger function app with 'azure-functions' library
should return 200 and by default customer debug logging should be
Expand Down Expand Up @@ -230,8 +160,6 @@ def test_pinning_functions_to_older_version(self):
self.assertEqual(resp.status_code, 200)
self.assertIn("Func Version: 1.11.1", resp.text)

@skipIf(sys.version_info.minor != 10,
"This is testing only for python310")
def test_opencensus_with_extensions_enabled(self):
"""A function app with extensions enabled containing the
following libraries:
Expand All @@ -251,8 +179,6 @@ def test_opencensus_with_extensions_enabled(self):
resp = ctrl.send_request(req)
self.assertEqual(resp.status_code, 200)

@skipIf(sys.version_info.minor != 10,
"This is testing only for python310")
def test_opencensus_with_extensions_enabled_init_indexing(self):
"""
A function app with init indexing enabled
Expand All @@ -269,43 +195,6 @@ def test_opencensus_with_extensions_enabled_init_indexing(self):
resp = ctrl.send_request(req)
self.assertEqual(resp.status_code, 200)

@skipIf(sys.version_info.minor != 9,
"This is testing only for python39 where extensions"
"enabled by default")
def test_reload_variables_after_timeout_error(self):
"""
A function app with HTTPtrigger which has a function timeout of
20s. The app as a sleep of 30s which should trigger a timeout
"""
with LinuxConsumptionWebHostController(_DEFAULT_HOST_VERSION,
self._py_version) as ctrl:
ctrl.assign_container(env={
"AzureWebJobsStorage": self._storage,
"SCM_RUN_FROM_PACKAGE": self._get_blob_url(
"TimeoutError"),
PYTHON_ISOLATE_WORKER_DEPENDENCIES: "1"
})
req = Request('GET', f'{ctrl.url}/api/hello')
resp = ctrl.send_request(req)
self.assertEqual(resp.status_code, 500)

sleep(2)
logs = ctrl.get_container_logs()
self.assertRegex(
logs,
r"Applying prioritize_customer_dependencies: "
r"worker_dependencies_path: \/azure-functions-host\/"
r"workers\/python\/.*?\/LINUX\/X64,"
r" customer_dependencies_path: \/home\/site\/wwwroot\/"
r"\.python_packages\/lib\/site-packages, working_directory:"
r" \/home\/site\/wwwroot, Linux Consumption: True,"
r" Placeholder: False")
self.assertNotIn("Failure Exception: ModuleNotFoundError",
logs)

@skipIf(sys.version_info.minor != 9,
"This is testing only for python39 where extensions"
"enabled by default")
def test_reload_variables_after_oom_error(self):
"""
A function app with HTTPtrigger mocking error code 137
Expand Down Expand Up @@ -337,8 +226,7 @@ def test_reload_variables_after_oom_error(self):
self.assertNotIn("Failure Exception: ModuleNotFoundError",
logs)

@skipIf(sys.version_info.minor != 10,
"This is testing only for python310")
@skip("Flaky test.")
def test_http_v2_fastapi_streaming_upload_download(self):
"""
A function app using http v2 fastapi extension with streaming upload and
Expand Down Expand Up @@ -377,7 +265,10 @@ def generate_random_bytes_stream():
streamed_data, b'streamingtestingresponseisreturned')

def _get_blob_url(self, scenario_name: str) -> str:
return (
f'https://pythonworker{self._py_shortform}sa.blob.core.windows.net/'
f'python-worker-lc-apps/{scenario_name}{self._py_shortform}.zip'
)
base_url = "http://172.17.0.1:10000/devstoreaccount1/apps"

container_sas_token = os.getenv('CONTAINER_SAS_TOKEN')
if not container_sas_token:
raise RuntimeError('Environment variable CONTAINER_SAS_TOKEN is '
'required before running Linux Consumption test')
return f"{base_url}/{scenario_name}.zip?{container_sas_token}"
Loading
Loading