internal async Task <bool> ConfigureAsync(InstanceName instance, string name, bool?disable = null, bool?impersonate = null, bool?bypassrules = null, CancellationToken cancellationToken = default)
        {
            var webFunctionApp = await GetWebApp(instance, cancellationToken);

            var configuration = await AggregatorConfiguration.ReadConfiguration(webFunctionApp);

            var ruleConfig = configuration.GetRuleConfiguration(name);

            if (disable.HasValue)
            {
                ruleConfig.IsDisabled = disable.Value;
            }

            if (impersonate.HasValue)
            {
                ruleConfig.Impersonate = impersonate.Value;
            }

            if (bypassrules.HasValue)
            {
                ruleConfig.BypassRules = impersonate.Value;
            }

            ruleConfig.WriteConfiguration(webFunctionApp);

            return(true);
        }
        internal async Task <bool> ChangeAppSettingsAsync(InstanceName instance, DevOpsLogon devopsLogonData, SaveMode saveMode, CancellationToken cancellationToken)
        {
            var webFunctionApp = await GetWebApp(instance, cancellationToken);

            var configuration = await AggregatorConfiguration.ReadConfiguration(webFunctionApp);

            configuration.DevOpsTokenType = devopsLogonData.Mode;
            configuration.DevOpsToken     = devopsLogonData.Token;
            configuration.SaveMode        = saveMode;

            configuration.WriteConfiguration(webFunctionApp);
            return(true);
        }
Beispiel #3
0
        internal async Task <bool> ChangeAppSettings(InstanceName instance, VstsLogon vstsLogonData)
        {
            var webFunctionApp = await azure
                                 .AppServices
                                 .WebApps
                                 .GetByResourceGroupAsync(
                instance.ResourceGroupName,
                instance.FunctionAppName);

            var configuration = new AggregatorConfiguration();

            configuration.VstsTokenType = vstsLogonData.Mode;
            configuration.VstsToken     = vstsLogonData.Token;
            configuration.Write(webFunctionApp);
            return(true);
        }
