Azure-Samples / fastapi-on-azure-functions

A sample to run a FastAPI app on Azure Functions
MIT License
89 stars 69 forks source link

"No job functions found" when migrating from folder structure to fast API #26

Open PranshuBansalDev opened 1 year ago

PranshuBansalDev commented 1 year ago

Please provide us with the following information:

This issue is for a: (mark with an x)

- [ x ] bug report -> please search issues before submitting
- [ ] feature request
- [ ] documentation issue or request
- [ ] regression (a behavior that used to work and stopped in a new release)

Minimal steps to reproduce

  1. Start a project where the code is generated from VSCode
  2. Try to add the FastAPI feature as suggested in this documentation

Any log messages given by the failure

image

No job functions found. Try making your job classes and methods public. If you're using binding extensions (e.g. Azure Storage, ServiceBus, Timers, etc.) make sure you've called the registration method for the extension(s) in your startup code (e.g. builder.AddAzureStorage(), builder.AddServiceBus(), builder.AddTimers(), etc.).

Here is my files:

image

Contents of WrapperFunction/__init__.py

import azure.functions as func

import fastapi

app = fastapi.FastAPI()

@app.get("/sample")
async def index():
    return {
        "info": "Try /hello/Shivani for parameterized route.",
    }

@app.get("/hello/{name}")
async def get_name(name: str):
    return {
        "name": name,
    }

Contents of function_app.py

import azure.functions as func

from WrapperFunction import app as fastapi_app

app = func.AsgiFunctionApp(app=fastapi_app, http_auth_level=func.AuthLevel.ANONYMOUS)

Contents of host.json

{
  "version": "2.0",
  "extensionBundle": {
    "id": "Microsoft.Azure.Functions.ExtensionBundle",
    "version": "[2.*, 3.0.0)"
  },
  "extensions": {
    "http": {
      "routePrefix": ""
    }
  }
}

Expected/desired behavior

Should just work like when I clone your repo

OS and Version?

Windows 7, 8 or 10. Linux (which distribution). macOS (Yosemite? El Capitan? Sierra?)

Windows 11 Pro - 22621.2134

Versions

> pip freeze           
annotated-types==0.5.0
anyio==3.7.1
azure-common==1.1.28
azure-core==1.26.4
azure-functions==1.14.0
azure-identity==1.11.0
azure-keyvault-secrets==4.6.0
certifi==2023.5.7
cffi==1.15.1
charset-normalizer==3.1.0
colorama==0.4.6
cryptography==40.0.2
et-xmlfile==1.1.0
exceptiongroup==1.1.1
fastapi==0.103.1
idna==3.4
iniconfig==2.0.0
isodate==0.6.1
msal==1.22.0
msal-extensions==1.0.0
msrest==0.7.1
numpy==1.24.3
oauthlib==3.2.2
openpyxl==3.0.10
packaging==23.1
pandas==2.0.1
pluggy==1.0.0
portalocker==2.7.0
pycparser==2.21
pydantic==2.3.0
pydantic_core==2.6.3
PyJWT==2.7.0
pyodbc==4.0.32
pytest==7.3.1
python-dateutil==2.8.2
pytz==2023.3
pywin32==306
requests==2.30.0
requests-oauthlib==1.3.1
six==1.16.0
sniffio==1.3.0
starlette==0.27.0
tomli==2.0.1
typing_extensions==4.7.1
tzdata==2023.3
urllib3==2.0.2

Mention any other details that might be useful


Thanks! We'll be in touch soon.

PranshuBansalDev commented 1 year ago

Question: How does the app know to look at function_app.py for the registration of the endpoints?

Is there any debugging I can do on my end regarding typos etc? Or is there some magic that happens behind the scenes?

I do see this in my code, but not sure why the registrations aren't working

import azure.functions as func

from WrapperFunction import app as fastapi_app

app = func.AsgiFunctionApp(app=fastapi_app, http_auth_level=func.AuthLevel.ANONYMOUS)
PranshuBansalDev commented 1 year ago

Here are some logs from func host start --verbose

