コード例 #1
0
        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);
 }
コード例 #4
0
 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);
            }
        }
コード例 #7
0
        public Task <AvailabilityTestInvocationContext> StartInvocationAsync(FunctionInvocationContext context)
        {
            var invocationContext = _availabilityTestInvocationsContextMap.GetOrAdd(context.FunctionInstanceId, n =>
            {
                return(new AvailabilityTestInvocationContext
                {
                    InvocationId = context.FunctionInstanceId
                });
            });

            return(Task.FromResult(invocationContext));
        }
コード例 #8
0
            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);
        }
コード例 #10
0
 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;
        }