Beispiel #4
0
        internal async Task <bool> ChangeAppSettingsAsync(InstanceName instance, DevOpsLogon devopsLogonData, SaveMode saveMode, CancellationToken cancellationToken)
        {
            var webFunctionApp = await azure
                                 .AppServices
                                 .WebApps
                                 .GetByResourceGroupAsync(
                instance.ResourceGroupName,
                instance.FunctionAppName, cancellationToken);

            var configuration = new AggregatorConfiguration
            {
                DevOpsTokenType = devopsLogonData.Mode,
                DevOpsToken     = devopsLogonData.Token,
                SaveMode        = saveMode
            };

            configuration.Write(webFunctionApp);
            return(true);
        }
        internal async Task <IEnumerable <RuleOutputData> > ListAsync(InstanceName instance, CancellationToken cancellationToken)
        {
            var kudu = GetKudu(instance);

            _logger.WriteInfo($"Retrieving Functions in {instance.PlainName}...");

            var webFunctionApp = await GetWebApp(instance, cancellationToken);

            cancellationToken.ThrowIfCancellationRequested();
            var configuration = await AggregatorConfiguration.ReadConfiguration(webFunctionApp);

            using (var client = new HttpClient())
            {
                KuduFunction[] kuduFunctions;
                using (var request = await kudu.GetRequestAsync(HttpMethod.Get, $"api/functions", cancellationToken))
                    using (var response = await client.SendAsync(request, cancellationToken))
                    {
                        var stream = await response.Content.ReadAsStreamAsync();

                        if (!response.IsSuccessStatusCode)
                        {
                            _logger.WriteError($"{response.ReasonPhrase} {await response.Content.ReadAsStringAsync()}");
                            return(Enumerable.Empty <RuleOutputData>());
                        }

                        using (var sr = new StreamReader(stream))
                            using (var jtr = new JsonTextReader(sr))
                            {
                                var js = new JsonSerializer();
                                kuduFunctions = js.Deserialize <KuduFunction[]>(jtr);
                            }
                    }

                List <RuleOutputData> ruleData = new List <RuleOutputData>();
                foreach (var kuduFunction in kuduFunctions)
                {
                    var ruleName    = kuduFunction.Name;
                    var ruleFileUrl = $"api/vfs/site/wwwroot/{ruleName}/{ruleName}.rule";
                    _logger.WriteInfo($"Retrieving Function Rule Details {ruleName}...");

                    using (var request = await kudu.GetRequestAsync(HttpMethod.Get, ruleFileUrl, cancellationToken))
                        using (var response = await client.SendAsync(request, cancellationToken))
                        {
                            var stream = await response.Content.ReadAsStreamAsync();

                            if (!response.IsSuccessStatusCode)
                            {
                                _logger.WriteError($"{response.ReasonPhrase} {await response.Content.ReadAsStringAsync()}");
                                continue;
                            }


                            var ruleCode = new List <string>();
                            using (var sr = new StreamReader(stream))
                            {
                                string line;
                                while ((line = sr.ReadLine()) != null)
                                {
                                    ruleCode.Add(line);
                                }
                            }

                            var ruleConfiguration = configuration.GetRuleConfiguration(ruleName);
                            var(preprocessedRule, _) = RuleFileParser.Read(ruleCode.ToArray());
                            ruleData.Add(new RuleOutputData(instance, ruleConfiguration, preprocessedRule.LanguageAsString()));
                        }
                }

                return(ruleData);
            }
        }
        public static StatsdnetConfiguration Parse(string configFile)
        {
            var config    = new StatsdnetConfiguration();
            var xml       = XDocument.Parse(File.ReadAllText(configFile));
            var statsdnet = xml.Element("statsdnet");

            config.Name = statsdnet.Attribute("name").Value;
            if (statsdnet.Attributes().Any(p => p.Name == "hideSystemStats"))
            {
                config.HideSystemStats = statsdnet.ToBoolean("hideSystemStats");
            }

            // Add listeners
            foreach (var item in statsdnet.Element("listeners").Elements())
            {
                ListenerConfiguration listener = null;
                switch (item.Name.LocalName)
                {
                case "udp":
                    listener = new UDPListenerConfiguration(item.ToInt("port"));
                    break;

                case "tcp":
                    listener = new TCPListenerConfiguration(item.ToInt("port"));
                    break;

                case "http":
                    listener = new HTTPListenerConfiguration(item.ToInt("port"));
                    if (item.Attribute("headerKey") != null)
                    {
                        ((HTTPListenerConfiguration)listener).HeaderKey = item.Attribute("headerKey").Value;
                    }
                    break;

                case "statsdnet":
                    listener = new StatsdnetListenerConfiguration(item.ToInt("port"));
                    break;

                case "mssql-relay":
                    listener = new MSSQLRelayListenerConfiguration(item.Attribute("connectionString").Value,
                                                                   item.ToInt("batchSize"),
                                                                   item.ToBoolean("deleteAfterSend"),
                                                                   item.ToTimeSpan("pollInterval"));
                    break;

                default:
                    throw new ArgumentOutOfRangeException("Not sure what this listener is: " + item.Name);
                }
                config.Listeners.Add(listener);
            }

            // Add Backends
            foreach (var item in statsdnet.Element("backends").Elements())
            {
                string name = item.Name.LocalName;
                config.BackendConfigurations[name] = item;
            }

            // Add aggregators
            var flushInterval = statsdnet.Element("aggregation").ToTimeSpan("flushInterval");

            config.FlushInterval = flushInterval;
            var aggregatorGroup = new AggregatorConfiguration();

            foreach (var item in statsdnet.Element("aggregation").Elements())
            {
                switch (item.Name.LocalName)
                {
                case "gauges":
                    config.Aggregators.Add("gauges", new GaugeAggregatorConfig(ns: item.Attribute("namespace").Value,
                                                                               removeZeroGauges: item.ToBoolean("removeZeroGauges")));
                    break;

                case "counters":
                    config.Aggregators.Add("counters", new CounterAggregationConfig(ns: item.Attribute("namespace").Value));
                    break;

                case "sets":
                    config.Aggregators.Add("sets", new SetAggregationConfig(ns: item.Attribute("namespace").Value));
                    break;

                case "calendargrams":
                    config.Aggregators.Add("calendargrams", new CalendargramAggregationConfig(ns: item.Attribute("namespace").Value));
                    break;

                case "timers":
                    var timerConfig = new TimersAggregationConfig(ns: item.Attribute("namespace").Value, calculateSumSquares: item.ToBoolean("calculateSumSquares"));
                    config.Aggregators.Add("timers", timerConfig);
                    // Now add the percentiles
                    foreach (var subItem in item.Elements())
                    {
                        if (!timerConfig.AddPercentile(new PercentileConfig(
                                                           name: subItem.Attribute("name").Value,
                                                           threshold: subItem.ToInt("threshold"),
                                                           flushInterval: subItem.ToTimeSpan("flushInterval")
                                                           )))
                        {
                            // TODO: log that a duplicate percentile was ignored
                        }
                    }
                    break;
                }
            }
            return(config);
        }
        public static StatsdnetConfiguration Parse(string configFile)
        {
            var config = new StatsdnetConfiguration();
            var xml = XDocument.Parse(File.ReadAllText(configFile));
            var statsdnet = xml.Element("statsdnet");
            config.Name = statsdnet.Attribute("name").Value;
            if (statsdnet.Attributes().Any(p => p.Name == "hideSystemStats"))
            {
                config.HideSystemStats = statsdnet.ToBoolean("hideSystemStats");
            }

            // Add listeners
            foreach (var item in statsdnet.Element("listeners").Elements())
            {
                ListenerConfiguration listener = null;
                switch (item.Name.LocalName)
                {
                    case "udp":
                        listener = new UDPListenerConfiguration(item.ToInt("port"));
                        break;
                    case "tcp":
                        listener = new TCPListenerConfiguration(item.ToInt("port"));
                        break;
                    case "http":
                        listener = new HTTPListenerConfiguration(item.ToInt("port"));
                        if (item.Attribute("headerKey") != null)
                        {
                            ((HTTPListenerConfiguration)listener).HeaderKey = item.Attribute("headerKey").Value;
                        }
                        break;
                    case "statsdnet":
                        listener = new StatsdnetListenerConfiguration(item.ToInt("port"));
                        break;
                    case "mssql-relay":
                        listener = new MSSQLRelayListenerConfiguration(item.Attribute("connectionString").Value,
                            item.ToInt("batchSize"),
                            item.ToBoolean("deleteAfterSend"),
                            item.ToTimeSpan("pollInterval"));
                        break;
                    default:
                        throw new ArgumentOutOfRangeException("Not sure what this listener is: " + item.Name);
                }
                config.Listeners.Add(listener);
            }

            // Add Backends
            foreach (var item in statsdnet.Element("backends").Elements())
            {
                string name = item.Name.LocalName;
                config.BackendConfigurations[name] = item;
            }

            // Add aggregators
            var flushInterval = statsdnet.Element("aggregation").ToTimeSpan("flushInterval");
            config.FlushInterval = flushInterval;
            var aggregatorGroup = new AggregatorConfiguration();
            foreach (var item in statsdnet.Element("aggregation").Elements())
            {
                switch (item.Name.LocalName)
                {
                    case "gauges":
                        config.Aggregators.Add("gauges", new GaugeAggregatorConfig(ns: item.Attribute("namespace").Value,
                            removeZeroGauges: item.ToBoolean("removeZeroGauges")));
                        break;
                    case "counters":
                        config.Aggregators.Add("counters", new CounterAggregationConfig(ns: item.Attribute("namespace").Value));
                        break;
                    case "sets":
                        config.Aggregators.Add("sets", new SetAggregationConfig(ns: item.Attribute("namespace").Value));
                        break;
                    case "calendargrams":
                        config.Aggregators.Add("calendargrams", new CalendargramAggregationConfig(ns: item.Attribute("namespace").Value));
                        break;
                    case "timers":
                        var timerConfig = new TimersAggregationConfig(ns: item.Attribute("namespace").Value, calculateSumSquares: item.ToBoolean("calculateSumSquares"));
                        config.Aggregators.Add("timers", timerConfig);
                        // Now add the percentiles
                        foreach (var subItem in item.Elements())
                        {
                            if (!timerConfig.AddPercentile(new PercentileConfig(
                              name: subItem.Attribute("name").Value,
                              threshold: subItem.ToInt("threshold"),
                              flushInterval: subItem.ToTimeSpan("flushInterval")
                              )))
                            {
                                // TODO: log that a duplicate percentile was ignored
                            }
                        }
                        break;
                }
            }
            return config;
        }