[2023-09-12T17:42:52.491Z] Host configuration file read:
[2023-09-12T17:42:52.492Z] {
[2023-09-12T17:42:52.493Z]   "version": "2.0",
[2023-09-12T17:42:52.493Z]   "extensionBundle": {
[2023-09-12T17:42:52.494Z]     "id": "Microsoft.Azure.Functions.ExtensionBundle",
[2023-09-12T17:42:52.495Z]     "version": "[2.*, 3.0.0)"
[2023-09-12T17:42:52.496Z]   },
[2023-09-12T17:42:52.497Z]   "extensions": {
[2023-09-12T17:42:52.498Z]     "http": {
[2023-09-12T17:42:52.499Z]       "routePrefix": ""
[2023-09-12T17:42:52.500Z]     }
[2023-09-12T17:42:52.500Z]   }
[2023-09-12T17:42:52.501Z] }
[2023-09-12T17:42:52.761Z] FUNCTIONS_WORKER_RUNTIME set to python. Skipping WorkerConfig for language: java
[2023-09-12T17:42:52.763Z] FUNCTIONS_WORKER_RUNTIME set to python. Skipping WorkerConfig for language: node
[2023-09-12T17:42:52.764Z] FUNCTIONS_WORKER_RUNTIME set to python. Skipping WorkerConfig for language: powershell
[2023-09-12T17:42:52.847Z] Initializing Warmup Extension.
[2023-09-12T17:42:52.889Z] Initializing Host. OperationId: 'f3e2dd66-d337-4936-a2d6-5eb4c30f61d2'.
[2023-09-12T17:42:52.898Z] Host initialization: ConsecutiveErrors=0, StartupCount=1, OperationId=f3e2dd66-d337-4936-a2d6-5eb4c30f61d2
[2023-09-12T17:42:52.937Z] LoggerFilterOptions
[2023-09-12T17:42:52.938Z] {
[2023-09-12T17:42:52.939Z]   "MinLevel": "None",
[2023-09-12T17:42:52.939Z]   "Rules": [
[2023-09-12T17:42:52.940Z]     {
[2023-09-12T17:42:52.941Z]       "ProviderName": null,
[2023-09-12T17:42:52.941Z]       "CategoryName": null,
[2023-09-12T17:42:52.942Z]       "LogLevel": null,
[2023-09-12T17:42:52.943Z]       "Filter": "<AddFilter>b__0"
[2023-09-12T17:42:52.944Z]     },
[2023-09-12T17:42:52.944Z]     {
[2023-09-12T17:42:52.945Z]       "ProviderName": "Microsoft.Azure.WebJobs.Script.WebHost.Diagnostics.SystemLoggerProvider",
[2023-09-12T17:42:52.945Z]       "CategoryName": null,
[2023-09-12T17:42:52.946Z]       "LogLevel": "None",
[2023-09-12T17:42:52.947Z]       "Filter": null
[2023-09-12T17:42:52.948Z]     },
[2023-09-12T17:42:52.948Z]     {
[2023-09-12T17:42:52.949Z]       "ProviderName": "Microsoft.Azure.WebJobs.Script.WebHost.Diagnostics.SystemLoggerProvider",
[2023-09-12T17:42:52.950Z]       "CategoryName": null,
[2023-09-12T17:42:52.951Z]       "LogLevel": null,
[2023-09-12T17:42:52.951Z]       "Filter": "<AddFilter>b__0"
[2023-09-12T17:42:52.952Z]     },
[2023-09-12T17:42:52.953Z]     {
[2023-09-12T17:42:52.954Z]       "ProviderName": "Azure.Functions.Cli.Diagnostics.ColoredConsoleLoggerProvider",
[2023-09-12T17:42:52.954Z]       "CategoryName": null,
[2023-09-12T17:42:52.955Z]       "LogLevel": null,
[2023-09-12T17:42:52.955Z]       "Filter": "<AddFilter>b__0"
[2023-09-12T17:42:52.956Z]     }
[2023-09-12T17:42:52.957Z]   ]
[2023-09-12T17:42:52.958Z] }
[2023-09-12T17:42:52.959Z] LoggerFilterOptions
[2023-09-12T17:42:52.959Z] {
[2023-09-12T17:42:52.960Z]   "MinLevel": "None",
[2023-09-12T17:42:52.961Z]   "Rules": [
[2023-09-12T17:42:52.962Z]     {
[2023-09-12T17:42:52.962Z]       "ProviderName": null,
[2023-09-12T17:42:52.963Z]       "CategoryName": null,
[2023-09-12T17:42:52.964Z]       "LogLevel": null,
[2023-09-12T17:42:52.965Z]       "Filter": "<AddFilter>b__0"
[2023-09-12T17:42:52.965Z]     },
[2023-09-12T17:42:52.966Z]     {
[2023-09-12T17:42:52.966Z]       "ProviderName": "Microsoft.Azure.WebJobs.Script.WebHost.Diagnostics.SystemLoggerProvider",
[2023-09-12T17:42:52.967Z]       "CategoryName": null,
[2023-09-12T17:42:52.968Z]       "LogLevel": "None",
[2023-09-12T17:42:52.969Z]       "Filter": null
[2023-09-12T17:42:52.970Z]     },
[2023-09-12T17:42:52.970Z]     {
[2023-09-12T17:42:52.971Z]       "ProviderName": "Microsoft.Azure.WebJobs.Script.WebHost.Diagnostics.SystemLoggerProvider",
[2023-09-12T17:42:52.972Z]       "CategoryName": null,
[2023-09-12T17:42:52.972Z]       "LogLevel": null,
[2023-09-12T17:42:52.973Z]       "Filter": "<AddFilter>b__0"
[2023-09-12T17:42:52.974Z]     },
[2023-09-12T17:42:52.974Z]     {
[2023-09-12T17:42:52.975Z]       "ProviderName": "Azure.Functions.Cli.Diagnostics.ColoredConsoleLoggerProvider",
[2023-09-12T17:42:52.976Z]       "CategoryName": null,
[2023-09-12T17:42:52.977Z]       "LogLevel": null,
[2023-09-12T17:42:52.978Z]       "Filter": "<AddFilter>b__0"
[2023-09-12T17:42:52.978Z]     }
[2023-09-12T17:42:52.979Z]   ]
[2023-09-12T17:42:52.979Z] }
[2023-09-12T17:42:52.980Z] ConcurrencyOptions
[2023-09-12T17:42:52.981Z] {
[2023-09-12T17:42:52.982Z]   "DynamicConcurrencyEnabled": false,
[2023-09-12T17:42:52.982Z]   "MaximumFunctionConcurrency": 500,
[2023-09-12T17:42:52.983Z]   "CPUThreshold": 0.8,
[2023-09-12T17:42:52.984Z]   "SnapshotPersistenceEnabled": true
[2023-09-12T17:42:52.985Z] }
[2023-09-12T17:42:52.985Z] FunctionResultAggregatorOptions
[2023-09-12T17:42:52.986Z] {
[2023-09-12T17:42:52.987Z]   "BatchSize": 1000,
[2023-09-12T17:42:52.988Z]   "FlushTimeout": "00:00:30",
[2023-09-12T17:42:52.988Z]   "IsEnabled": true
[2023-09-12T17:42:52.989Z] }
[2023-09-12T17:42:52.990Z] SingletonOptions
[2023-09-12T17:42:52.990Z] {
[2023-09-12T17:42:52.991Z]   "LockPeriod": "00:00:15",
[2023-09-12T17:42:52.992Z]   "ListenerLockPeriod": "00:00:15",
[2023-09-12T17:42:52.993Z]   "LockAcquisitionTimeout": "10675199.02:48:05.4775807",
[2023-09-12T17:42:52.994Z]   "LockAcquisitionPollingInterval": "00:00:05",
[2023-09-12T17:42:52.994Z]   "ListenerLockRecoveryPollingInterval": "00:01:00"
[2023-09-12T17:42:52.995Z] }
[2023-09-12T17:42:52.996Z] ScaleOptions
[2023-09-12T17:42:52.996Z] {
[2023-09-12T17:42:52.997Z]   "ScaleMetricsMaxAge": "00:02:00",
[2023-09-12T17:42:52.998Z]   "ScaleMetricsSampleInterval": "00:00:10",
[2023-09-12T17:42:52.999Z]   "MetricsPurgeEnabled": true,
[2023-09-12T17:42:53.000Z]   "IsTargetScalingEnabled": true,
[2023-09-12T17:42:53.000Z]   "IsRuntimeScalingEnabled": false
[2023-09-12T17:42:53.001Z] }
[2023-09-12T17:42:53.003Z] Starting JobHost
[2023-09-12T17:42:53.006Z] Starting Host (HostId=pbhome-108974405, InstanceId=b5965fdb-7ae4-41ed-85ef-bf902ab88576, Version=4.24.5.21262, ProcessId=46356, AppDomainId=1, InDebugMode=False, InDiagnosticMode=False, FunctionsExtensionVersion=(null))
[2023-09-12T17:42:53.020Z] Loading functions metadata
[2023-09-12T17:42:53.021Z] Reading functions metadata
[2023-09-12T17:42:53.022Z] 0 functions found
[2023-09-12T17:42:53.029Z] Reading functions metadata
[2023-09-12T17:42:53.041Z] 1 functions found
[2023-09-12T17:42:53.042Z] 0 functions loaded
[2023-09-12T17:42:53.058Z] Generating 0 job function(s)
[2023-09-12T17:42:53.084Z] No job functions found. Try making your job classes and methods public. If you're using binding extensions (e.g. Azure Storage, ServiceBus, Timers, etc.) make sure you've called the registration method for the extension(s) in your startup code (e.g. builder.AddAzureStorage(), builder.AddServiceBus(), builder.AddTimers(), etc.).
[2023-09-12T17:42:53.093Z] HttpOptions
[2023-09-12T17:42:53.094Z] {
[2023-09-12T17:42:53.094Z] Initializing function HTTP routes
[2023-09-12T17:42:53.095Z]   "DynamicThrottlesEnabled": false,
[2023-09-12T17:42:53.095Z] No HTTP routes mapped
[2023-09-12T17:42:53.096Z]   "EnableChunkedRequestBinding": false,
[2023-09-12T17:42:53.097Z]
[2023-09-12T17:42:53.097Z]   "MaxConcurrentRequests": -1,
[2023-09-12T17:42:53.098Z]   "MaxOutstandingRequests": -1,
[2023-09-12T17:42:53.099Z]   "RoutePrefix": ""
[2023-09-12T17:42:53.100Z] }
[2023-09-12T17:42:53.105Z] Host initialized (89ms)
[2023-09-12T17:42:53.109Z] Host started (100ms)
[2023-09-12T17:42:53.110Z] Job host started
[2023-09-12T17:42:57.952Z] Host lock lease acquired by instance ID '000000000000000000000000506973E3'.
PranshuBansalDev commented 1 year ago

