Code Monkey home page Code Monkey logo

functions-custom-handlers's Introduction

Azure Functions custom handlers (preview)

Every Functions app is executed by a language-specific handler. While Azure Functions supports many language handlers by default, there are cases where you may want additional control over the app execution environment. Custom handlers give you this additional control.

Custom handlers are lightweight web servers that receive events from the Functions host. Any language that supports HTTP primitives can implement a custom handler.

Custom handlers are best suited for situations where you want to:

  • Implement a Functions app in a language beyond the officially supported languages
  • Implement a Functions app in a language version or runtime not supported by default
  • Have granular control over the app execution environment

With custom handlers, all triggers and input and output bindings are supported via extension bundles.

Read more about custom handlers in detail.

Samples

The following samples demonstrate how to implement a custom handler in the following languages:

Docker

Following is an example dockerfile using azure functions node base image

# To enable ssh & remote debugging on app service change the base image to the one below
# FROM mcr.microsoft.com/azure-functions/node:2.0-appservice
FROM mcr.microsoft.com/azure-functions/node:2.0

ENV AzureWebJobsScriptRoot=/home/site/wwwroot \
    AzureFunctionsJobHost__Logging__Console__IsEnabled=true

COPY . /home/site/wwwroot

RUN cd /home/site/wwwroot

Copy any of the samples to the directory where you have dockerfile and build an image

functions-custom-handlers's People

Contributors

craigshoemaker avatar microsoft-github-operations[bot] avatar microsoftopensource avatar pragnagopa avatar rylev avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

functions-custom-handlers's Issues

Connect to cosmosDB

Hi, it is more question than issue.
I know, I can ask it on stackowerflow, but since I'm asking about official way, only you know it.

Is there official way to connect to CosmosDB through RUst custom handler ?

Thanks

How to log properly from a custom handler

Hi, I'm using a custom handler so I write a function in Rust: https://github.com/squamishaccess/squamishaccess-signup-function-rs

My main blocker right now is figuring out how to log properly.
Logging to the stdio only seems to show up in a local (VS Code) log stream, and not in the function monitor panel log stream. Ideally I'd like to make log traces to the "invocations" panel, I'm unsure if that is scraped from file system logs or Application Insights.

Additionally, it doesn't seem to parse JSON logs. Here is an example:

2020-10-23T23:05:47.930 [Trace] Sending invocation id:cae182d4-f137-4a62-bc79-76d9265924e1
2020-10-23T23:05:47.930 [Debug] Forwarding httpTrigger invocation for function: 'Paypal-IPN' invocationId: 'cae182d4-f137-4a62-bc79-76d9265924e1'
2020-10-23T23:05:47.931 [Debug] Sending invocation for function: 'Paypal-IPN' invocationId: 'cae182d4-f137-4a62-bc79-76d9265924e1'
2020-10-23T23:05:47.940 [Information] {"level":30,"time":1603494347939,"msg":"<-- Request received","method":POST,"path":/api/Paypal-IPN}
2020-10-23T23:05:47.946 [Information] {"level":30,"time":1603494347939,"msg":"cae182d4-f137-4a62-bc79-76d9265924e1 PayPal IPN Notification Event received successfully."}

