コード例 #1
0
        private AvailabilityResultAsyncCollector CreateAvailabilityResultAsyncCollector(AvailabilityTestResultAttribute attribute, ValueBindingContext valueBindingContext)
        {
            // A function is defined as an Availability Test iff is has a return value marked with [AvailabilityTestResult].
            // If that is the case, this method will be invoked as some point to construct a collector for the return value.
            // Depending on the kind of the function, this will happen in different ways:
            //
            //  - For .Net functions (in-proc), this method runs BEFORE function filters:
            //     a) We will register this function as an Availability Test in the Functions registry (this is a NoOp for all,
            //        except the very first invocation).
            //     b) We will create new invocation state bag and register it with the Invocations registry.
            //     c) We will instantiate a result collector and attach it to the state bag.
            //     d) Later on, BEFORE the function body runs, the runtime execute the pre-function filter. At that point we will:
            //         ~ Initialize an Availablity Test Scope and attach it to the invocation state bag;
            //         ~ Link the results collector and the test scope.
            //     e) Subsequently, AFTER the function body runs the result will be set in one of two ways:
            //         ~ If no error:        the runtime will add the return value to the result collector -> the collector will Complete the Test Scope;
            //         ~ If error/exception: the runtime will invoke the post-function filter -> the filter will Complete the Test Scope.
            //
            //  - For non-.Net functions (out-of-proc), this method runs AFTER function filters (and, potentially, even AFTER the function body has completed):
            //     a) Registering this function as an Availability Test in the Functions registry will be a NoOp.
            //     b) We will receive an existing invocation state bag; the Availablity Test Scope will be already set in the state bag.
            //     c&d) We will instantiate a result collector and link it with the test scope right away; we will attach the collector to the state bag.
            //     e) The results will be set in a simillar manner as for .Net described above.

            Validate.NotNull(attribute, nameof(attribute));
            Validate.NotNull(valueBindingContext, nameof(valueBindingContext));

            string functionName = valueBindingContext.FunctionContext.MethodName;

            using (_log.BeginScope(LogMonikers.Scopes.CreateForTestInvocation(functionName)))
            {
                // Register this Function as an Availability Test (NoOp for all invocations of this method, except the very first one):
                _availabilityTestRegistry.Functions.Register(functionName, attribute, _log);

                // Register this particular invocation of this function:
                Guid functionInstanceId = valueBindingContext.FunctionInstanceId;
                AvailabilityTestInvocationState invocationState = _availabilityTestRegistry.Invocations.GetOrRegister(functionInstanceId, _log);

                // Create the result collector:
                var resultCollector = new AvailabilityResultAsyncCollector();

                // If the test scope is already set (out-of-proc function), then link it with the collector:
                bool isTestScopeInitialized = invocationState.TryGetTestScope(out AvailabilityTestScope testScope);
                if (isTestScopeInitialized)
                {
                    resultCollector.Initialize(testScope);
                }

                // Attache the collector to the invocation state bag:
                invocationState.AttachResultCollector(resultCollector);

                // Done:
                return(resultCollector);
            }
        }
コード例 #2
0
        public bool TryDeregister(Guid functionInstanceId, ILogger log, out AvailabilityTestInvocationState invocationState)
        {
            bool wasRegistered = _registeredInvocations.TryRemove(functionInstanceId, out invocationState);

            if (wasRegistered)
            {
                log = AvailabilityTest.Log.CreateFallbackLogIfRequired(log);

                log?.LogInformation($"A Coded Availability Test invocation instance was deregistered (completed):"
                                    + " {{ FunctionInstanceId=\"{FunctionInstanceId}\" }}",
                                    functionInstanceId);
            }

            return(wasRegistered);
        }
コード例 #3
0
        private Task <AvailabilityTestInfo> CreateAvailabilityTestInfo(AvailabilityTestInfoAttribute attribute, ValueBindingContext valueBindingContext)
        {
            // A function is an Availability Test iff is has a return value marked with [AvailabilityTestResult];
            // whereas a [AvailabilityTestInfo] is OPTIONAL to get test information at runtime.
            // User could have marked a parameter with [AvailabilityTestInfo] but no return value with [AvailabilityTestResult]:
            // That does not make sense, but we need to do something graceful.
            // There is no telling what will run first: this method, or CreateAvailabilityTelemetryAsyncCollector(..) above.
            // From here we cannot call _availabilityTestRegistry.Functions.Register(..), becasue the attribute type we get
            // here does not contain any configuration.
            // We will attach a raw test info object to this invocation.
            // If a test-RESULT-attribute is attached to this function later, it will supply configuration eventually.
            // If not, the test info will remain raw and we must remember to clear the invocation from the registry in the post-function filter.

            Validate.NotNull(attribute, nameof(attribute));
            Validate.NotNull(valueBindingContext, nameof(valueBindingContext));

            string functionName = valueBindingContext.FunctionContext.MethodName;

            using (_log.BeginScope(LogMonikers.Scopes.CreateForTestInvocation(functionName)))
            {
                // Register this particular invocation of this function:
                Guid functionInstanceId = valueBindingContext.FunctionInstanceId;
                AvailabilityTestInvocationState invocationState = _availabilityTestRegistry.Invocations.GetOrRegister(functionInstanceId, _log);

                // Create the test info:
                var testInfo = new AvailabilityTestInfo();

                // If the test scope is already set (out-of-proc function), then use it to initialize the test info:
                bool isTestScopeInitialized = invocationState.TryGetTestScope(out AvailabilityTestScope testScope);
                if (isTestScopeInitialized)
                {
                    testInfo.CopyFrom(testScope.CreateAvailabilityTestInfo());
                }

                // Attach the test info to the invocation state bag:
                invocationState.AttachTestInfo(testInfo);

                // Done:
                return(Task.FromResult(testInfo));
            }
        }