Beispiel #8
0
        public async Task <IActionResult> PostAsync(WebHookEvent eventData, string ruleName, CancellationToken cancellationToken)
        {
            var aggregatorVersion = RequestHelper.AggregatorVersion;

            _log.LogInformation($"Aggregator v{aggregatorVersion} executing rule '{ruleName}'");

            var webHookStartEvent = new EventTelemetry()
            {
                Name = "WebHookEvent Start"
            };

            webHookStartEvent.Properties["rule"]            = ruleName;
            webHookStartEvent.Properties["eventType"]       = eventData?.EventType;
            webHookStartEvent.Properties["resourceVersion"] = eventData?.ResourceVersion;
            Telemetry.TrackEvent(webHookStartEvent);

            if (eventData == null)
            {
                return(BadRequest("Request body is empty"));
            }

            // sanity check
            if (!DevOpsEvents.IsValidEvent(eventData.EventType) ||
                eventData.PublisherId != DevOpsEvents.PublisherId)
            {
                _log.LogDebug("return BadRequest");
                return(BadRequest(new { Error = "Not a good Azure DevOps post..." }));
            }

            var helper       = new RequestHelper(_log);
            var eventContext = helper.CreateContextFromEvent(eventData);

            if (eventContext.IsTestEvent())
            {
                Response.AddCustomHeaders(aggregatorVersion, ruleName);
                return(RespondToTestEventMessage(aggregatorVersion, ruleName));
            }

            var configContext = _configuration;
            var configuration = AggregatorConfiguration.ReadConfiguration(configContext)
                                .UpdateFromUrl(ruleName, GetRequestUri());

            var logger       = new ForwarderLogger(_log);
            var ruleProvider = new AspNetRuleProvider(logger, _configuration);
            var ruleExecutor = new RuleExecutor(logger, configuration);

            using (_log.BeginScope($"WorkItem #{eventContext.WorkItemPayload.WorkItem.Id}"))
            {
                try
                {
                    var rule = await ruleProvider.GetRule(ruleName);

                    var ruleStartEvent = new EventTelemetry()
                    {
                        Name = "Rule Exec Start"
                    };
                    ruleStartEvent.Properties["rule"] = rule.Name;
                    ruleStartEvent.Properties["ImpersonateExecution"] = rule.ImpersonateExecution.ToString();
                    ruleStartEvent.Properties["EnableRevisionCheck"]  = rule.Settings.EnableRevisionCheck.ToString();
                    ruleStartEvent.Properties["eventType"]            = eventContext.EventType;
                    Telemetry.TrackEvent(ruleStartEvent);
                    var ruleExecTimer = new Stopwatch();
                    ruleExecTimer.Start();

                    var execResult = await ruleExecutor.ExecuteAsync(rule, eventContext, cancellationToken);

                    ruleExecTimer.Stop();
                    var ruleEndEvent = new EventTelemetry()
                    {
                        Name = "Rule Exec End"
                    };
                    ruleEndEvent.Properties["rule"]  = ruleName;
                    ruleEndEvent.Metrics["duration"] = ruleExecTimer.ElapsedMilliseconds;
                    Telemetry.TrackEvent(ruleEndEvent);

                    if (string.IsNullOrEmpty(execResult))
                    {
                        return(Ok());
                    }
                    else
                    {
                        _log.LogInformation($"Returning '{execResult}' from '{rule.Name}'");
                        return(Ok(execResult));
                    }
                }
                catch (Exception ex)
                {
                    _log.LogWarning($"Rule '{ruleName}' failed: {ex.Message}");
                    Telemetry.TrackException(ex);
                    return(BadRequest(ex.Message));
                }
            }
        }