The lines that say ... [Information] {"level":30, ... are from my json logger.

Golang Handler encountering System.Net.Sockets.SocketException

I have created a Golang custom handler that is running on a windows consumption plan. The Function App runtime version is 3.0.13353.0. I have followed the sample Golang code in this repo and modified it to trigger on event hub events.

My host.json file is:

{
  "version": "2.0",
  "extensionBundle": {
    "id": "Microsoft.Azure.Functions.ExtensionBundle",
    "version": "[1.*, 2.0.0)"
  },
  "extensions": {
    "eventHubs": {
      "batchCheckpointFrequency": 1,
      "eventProcessorOptions": {
        "maxBatchSize": 512,
        "prefetchCount": 1024
      }
    }
  },
  "httpWorker": {
    "description": {
      "defaultExecutablePath": "main.exe"
    }
  }
}

and my function.json file is:

{
  "bindings": [
    {
      "type": "eventHubTrigger",
      "name": "events",
      "direction": "in",
      "eventHubName": "diagnostic-logs",
      "consumerGroup": "consumer-group",
      "cardinality": "many",
      "connection": "DIAGNOSTIC_LOGS_EVENT_HUB_CONNECTION_STRING"
    }
  ]
}

In addition to modifying the code to be triggered off of event hub events, I also added the ability to fetch a secret from Key Vault on start up. Because of this, I have assigned the Function App a system assigned managed service identity and have given it permissions get secrets from a Key Vault.

My code is as follows:

package main

import (
	"context"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"os"
	"strings"

	"github.com/Azure/azure-sdk-for-go/services/keyvault/2016-10-01/keyvault"
	"github.com/Azure/go-autorest/autorest/azure"
	"github.com/Azure/go-autorest/autorest/azure/auth"
)

var (
	mySecret string
)

type InvokeResponse struct {
	Outputs     map[string]interface{}
	Logs        []string
	ReturnValue interface{}
}

type InvokeRequest struct {
	Data     map[string]interface{}
	Metadata map[string]interface{}
}

func init() {
	authorizer, err := auth.NewAuthorizerFromEnvironmentWithResource(strings.TrimSuffix(azure.PublicCloud.KeyVaultEndpoint, "/"))
	if err != nil {
		fmt.Println("Failed to create authorizer")
		os.Exit(1)
	}

	secretsClient := keyvault.New()
	secretsClient.Authorizer = authorizer

	fmt.Println("Fetching secret")
	secret, err := secretsClient.GetSecret(context.Background(), os.Getenv("AZU_KEY_VAULT_URI"), "my-secret", "")
	if err != nil {
		fmt.Printf("Failed to fetch secret: %s\n", err)
		os.Exit(1)
	}
	mySecret = *secret.Value
	fmt.Println("Successfully fetched secret")
}

func eventHubTrigger(w http.ResponseWriter, r *http.Request) {
	fmt.Println("Triggered")

	invokeResponse := InvokeResponse{Logs: []string{"success"}}

	js, err := json.Marshal(invokeResponse)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	w.Write(js)
}

func main() {
	httpInvokerPort, exists := os.LookupEnv("FUNCTIONS_HTTPWORKER_PORT")
	if exists {
		fmt.Println("FUNCTIONS_HTTPWORKER_PORT: " + httpInvokerPort)
	}
	mux := http.NewServeMux()

	mux.HandleFunc("/diagnostic_logs", eventHubTrigger)

	log.Println("Go server Listening...on httpInvokerPort:", httpInvokerPort)
	log.Fatal(http.ListenAndServe(":"+httpInvokerPort, mux))
}

The main thing to call out is the init() function which fetches the secret from Key Vault.

Every execution of the function is throwing a System.Net.Sockets.SocketException exception for me. The message for the exception is "An attempt was made to access a socket in a way forbidden by its access permissions. An attempt was made to access a socket in a way forbidden by its access permissions."

I can see the line fmt.Println("Fetching secret") printing in App Insights but nothing else seems to be printed after this. If I were to remove the fetching of the secret from Key Vault (so the whole init() function), the function executes fine.

I don't know if this issue is related to custom handlers, but I would appreciate some guidance.

Missing Timer trigger sample

Is there documentation or an example that someone could point me to for how to implement a Timer trigger?

I've reviewed the available samples and documentation but wasn't able to find anything for Golang custom handlers using a Timer trigger.

R Example Does Not Work as Docker Image

I am trying to use the R example code provided in this repo which works fine until I try and run it inside a Docker container. I have tried multiple variations on the base Dockerfile listed here as well as the R-specific one in this article: https://docs.microsoft.com/en-us/azure/azure-functions/functions-create-function-linux-custom-image

My Dockerfile presently looks like:

# To enable ssh & remote debugging on app service change the base image to the one below
# FROM mcr.microsoft.com/azure-functions/node:2.0-appservice
FROM mcr.microsoft.com/azure-functions/node:3.0

ENV AzureWebJobsScriptRoot=/home/site/wwwroot \
    AzureFunctionsJobHost__Logging__Console__IsEnabled=true

RUN apt update
RUN apt install -y r-base
RUN R -e "install.packages('httpuv', repos='http://cran.rstudio.com/')"

COPY . /home/site/wwwroot

RUN cd /home/site/wwwroot

I've attached the full logs from attempting to run this but the only failure is:

fail: Worker.HttpWorkerProcess.bbbfc2f8-34a3-4a6b-89d9-8a537071af94[0]
      Failed to start Worker Channel. Process fileName: Rscript
System.ComponentModel.Win32Exception (2): No such file or directory
   at System.Diagnostics.Process.ForkAndExecProcess(String filename, String[] argv, String[] envp, String cwd, Boolean redirectStdin, Boolean redirectStdout, Boolean redirectStderr, Boolean setCredentials, UInt32 userId, UInt32 groupId, UInt32[] groups, Int32& stdinFd, Int32& stdoutFd, Int32& stderrFd, Boolean usesTerminal, Boolean throwOnNoExec)
   at System.Diagnostics.Process.StartCore(ProcessStartInfo startInfo)
   at System.Diagnostics.Process.Start()
   at Microsoft.Azure.WebJobs.Script.Workers.WorkerProcess.StartProcessAsync() in /src/azure-functions-host/src/WebJobs.Script/Workers/ProcessManagement/WorkerProcess.cs:line 65

As a test, I ran a shell in the base image: mcr.microsoft.com/azure-functions/node:3.0 and manually executed all the build steps in the Dockerfile. They all completed successfully and I could happily execute Rscript both with and without the full path to the executable

Any help greatly appreciated

logs.txt

Example SimpleHttpTriggerWithReturn

The Go example says:
Name Trigger Input Output
SimpleHttpTriggerWithReturn | HTTP | Event Hub | n/a

Since I don't see any Event Hub in the function.json, is this a mistake in the README?

Unable to execute a simple Go custom handler

I deployed a basic Go custom handler to a brand new function app and am unable to invoke it. I get an HTTP 502 error, with the message "There is a problem with the page you are looking for, and it cannot be displayed. When the Web server (while acting as a gateway or proxy) contacted the upstream content server, it received an invalid response from the content server.".

Running func host start works just fine, so I'm not entirely sure what's causing it not to work once deployed.

host.json

{
  "version": "2.0",
  "extensionBundle": {
    "id": "Microsoft.Azure.Functions.ExtensionBundle",
    "version": "[1.*, 2.0.0)"
  },
  "httpWorker": {
    "description": {
      "defaultExecutablePath": "server.exe"
    }
  }
}

function.json

{
  "bindings": [
    {
      "type": "httpTrigger",
      "authLevel": "anonymous",
      "direction": "in",
      "methods": ["GET", "POST"],
      "name": "req"
    },
    {
      "type": "http",
      "direction": "out",
      "name": "$return"
    }
  ]
}

server.go

package main

import (
    "net/http"
    "log"
    "os"
    "fmt"
)

func handler(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("Hello World from go worker"))
}

