From f01658da59db77ab353b23e9cbf08e9f26c90ae5 Mon Sep 17 00:00:00 2001 From: Eric Jizba Date: Tue, 17 Oct 2023 20:22:46 -0700 Subject: [PATCH 1/3] Update protobuf subtree to tag v1.10.0-protofile https://github.com/Azure/azure-functions-language-worker-protobuf/releases/tag/v1.10.0-protofile --- .../src/proto/FunctionRpc.proto | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/azure-functions-language-worker-protobuf/src/proto/FunctionRpc.proto b/azure-functions-language-worker-protobuf/src/proto/FunctionRpc.proto index 37297640..110df5d2 100644 --- a/azure-functions-language-worker-protobuf/src/proto/FunctionRpc.proto +++ b/azure-functions-language-worker-protobuf/src/proto/FunctionRpc.proto @@ -245,6 +245,14 @@ message FunctionEnvironmentReloadRequest { } message FunctionEnvironmentReloadResponse { + enum CapabilitiesUpdateStrategy { + // overwrites existing values and appends new ones + // ex. worker init: {A: foo, B: bar} + env reload: {A:foo, B: foo, C: foo} -> {A: foo, B: foo, C: foo} + merge = 0; + // existing capabilities are cleared and new capabilities are applied + // ex. worker init: {A: foo, B: bar} + env reload: {A:foo, C: foo} -> {A: foo, C: foo} + replace = 1; + } // After specialization, worker sends capabilities & metadata. // Worker metadata captured for telemetry purposes WorkerMetadata worker_metadata = 1; @@ -254,6 +262,9 @@ message FunctionEnvironmentReloadResponse { // Status of the response StatusResult result = 3; + + // If no strategy is defined, the host will default to merge + CapabilitiesUpdateStrategy capabilities_update_strategy = 4; } // Tell the out-of-proc worker to close any shared memory maps it allocated for given invocation From 0b39a9bec2f694fdcc9b191889927c4007c00aee Mon Sep 17 00:00:00 2001 From: Eric Jizba Date: Tue, 17 Oct 2023 21:05:03 -0700 Subject: [PATCH 2/3] Allow core api to modify capabilities And log outside of an invocation --- src/coreApi/coreApiLog.ts | 14 +++++++++++ .../FunctionEnvironmentReloadHandler.ts | 5 ++++ src/eventHandlers/InvocationHandler.ts | 2 +- src/eventHandlers/WorkerInitHandler.ts | 12 ++-------- src/eventHandlers/getWorkerCapabilities.ts | 24 +++++++++++++++++++ src/setupCoreModule.ts | 2 ++ types-core/index.d.ts | 15 +++++++++++- 7 files changed, 62 insertions(+), 12 deletions(-) create mode 100644 src/coreApi/coreApiLog.ts create mode 100644 src/eventHandlers/getWorkerCapabilities.ts diff --git a/src/coreApi/coreApiLog.ts b/src/coreApi/coreApiLog.ts new file mode 100644 index 00000000..4e3b9e21 --- /dev/null +++ b/src/coreApi/coreApiLog.ts @@ -0,0 +1,14 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. + +import * as coreTypes from '@azure/functions-core'; +import { worker } from '../WorkerContext'; +import { fromCoreLogCategory, fromCoreLogLevel } from './converters/fromCoreStatusResult'; + +export function coreApiLog(level: coreTypes.RpcLogLevel, category: coreTypes.RpcLogCategory, message: string): void { + worker.log({ + message, + level: fromCoreLogLevel(level), + logCategory: fromCoreLogCategory(category), + }); +} diff --git a/src/eventHandlers/FunctionEnvironmentReloadHandler.ts b/src/eventHandlers/FunctionEnvironmentReloadHandler.ts index 6a1660bf..f972effb 100644 --- a/src/eventHandlers/FunctionEnvironmentReloadHandler.ts +++ b/src/eventHandlers/FunctionEnvironmentReloadHandler.ts @@ -5,9 +5,11 @@ import { AzureFunctionsRpcMessages as rpc } from '../../azure-functions-language import { worker } from '../WorkerContext'; import { startApp } from '../startApp'; import { EventHandler } from './EventHandler'; +import { getWorkerCapabilities } from './getWorkerCapabilities'; import { getWorkerMetadata } from './getWorkerMetadata'; import LogCategory = rpc.RpcLog.RpcLogCategory; import LogLevel = rpc.RpcLog.Level; +import CapabilitiesUpdateStrategy = rpc.FunctionEnvironmentReloadResponse.CapabilitiesUpdateStrategy; /** * Environment variables from the current process @@ -55,6 +57,9 @@ export class FunctionEnvironmentReloadHandler extends EventHandler< response.workerMetadata = getWorkerMetadata(); } + response.capabilities = await getWorkerCapabilities(); + response.capabilitiesUpdateStrategy = CapabilitiesUpdateStrategy.replace; + return response; } } diff --git a/src/eventHandlers/InvocationHandler.ts b/src/eventHandlers/InvocationHandler.ts index b252369d..a4a9fb19 100644 --- a/src/eventHandlers/InvocationHandler.ts +++ b/src/eventHandlers/InvocationHandler.ts @@ -66,7 +66,7 @@ export class InvocationHandler extends EventHandler<'invocationRequest', 'invoca ); const programmingModel: ProgrammingModel = nonNullProp(worker.app, 'programmingModel'); - const invocModel = programmingModel.getInvocationModel(coreCtx); + const invocModel = await programmingModel.getInvocationModel(coreCtx); const hookData: HookData = {}; let { context, inputs } = await invocModel.getArguments(); diff --git a/src/eventHandlers/WorkerInitHandler.ts b/src/eventHandlers/WorkerInitHandler.ts index 37f7ebca..b333cf10 100644 --- a/src/eventHandlers/WorkerInitHandler.ts +++ b/src/eventHandlers/WorkerInitHandler.ts @@ -9,6 +9,7 @@ import { isError } from '../errors'; import { startApp } from '../startApp'; import { nonNullProp } from '../utils/nonNull'; import { EventHandler } from './EventHandler'; +import { getWorkerCapabilities } from './getWorkerCapabilities'; import { getWorkerMetadata } from './getWorkerMetadata'; import LogCategory = rpc.RpcLog.RpcLogCategory; import LogLevel = rpc.RpcLog.Level; @@ -44,16 +45,7 @@ export class WorkerInitHandler extends EventHandler<'workerInitRequest', 'worker response.workerMetadata = getWorkerMetadata(); } - response.capabilities = { - RawHttpBodyBytes: 'true', - RpcHttpTriggerMetadataRemoved: 'true', - RpcHttpBodyOnly: 'true', - IgnoreEmptyValuedRpcHttpHeaders: 'true', - UseNullableValueDictionaryForHttp: 'true', - WorkerStatus: 'true', - TypedDataCollection: 'true', - HandlesWorkerTerminateMessage: 'true', - }; + response.capabilities = await getWorkerCapabilities(); return response; } diff --git a/src/eventHandlers/getWorkerCapabilities.ts b/src/eventHandlers/getWorkerCapabilities.ts new file mode 100644 index 00000000..24459534 --- /dev/null +++ b/src/eventHandlers/getWorkerCapabilities.ts @@ -0,0 +1,24 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. + +import { WorkerCapabilities } from '@azure/functions-core'; +import { worker } from '../WorkerContext'; + +export async function getWorkerCapabilities(): Promise { + let capabilities: WorkerCapabilities = { + RawHttpBodyBytes: 'true', + RpcHttpTriggerMetadataRemoved: 'true', + RpcHttpBodyOnly: 'true', + IgnoreEmptyValuedRpcHttpHeaders: 'true', + UseNullableValueDictionaryForHttp: 'true', + WorkerStatus: 'true', + TypedDataCollection: 'true', + HandlesWorkerTerminateMessage: 'true', + }; + + if (worker.app.programmingModel?.getCapabilities) { + capabilities = await worker.app.programmingModel.getCapabilities(capabilities); + } + + return capabilities; +} diff --git a/src/setupCoreModule.ts b/src/setupCoreModule.ts index 10d62d1f..c20d60cf 100644 --- a/src/setupCoreModule.ts +++ b/src/setupCoreModule.ts @@ -4,6 +4,7 @@ import { Disposable } from './Disposable'; import { worker } from './WorkerContext'; import { version } from './constants'; +import { coreApiLog } from './coreApi/coreApiLog'; import { registerFunction } from './coreApi/registerFunction'; import { setProgrammingModel } from './coreApi/setProgrammingModel'; import { registerHook } from './hooks/registerHook'; @@ -25,6 +26,7 @@ export function setupCoreModule(): void { getProgrammingModel: () => { return worker.app.programmingModel; }, + log: coreApiLog, registerFunction, Disposable, }; diff --git a/types-core/index.d.ts b/types-core/index.d.ts index 73d1642e..4d6a1b01 100644 --- a/types-core/index.d.ts +++ b/types-core/index.d.ts @@ -185,6 +185,12 @@ declare module '@azure/functions-core' { */ function getProgrammingModel(): ProgrammingModel; + /** + * The recommended way to log information outside the context of an invocation + * During an invocation, use `CoreInvocationContext.log` instead + */ + function log(level: RpcLogLevel, category: RpcLogCategory, message: string): void; + /** * A set of information and methods that describe the model for handling a Node.js function app * Currently, this is mainly focused on invocation @@ -203,9 +209,16 @@ declare module '@azure/functions-core' { /** * Returns a new instance of the invocation model for each invocation */ - getInvocationModel(coreContext: CoreInvocationContext): InvocationModel; + getInvocationModel(coreContext: CoreInvocationContext): InvocationModel | Promise; + + /** + * Optional method to modify worker capabilities + */ + getCapabilities?(defaultCapabilities: WorkerCapabilities): WorkerCapabilities | Promise; } + type WorkerCapabilities = Record; + /** * Basic information and helper methods about an invocation provided from the core worker to the programming model */ From 7b0761ffc96ed842b76432cd8377aa9a710186d5 Mon Sep 17 00:00:00 2001 From: Eric Jizba Date: Wed, 18 Oct 2023 11:24:35 -0700 Subject: [PATCH 3/3] fix unit tests --- test/eventHandlers/msg.ts | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/test/eventHandlers/msg.ts b/test/eventHandlers/msg.ts index 78bd2de6..ba0ce222 100644 --- a/test/eventHandlers/msg.ts +++ b/test/eventHandlers/msg.ts @@ -108,6 +108,17 @@ export namespace msg { }; } + const capabilities = { + RawHttpBodyBytes: 'true', + RpcHttpBodyOnly: 'true', + RpcHttpTriggerMetadataRemoved: 'true', + IgnoreEmptyValuedRpcHttpHeaders: 'true', + UseNullableValueDictionaryForHttp: 'true', + WorkerStatus: 'true', + TypedDataCollection: 'true', + HandlesWorkerTerminateMessage: 'true', + }; + export namespace init { export const receivedRequestLog = msg.receivedRequestLog('WorkerInitRequest'); @@ -133,16 +144,7 @@ export namespace msg { { requestId: 'testReqId', workerInitResponse: { - capabilities: { - RawHttpBodyBytes: 'true', - RpcHttpBodyOnly: 'true', - RpcHttpTriggerMetadataRemoved: 'true', - IgnoreEmptyValuedRpcHttpHeaders: 'true', - UseNullableValueDictionaryForHttp: 'true', - WorkerStatus: 'true', - TypedDataCollection: 'true', - HandlesWorkerTerminateMessage: 'true', - }, + capabilities, result: { status: rpc.StatusResult.Status.Success, }, @@ -198,6 +200,9 @@ export namespace msg { result: { status: rpc.StatusResult.Status.Success, }, + capabilities, + capabilitiesUpdateStrategy: + rpc.FunctionEnvironmentReloadResponse.CapabilitiesUpdateStrategy.replace, workerMetadata: { runtimeName: 'node', customProperties: {