Skip to content

Commit

Permalink
Allow core api to modify capabilities (#720)
Browse files Browse the repository at this point in the history
* Update protobuf subtree to tag v1.10.0-protofile

https://github.com/Azure/azure-functions-language-worker-protobuf/releases/tag/v1.10.0-protofile

* Allow core api to modify capabilities

And log outside of an invocation

* fix unit tests
  • Loading branch information
ejizba authored Oct 20, 2023
1 parent 4532d40 commit dd2e130
Show file tree
Hide file tree
Showing 9 changed files with 88 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand Down
14 changes: 14 additions & 0 deletions src/coreApi/coreApiLog.ts
Original file line number Diff line number Diff line change
@@ -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),
});
}
5 changes: 5 additions & 0 deletions src/eventHandlers/FunctionEnvironmentReloadHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -55,6 +57,9 @@ export class FunctionEnvironmentReloadHandler extends EventHandler<
response.workerMetadata = getWorkerMetadata();
}

response.capabilities = await getWorkerCapabilities();
response.capabilitiesUpdateStrategy = CapabilitiesUpdateStrategy.replace;

return response;
}
}
2 changes: 1 addition & 1 deletion src/eventHandlers/InvocationHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
12 changes: 2 additions & 10 deletions src/eventHandlers/WorkerInitHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down
24 changes: 24 additions & 0 deletions src/eventHandlers/getWorkerCapabilities.ts
Original file line number Diff line number Diff line change
@@ -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<WorkerCapabilities> {
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;
}
2 changes: 2 additions & 0 deletions src/setupCoreModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -25,6 +26,7 @@ export function setupCoreModule(): void {
getProgrammingModel: () => {
return worker.app.programmingModel;
},
log: coreApiLog,
registerFunction,
Disposable,
};
Expand Down
25 changes: 15 additions & 10 deletions test/eventHandlers/msg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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');

Expand All @@ -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,
},
Expand Down Expand Up @@ -198,6 +200,9 @@ export namespace msg {
result: {
status: rpc.StatusResult.Status.Success,
},
capabilities,
capabilitiesUpdateStrategy:
rpc.FunctionEnvironmentReloadResponse.CapabilitiesUpdateStrategy.replace,
workerMetadata: {
runtimeName: 'node',
customProperties: {
Expand Down
15 changes: 14 additions & 1 deletion types-core/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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<InvocationModel>;

/**
* Optional method to modify worker capabilities
*/
getCapabilities?(defaultCapabilities: WorkerCapabilities): WorkerCapabilities | Promise<WorkerCapabilities>;
}

type WorkerCapabilities = Record<string, string>;

/**
* Basic information and helper methods about an invocation provided from the core worker to the programming model
*/
Expand Down

0 comments on commit dd2e130

Please sign in to comment.