func main() {
    httpInvokerPort, exists := os.LookupEnv("FUNCTIONS_HTTPWORKER_PORT")
    if exists {
        fmt.Println("FUNCTIONS_HTTPWORKER_PORT: " + httpInvokerPort)
    }

    log.Println("Go server Listening...on httpInvokerPort:", httpInvokerPort)

    http.HandleFunc("/convert", handler)
    log.Fatal(http.ListenAndServe(":"+httpInvokerPort, nil))
}

Directory Structure

convert/
  function.json
host.json
server.exe
server.go

Project Setup Commands

az group create --name EpubToKpx-rg --location westus
az storage account create --name epubtokpxstorage --location westus --resource-group EpubToKpx-rg --sku Standard_LRS
az functionapp create --resource-group EpubToKpx-rg --os-type Windows --consumption-plan-location westus --functions-version 3 --name epubtokpx --storage-account epubtokpxstorage
go build -o server.exe server.go
func host start
func azure functionapp publish epubtokpx

For the R sample, where does the R runtime come from?

I'm trying to understand custom handlers, and how you write them in an arbitrary language.
R is a good example of this.
The R sample here though doesn't seem to tell me anything about where the R runtime actually comes from. The only Dockerfile provided uses a "node" base image.
Naively I was expecting there to be a Dockerfile for the R sample that specified some kind of base image that contained an R runtime environment? But I probably misunderstand how custom handlers, and custom image work.

