public bool IsAvailabilityTest(FunctionInvocationContext context) { if (IsAvailabilityTest(context.FunctionName)) { return(true); } // For .NET languages, binding happens BEFORE filters, so if the function is an availability test // we'll have known that already above // The following check handles OOP languages, where bindings happen late and dynamically, AFTER filters. // In these cases, we must read the function metadata, since NO binding has yet occurred if (context.Arguments.TryGetValue("_context", out object value)) { var executionContext = value as ExecutionContext; if (executionContext != null) { var metadataPath = Path.Combine(executionContext.FunctionAppDirectory, "function.json"); bool isAvailabilityTest = HasAvailabilityTestBinding(metadataPath); _availabilityTests[context.FunctionName] = isAvailabilityTest; return(isAvailabilityTest); } } return(false); }
protected override async Task <object> InvokeCore(object[] parameters, FunctionInvocationContext context) { FunctionInstanceLogEntry item = new FunctionInstanceLogEntry { FunctionInstanceId = context.ExecutionContext.InvocationId, StartTime = DateTime.UtcNow, FunctionName = Metadata.Name, Properties = new Dictionary <string, object>() }; await _fastLogger.AddAsync(item); InvocationData invocation = parameters.OfType <InvocationData>().FirstOrDefault() ?? new InvocationData(); string error = "failed"; try { if (invocation.Throw) { throw new InvalidOperationException("Kaboom!"); } await Task.Delay(invocation.Delay); error = null; // success return(null); } finally { item.EndTime = DateTime.UtcNow; item.ErrorDetails = error; await _fastLogger.AddAsync(item); } }
protected override async Task InvokeCore(object[] parameters, FunctionInvocationContext context) { if (Throw) { throw new InvalidOperationException("Kaboom!"); } await Task.Delay(500); }
public Task CompleteInvocationAsync(FunctionInvocationContext context) { if (_availabilityTestInvocationsContextMap.TryRemove(context.FunctionInstanceId, out AvailabilityTestInvocationContext invocationContext)) { // TODO: complete the invocation } return(Task.CompletedTask); }
protected override async Task InvokeCore(object[] parameters, FunctionInvocationContext context) { InvocationData invocation = parameters.OfType <InvocationData>().FirstOrDefault() ?? new InvocationData(); if (invocation.Throw) { throw new InvalidOperationException("Kaboom!"); } await Task.Delay(invocation.Delay); }
// Type 'FunctionInvocationContext' (and other Filter-related types) is marked as preview/obsolete, // but the guidance from the Azure Functions team is to use it, so we disable the warning. #pragma warning disable CS0618 public bool IsAvailabilityTest(FunctionInvocationContext functionInvocationContext, out string functionName, out IAvailabilityTestConfiguration testConfig) #pragma warning restore CS0618 { Validate.NotNull(functionInvocationContext, nameof(functionInvocationContext)); functionName = functionInvocationContext.FunctionName; Validate.NotNullOrWhitespace(functionName, "functionInvocationContext.FunctionName"); // In most cases we have already registered the Function: // either by callign this method from the filter during an earlier execution (out-of-proc languages) // or by calling Register(..) from the binding (.Net (in-proc) functions). if (_registeredAvailabilityTests.TryGetValue(functionName, out AvailabilityTestRegistration registration)) { testConfig = registration.Config; return(registration.IsAvailabilityTest); } ILogger log = functionInvocationContext.Logger; log = AvailabilityTest.Log.CreateFallbackLogIfRequired(log); // Getting here means that we are executing out-of-proc language function for the first time. // In such cases, bindings happen late and dynamically, AFTER filters. Thus, NO binding has yet occurred. // We will read the function metadata to see if the return value of the function is tagged with the right attribute. try { // Attempt to parse the function metadata file. This will throw if something goes wrong. // We will catch immediately, but this is rare if it happens at all) and helps attaching debuggers. GetTestConfigFromMetadata(functionName, functionInvocationContext, log, out bool isAvailabilityTest, out testConfig); // We got here becasue the function was not registered, so take the insertion path right away: GetOrRegisterSlow(functionName, testConfig, isAvailabilityTest, log, "based on the function metadata file"); return(isAvailabilityTest); } catch (Exception ex) { log.LogError(ex, $"Error while processing function metadata file to determine whether this function is a Coded Availability Test:" + " FunctionName=\"{FunctionName}\", {{ErrorType=\"{ErrorType}\", {{ErrorMessage=\"{ErrorMessage}\"}}", functionName, ex.GetType().Name, ex.Message); // We could not conclusively determine the aswer from metadata. // We assume "NOT an Availability Test", but we do not cache this, so we will keep checking in case this was some transient IO error. // We are not worried about the resulting perf impace, bacause this should not happen anyway. testConfig = null; return(false); } }
public Task <AvailabilityTestInvocationContext> StartInvocationAsync(FunctionInvocationContext context) { var invocationContext = _availabilityTestInvocationsContextMap.GetOrAdd(context.FunctionInstanceId, n => { return(new AvailabilityTestInvocationContext { InvocationId = context.FunctionInstanceId }); }); return(Task.FromResult(invocationContext)); }
static void Append(FunctionInvocationContext context, string text) { var props = context.Properties; object obj; if (!props.TryGetValue(Key, out obj)) { obj = _sb; props[Key] = obj; } var sb = (StringBuilder)obj; sb.Append(text); }
// Type 'FunctionInvocationContext' (and other Filter-related types) is marked as preview/obsolete, // but the guidance from the Azure Functions team is to use it, so we disable the warning. #pragma warning disable CS0618 private static string ReadFunctionMetadataFile(FunctionInvocationContext functionInvocationContext) #pragma warning restore CS0618 { // We will do very verbose error checking and logging via exceptions here to aid supportability // in case out assumptions about Function Runtime behaviur get violated. // For out-of-proc languages, the _context parameter should contain info about the runtime environment. // It should be of type ExecutionContext. // ExecutionContext should have info about the location of the function metadata file. Validate.NotNull(functionInvocationContext.Arguments, "functionInvocationContext.Arguments"); const string NeedContextArgumentErrorPrefix = "For non-.Net (out-of-proc) functions, the Arguments table of the specified" + " FunctionInvocationContext is expected to have a an entry with the key \"_context\"" + " and a value of type \"ExecutionContext\"."; if (!functionInvocationContext.Arguments.TryGetValue("_context", out object execContextObj)) { throw new InvalidOperationException(NeedContextArgumentErrorPrefix + " However, such entry does not exist."); } if (execContextObj == null) { throw new InvalidOperationException(NeedContextArgumentErrorPrefix + " Such entry exists, but the value is null."); } string metadataFilePath; if (execContextObj is ExecutionContext execContext) { metadataFilePath = GetFullFunctionMetadataPath(execContext); } else { throw new InvalidOperationException(NeedContextArgumentErrorPrefix + $" Such entry exists, but is has the wrong type (\"{execContextObj.GetType().Name}\")."); } string metadataFileContent = File.ReadAllText(metadataFilePath); return(metadataFileContent); }
public new Task <object> InvokeCore(object[] parameters, FunctionInvocationContext context) { return(base.InvokeCore(parameters, context)); }
// Type 'FunctionInvocationContext' (and other Filter-related types) is marked as preview/obsolete, // but the guidance from the Azure Functions team is to use it, so we disable the warning. #pragma warning disable CS0618 private static void GetTestConfigFromMetadata(string functionName, FunctionInvocationContext functionInvocationContext, ILogger log, out bool isAvailabilityTest, out IAvailabilityTestConfiguration testConfig) #pragma warning restore CS0618 { // We will do very verbose error checking and logging via exception here to aid supportability // in case out assumptions about Function Runtime behaviur get violated. const string BeginAnalysisLogMessage = "Analysis of function metadata file to determine whether this function" + " is a Coded Availability Test beginning:" + " {{FunctionName=\"{FunctionName}\"}}"; const string FinishAnalysisLogMessage = "Analysis of function metadata file to determine whether this function" + " is a Coded Availability Test finished:" + " {{FunctionName=\"{FunctionName}\", IsAvailabilityTest=\"{IsAvailabilityTest}\"}}"; log?.LogDebug(BeginAnalysisLogMessage, functionName); string metadataFileContent = ReadFunctionMetadataFile(functionInvocationContext); FunctionMetadata functionMetadata = JsonConvert.DeserializeObject <FunctionMetadata>(metadataFileContent); if (functionMetadata == null) { throw new InvalidOperationException($"Could not parse the function metadata for function \"{functionName}\"."); } if (functionMetadata.Bindings == null) { throw new InvalidOperationException($"The function metadata for function \"{functionName}\" was parsed," + " but it did not contain a list of bindings."); } if (functionMetadata.Bindings.Count == 0) { throw new InvalidOperationException($"The function metadata for function \"{functionName}\" was parsed;" + " it contained a list of bindings, but the list had no entries."); } foreach (BindingMetadata bindingMetadata in functionMetadata.Bindings) { if (bindingMetadata == null || bindingMetadata.Type == null) { continue; } if (bindingMetadata.Type.Equals(AvailabilityTestResultAttribute.BindingTypeName, StringComparison.OrdinalIgnoreCase) || bindingMetadata.Type.Equals(nameof(AvailabilityTestResultAttribute), StringComparison.OrdinalIgnoreCase)) { isAvailabilityTest = true; testConfig = bindingMetadata; log?.LogDebug(FinishAnalysisLogMessage, functionName, isAvailabilityTest); return; } } isAvailabilityTest = false; testConfig = null; log?.LogDebug(FinishAnalysisLogMessage, functionName, isAvailabilityTest); return; }