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.