AH figured it out

@pamelafox - could you please update this to a doc bug?

Essentially you have to have this value present in your local.settings.json for this to work:

{
  "IsEncrypted": false,
  "Values": {
    "FUNCTIONS_WORKER_RUNTIME": "python",
    "AzureWebJobsFeatureFlags": "EnableWorkerIndexing" <<< IMPORTANT - will not detect functions otherwise
  },
  "Host": {
    "CORS": "*"
  }
}
pamelafox commented 1 year ago

Yes, that seems to still be required. I made the change in the repo, did you have an older version?

https://github.com/Azure-Samples/fastapi-on-azure-functions/blob/f00faf65b34caee539388c65729ed8cbf8dfa6c9/local.settings.json#L5

PranshuBansalDev commented 1 year ago

This repo works great - the issue is that neither these docs have any references to this variable, or why its required

  1. https://learn.microsoft.com/en-us/samples/azure-samples/fastapi-on-azure-functions/azure-functions-python-create-fastapi-app/
  2. https://github.com/Azure-Samples/fastapi-on-azure-functions/blob/main/README.md

So if you're trying to migrate code from an existing repo that does not use the WrapperFunction pattern, it is not clear how to resolve this bug

pamelafox commented 1 year ago

Okay yep, makes sense. I think the hope was that the flag wouldn't be needed for very long, but it looks like it's still needed, so I've pinged the Functions team about it.

PranshuBansalDev commented 1 year ago

Awesome thanks! Feel free to resolve/close once docs have been updated

Would be great to add something in the README short term