Skip to content

Commit dd2e130

Browse files
authored
Allow core api to modify capabilities (#720)
* 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
1 parent 4532d40 commit dd2e130

File tree

9 files changed

+88
-22
lines changed

9 files changed

+88
-22
lines changed

azure-functions-language-worker-protobuf/src/proto/FunctionRpc.proto

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,14 @@ message FunctionEnvironmentReloadRequest {
245245
}
246246

247247
message FunctionEnvironmentReloadResponse {
248+
enum CapabilitiesUpdateStrategy {
249+
// overwrites existing values and appends new ones
250+
// ex. worker init: {A: foo, B: bar} + env reload: {A:foo, B: foo, C: foo} -> {A: foo, B: foo, C: foo}
251+
merge = 0;
252+
// existing capabilities are cleared and new capabilities are applied
253+
// ex. worker init: {A: foo, B: bar} + env reload: {A:foo, C: foo} -> {A: foo, C: foo}
254+
replace = 1;
255+
}
248256
// After specialization, worker sends capabilities & metadata.
249257
// Worker metadata captured for telemetry purposes
250258
WorkerMetadata worker_metadata = 1;
@@ -254,6 +262,9 @@ message FunctionEnvironmentReloadResponse {
254262

255263
// Status of the response
256264
StatusResult result = 3;
265+
266+
// If no strategy is defined, the host will default to merge
267+
CapabilitiesUpdateStrategy capabilities_update_strategy = 4;
257268
}
258269

259270
// Tell the out-of-proc worker to close any shared memory maps it allocated for given invocation

src/coreApi/coreApiLog.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
import * as coreTypes from '@azure/functions-core';
5+
import { worker } from '../WorkerContext';
6+
import { fromCoreLogCategory, fromCoreLogLevel } from './converters/fromCoreStatusResult';
7+
8+
export function coreApiLog(level: coreTypes.RpcLogLevel, category: coreTypes.RpcLogCategory, message: string): void {
9+
worker.log({
10+
message,
11+
level: fromCoreLogLevel(level),
12+
logCategory: fromCoreLogCategory(category),
13+
});
14+
}

src/eventHandlers/FunctionEnvironmentReloadHandler.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@ import { AzureFunctionsRpcMessages as rpc } from '../../azure-functions-language
55
import { worker } from '../WorkerContext';
66
import { startApp } from '../startApp';
77
import { EventHandler } from './EventHandler';
8+
import { getWorkerCapabilities } from './getWorkerCapabilities';
89
import { getWorkerMetadata } from './getWorkerMetadata';
910
import LogCategory = rpc.RpcLog.RpcLogCategory;
1011
import LogLevel = rpc.RpcLog.Level;
12+
import CapabilitiesUpdateStrategy = rpc.FunctionEnvironmentReloadResponse.CapabilitiesUpdateStrategy;
1113

1214
/**
1315
* Environment variables from the current process
@@ -55,6 +57,9 @@ export class FunctionEnvironmentReloadHandler extends EventHandler<
5557
response.workerMetadata = getWorkerMetadata();
5658
}
5759

60+
response.capabilities = await getWorkerCapabilities();
61+
response.capabilitiesUpdateStrategy = CapabilitiesUpdateStrategy.replace;
62+
5863
return response;
5964
}
6065
}

src/eventHandlers/InvocationHandler.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ export class InvocationHandler extends EventHandler<'invocationRequest', 'invoca
6666
);
6767

6868
const programmingModel: ProgrammingModel = nonNullProp(worker.app, 'programmingModel');
69-
const invocModel = programmingModel.getInvocationModel(coreCtx);
69+
const invocModel = await programmingModel.getInvocationModel(coreCtx);
7070

7171
const hookData: HookData = {};
7272
let { context, inputs } = await invocModel.getArguments();

src/eventHandlers/WorkerInitHandler.ts

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { isError } from '../errors';
99
import { startApp } from '../startApp';
1010
import { nonNullProp } from '../utils/nonNull';
1111
import { EventHandler } from './EventHandler';
12+
import { getWorkerCapabilities } from './getWorkerCapabilities';
1213
import { getWorkerMetadata } from './getWorkerMetadata';
1314
import LogCategory = rpc.RpcLog.RpcLogCategory;
1415
import LogLevel = rpc.RpcLog.Level;
@@ -44,16 +45,7 @@ export class WorkerInitHandler extends EventHandler<'workerInitRequest', 'worker
4445
response.workerMetadata = getWorkerMetadata();
4546
}
4647

47-
response.capabilities = {
48-
RawHttpBodyBytes: 'true',
49-
RpcHttpTriggerMetadataRemoved: 'true',
50-
RpcHttpBodyOnly: 'true',
51-
IgnoreEmptyValuedRpcHttpHeaders: 'true',
52-
UseNullableValueDictionaryForHttp: 'true',
53-
WorkerStatus: 'true',
54-
TypedDataCollection: 'true',
55-
HandlesWorkerTerminateMessage: 'true',
56-
};
48+
response.capabilities = await getWorkerCapabilities();
5749

5850
return response;
5951
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
import { WorkerCapabilities } from '@azure/functions-core';
5+
import { worker } from '../WorkerContext';
6+
7+
export async function getWorkerCapabilities(): Promise<WorkerCapabilities> {
8+
let capabilities: WorkerCapabilities = {
9+
RawHttpBodyBytes: 'true',
10+
RpcHttpTriggerMetadataRemoved: 'true',
11+
RpcHttpBodyOnly: 'true',
12+
IgnoreEmptyValuedRpcHttpHeaders: 'true',
13+
UseNullableValueDictionaryForHttp: 'true',
14+
WorkerStatus: 'true',
15+
TypedDataCollection: 'true',
16+
HandlesWorkerTerminateMessage: 'true',
17+
};
18+
19+
if (worker.app.programmingModel?.getCapabilities) {
20+
capabilities = await worker.app.programmingModel.getCapabilities(capabilities);
21+
}
22+
23+
return capabilities;
24+
}

src/setupCoreModule.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import { Disposable } from './Disposable';
55
import { worker } from './WorkerContext';
66
import { version } from './constants';
7+
import { coreApiLog } from './coreApi/coreApiLog';
78
import { registerFunction } from './coreApi/registerFunction';
89
import { setProgrammingModel } from './coreApi/setProgrammingModel';
910
import { registerHook } from './hooks/registerHook';
@@ -25,6 +26,7 @@ export function setupCoreModule(): void {
2526
getProgrammingModel: () => {
2627
return worker.app.programmingModel;
2728
},
29+
log: coreApiLog,
2830
registerFunction,
2931
Disposable,
3032
};

test/eventHandlers/msg.ts

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,17 @@ export namespace msg {
108108
};
109109
}
110110

111+
const capabilities = {
112+
RawHttpBodyBytes: 'true',
113+
RpcHttpBodyOnly: 'true',
114+
RpcHttpTriggerMetadataRemoved: 'true',
115+
IgnoreEmptyValuedRpcHttpHeaders: 'true',
116+
UseNullableValueDictionaryForHttp: 'true',
117+
WorkerStatus: 'true',
118+
TypedDataCollection: 'true',
119+
HandlesWorkerTerminateMessage: 'true',
120+
};
121+
111122
export namespace init {
112123
export const receivedRequestLog = msg.receivedRequestLog('WorkerInitRequest');
113124

@@ -133,16 +144,7 @@ export namespace msg {
133144
{
134145
requestId: 'testReqId',
135146
workerInitResponse: {
136-
capabilities: {
137-
RawHttpBodyBytes: 'true',
138-
RpcHttpBodyOnly: 'true',
139-
RpcHttpTriggerMetadataRemoved: 'true',
140-
IgnoreEmptyValuedRpcHttpHeaders: 'true',
141-
UseNullableValueDictionaryForHttp: 'true',
142-
WorkerStatus: 'true',
143-
TypedDataCollection: 'true',
144-
HandlesWorkerTerminateMessage: 'true',
145-
},
147+
capabilities,
146148
result: {
147149
status: rpc.StatusResult.Status.Success,
148150
},
@@ -198,6 +200,9 @@ export namespace msg {
198200
result: {
199201
status: rpc.StatusResult.Status.Success,
200202
},
203+
capabilities,
204+
capabilitiesUpdateStrategy:
205+
rpc.FunctionEnvironmentReloadResponse.CapabilitiesUpdateStrategy.replace,
201206
workerMetadata: {
202207
runtimeName: 'node',
203208
customProperties: {

types-core/index.d.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,12 @@ declare module '@azure/functions-core' {
185185
*/
186186
function getProgrammingModel(): ProgrammingModel;
187187

188+
/**
189+
* The recommended way to log information outside the context of an invocation
190+
* During an invocation, use `CoreInvocationContext.log` instead
191+
*/
192+
function log(level: RpcLogLevel, category: RpcLogCategory, message: string): void;
193+
188194
/**
189195
* A set of information and methods that describe the model for handling a Node.js function app
190196
* Currently, this is mainly focused on invocation
@@ -203,9 +209,16 @@ declare module '@azure/functions-core' {
203209
/**
204210
* Returns a new instance of the invocation model for each invocation
205211
*/
206-
getInvocationModel(coreContext: CoreInvocationContext): InvocationModel;
212+
getInvocationModel(coreContext: CoreInvocationContext): InvocationModel | Promise<InvocationModel>;
213+
214+
/**
215+
* Optional method to modify worker capabilities
216+
*/
217+
getCapabilities?(defaultCapabilities: WorkerCapabilities): WorkerCapabilities | Promise<WorkerCapabilities>;
207218
}
208219

220+
type WorkerCapabilities = Record<string, string>;
221+
209222
/**
210223
* Basic information and helper methods about an invocation provided from the core worker to the programming model
211224
*/

0 commit comments

Comments
 (0)