コード例 #4
0
        private AvailabilityTestInvocationState GetOrRegisterSlow(Guid functionInstanceId, ILogger log)
        {
            AvailabilityTestInvocationState newRegistration  = null;
            AvailabilityTestInvocationState usedRegistration = _registeredInvocations.GetOrAdd(
                functionInstanceId,
                (id) =>
            {
                newRegistration = new AvailabilityTestInvocationState(id);
                return(newRegistration);
            });

            if (usedRegistration == newRegistration)
            {
                log = AvailabilityTest.Log.CreateFallbackLogIfRequired(log);

                log?.LogInformation($"A new Coded Availability Test invocation instance was registered:"
                                    + " {{ FunctionInstanceId=\"{FunctionInstanceId}\" }}",
                                    functionInstanceId);
            }

            return(usedRegistration);
        }
コード例 #5
0
// Types 'FunctionExecutingContext' and 'IFunctionFilter' (and other Filter-related types) are 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 Task OnExecutingAsync(FunctionExecutingContext executingContext, CancellationToken cancelControl)
#pragma warning restore CS0618
        {
            Validate.NotNull(executingContext, nameof(executingContext));

            // Grab the invocation id and the logger:
            Guid    functionInstanceId = executingContext.FunctionInstanceId;
            ILogger log = executingContext.Logger;

            // Check if this is an Availability Test.
            // There are 3 cases:
            //  1) This IS an Availability Test and this is an in-proc/.Net functuion:
            //     This filter runs AFTER the bindings.
            //     The current function was already registered, becasue the attribute binding was already executed.
            //  2) This IS an Availability Test and this is an out-of-proc/non-.Net function:
            //     This filter runs BEFORE the bindings.
            //      a) If this is the first time the filter runs for the current function, TryGetTestConfig(..) will
            //         read the metadata file, extract the config and return True.
            //      b) If this is not the first time, the function is already registered as described in (a).
            //  3) This is NOT an Availability Test:
            //     We will get False here and do nothing.

            bool isAvailabilityTest = _availabilityTestRegistry.Functions.IsAvailabilityTest(executingContext, out string functionName, out IAvailabilityTestConfiguration testConfig);

            if (!isAvailabilityTest)
            {
                if (log != null)
                {
                    using (log.BeginScope(LogMonikers.Scopes.CreateForTestInvocation(functionName)))
                    {
                        log.LogDebug($"Availability Test Pre-Function routine was invoked and determned that this function is NOT an Availability Test:"
                                     + " {{FunctionName=\"{FunctionName}\", FunctionInstanceId=\"{FunctionInstanceId}\"}}",
                                     functionName, functionInstanceId);
                    }
                }

                return(Task.CompletedTask);
            }

            // If configured, use a fall-back logger:
            log = AvailabilityTest.Log.CreateFallbackLogIfRequired(log);

            IReadOnlyDictionary <string, object> logScopeInfo = LogMonikers.Scopes.CreateForTestInvocation(functionName);

            using (log.BeginScopeSafe(logScopeInfo))
            {
                log?.LogDebug($"Availability Test Pre-Function routine was invoked:"
                              + " {{FunctionName=\"{FunctionName}\", FunctionInstanceId=\"{FunctionInstanceId}\","
                              + " TestConfiguration={{TestDisplayNameTemplate=\"{TestDisplayNameTemplate}\" }} }}",
                              functionName, functionInstanceId, testConfig.TestDisplayName);

                // - In case (1) described above, we have already registered this invocation:
                //   The function parameters have been instantiated, and attached to the invocationState.
                //   However, the parameters are NOT yet initialized, as we did not have a AvailabilityTestScope instance yet.
                //   We will set up an AvailabilityTestScope and attach it to the invocationState.
                //   Then we will initialize the parameters using data from that scope.
                // - In case (2) described above, we have not yet registered the invocation:
                //   A new invocationState will end up being created now.
                //   We will set up an AvailabilityTestScope and attach it to the invocationState.
                //   Subsequently, when the binings eventually get invoked by the Functions tuntime,
                //   they will instantiate and initialize the parameters using data from that scope.

                // Get the invocation state bag:

                AvailabilityTestInvocationState invocationState = _availabilityTestRegistry.Invocations.GetOrRegister(functionInstanceId, log);

                // If test configuration makes reference to configuration, resolve the settings
                IAvailabilityTestInternalConfiguration resolvedTestConfig = _availabilityTestScopeSettingsResolver.Resolve(testConfig, functionName);

                // Start the availability test scope (this will start timers and set up the activity span):
                AvailabilityTestScope testScope = AvailabilityTest.StartNew(resolvedTestConfig, _telemetryConfiguration, flushOnDispose: true, log, logScopeInfo);
                invocationState.AttachTestScope(testScope);

                // If we have previously instantiated a result collector, initialize it now:
                if (invocationState.TryGetResultCollector(out AvailabilityResultAsyncCollector resultCollector))
                {
                    resultCollector.Initialize(testScope);
                }

                // If we have previously instantiated a test info, initialize it now:
                if (invocationState.TryGetTestInfos(out IEnumerable <AvailabilityTestInfo> testInfos))
                {
                    AvailabilityTestInfo model = testScope.CreateAvailabilityTestInfo();
                    foreach (AvailabilityTestInfo testInfo in testInfos)
                    {
                        testInfo.CopyFrom(model);
                    }
                }
            }

            return(Task.CompletedTask);
        }