Azure Function Not Triggered by Event Subscription Despite Successful Pipeline Deployment

Description

I have set up a CI/CD pipeline in Azure DevOps for my Azure Function (written in GoLang) that seems to run successfully, including steps for building, packaging, and deploying the function. However, despite the successful deployment, the function does not get triggered when the associated Event Subscription receives events.

Pipeline Configuration

`trigger:

  • main

pool:
vmImage: 'ubuntu-latest'

variables:
directory: '.'
functionAppName: ''
resourceGroupName: '
'
azureSubscription: 'Microsoft Azure Sponsorship (
***)'

stages:

  • stage: Build
    displayName: 'Build Stage'
    jobs:

    • job: BuildJob
      displayName: 'Build Function App'
      steps:
      • task: GoTool@0
        inputs:
        version: '1.21.0'
        displayName: 'Installing Go 1.21.0'

      • script: |
        echo "Construindo a aplicação..."
        GOOS=linux GOARCH=amd64 go build -o $(directory)/main
        workingDirectory: $(directory)
        displayName: 'Build Application Binary'

      • script: |
        echo "Zip the project folder"
        zip -r config.zip .
        workingDirectory: $(directory)
        displayName: 'Zip the project folder'

      • task: PublishBuildArtifacts@1
        inputs:
        pathtoPublish: '$(directory)/config.zip'
        artifactName: 'drop'
        publishLocation: 'Container'

  • stage: Deploy
    displayName: 'Deploy Stage'
    dependsOn: Build
    condition: succeeded('Build')
    jobs:

    • job: DeployJob
      displayName: 'Deploy Function App'
      steps:
      • task: DownloadBuildArtifacts@0
        inputs:
        buildType: 'current'
        downloadType: 'single'
        artifactName: 'drop'
        downloadPath: '$(System.ArtifactsDirectory)'

      • script: |
        ls $(System.ArtifactsDirectory)/drop/
        ls $(System.ArtifactsDirectory)/
        displayName: 'Check if necessary zip is in the right directory'

      • task: AzureCLI@2
        inputs:
        azureSubscription: $(azureSubscription)
        scriptType: 'bash'
        scriptLocation: 'inlineScript'
        inlineScript: |
        az functionapp deployment source config-zip --resource-group $(resourceGroupName) --name $(functionAppName) --src $(System.ArtifactsDirectory)/drop/config.zip
        displayName: 'Deploy via Azure CLI'
        ``

      • task: AzureCLI@2
        inputs:
        azureSubscription: $(azureSubscription)
        scriptType: 'bash'
        scriptLocation: 'inlineScript'
        inlineScript: |
        az functionapp deployment list-publishing-profiles --name $(functionAppName) --resource-group $(resourceGroupName)
        displayName: 'Verify deploy via Azure CLI'
        `

host.json

{ "version": "2.0", "logging": { "logLevel": { "Function": "Information", "Host.Results": "Information", "Host.Aggregator": "Information", "FunctionApp": "Information" } }, "extensionBundle": { "id": "Microsoft.Azure.Functions.ExtensionBundle", "version": "[3.*, 4.0.0)" }, "customHandler": { "description": { "defaultExecutablePath": "main", "workingDirectory": "", "arguments": [] } } }

function.json
{
"bindings": [
{
"type": "eventGridTrigger",
"name": "eventGridEvent",
"direction": "in"
}
]
}

Issue Observed:

The pipeline completes successfully without any errors, and the deployment seems to be working fine.
However, the function does not trigger when the Event Subscription receives an event.

Attempts to Resolve:

I have verified that the Event Subscription is correctly configured and pointing to the function.
The function works as expected when deployed manually or tested locally.
There are no errors or issues in the Azure portal regarding the function or the Event Subscription.

Seeking Guidance:

Is there something I'm missing in my pipeline configuration that could cause this issue?
Are there any known issues with Azure Functions (especially custom handlers in GoLang) not being triggered by Event Subscriptions after a pipeline deployment?
Any guidance or suggestions on what else I can check or how to troubleshoot this further would be greatly appreciated.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.