/// <summary>
        /// Updates a successful run in the tracking table.
        /// </summary>
        /// <param name="signalExecutionInfo">The current signal execution information</param>
        /// <returns>A <see cref="Task"/> running the asynchronous operation</returns>
        public async Task UpdateSignalRunAsync(SignalExecutionInfo signalExecutionInfo)
        {
            // Execute the update operation
            this.tracer.TraceVerbose($"updating run for: {signalExecutionInfo.AlertRule.Id}");
            var operation = TableOperation.InsertOrReplace(new TrackSignalRunEntity
            {
                PartitionKey = PartitionKey,
                RowKey       = signalExecutionInfo.AlertRule.Id,
                SignalId     = signalExecutionInfo.AlertRule.SignalId,
                LastSuccessfulExecutionTime = signalExecutionInfo.CurrentExecutionTime
            });

            await this.trackingTable.ExecuteAsync(operation, CancellationToken.None);
        }
        public async Task WhenSignalExecutionThrowsExceptionThenNextSignalIsProcessed()
        {
            // Create signal execution information to be returned from the job tracker
            var signalExecution1 = new SignalExecutionInfo
            {
                AlertRule = new AlertRule
                {
                    SignalId   = "s1",
                    Id         = "r1",
                    ResourceId = "1",
                },
                LastExecutionTime = DateTime.UtcNow.AddHours(-1)
            };
            var signalExecution2 = new SignalExecutionInfo
            {
                AlertRule = new AlertRule
                {
                    SignalId   = "s2",
                    Id         = "r2",
                    ResourceId = "2",
                },
                LastExecutionTime = DateTime.UtcNow.AddHours(-1)
            };
            var signalExecutions = new List <SignalExecutionInfo> {
                signalExecution1, signalExecution2
            };

            this.signalRunTrackerMock.Setup(m => m.GetSignalsToRunAsync(It.IsAny <IList <AlertRule> >())).ReturnsAsync(signalExecutions);

            // first signal execution throws exception and the second one returns a result
            const string ResultItemTitle = "someTitle";

            this.analysisExecuterMock.SetupSequence(m => m.ExecuteSignalAsync(It.IsAny <SignalExecutionInfo>(), It.Is <IList <string> >(lst => lst.First() == "1" || lst.First() == "2")))
            .Throws(new Exception())
            .ReturnsAsync(new List <SmartSignalResultItemPresentation> {
                new TestResultItem(ResultItemTitle)
            });

            await this.scheduleFlow.RunAsync();

            this.alertRuleStoreMock.Verify(m => m.GetAllAlertRulesAsync(), Times.Once);

            // Verify that these were called only once since the first signal execution throwed exception
            this.publisherMock.Verify(m => m.PublishSignalResultItemsAsync("s2", It.Is <IList <SmartSignalResultItemPresentation> >(items => items.Count == 1 && items.First().Title == ResultItemTitle)), Times.Once);
            this.emailSenderMock.Verify(m => m.SendSignalResultEmailAsync(It.IsAny <SignalExecutionInfo>(), It.Is <IList <SmartSignalResultItemPresentation> >(items => items.Count == 1 && items.First().Title == ResultItemTitle)), Times.Once);
            this.signalRunTrackerMock.Verify(m => m.UpdateSignalRunAsync(It.IsAny <SignalExecutionInfo>()), Times.Once());
            this.signalRunTrackerMock.Verify(m => m.UpdateSignalRunAsync(signalExecution2));
        }
        public async Task WhenThereAreSignalsToRunThenAllSiganlResultItemsArePublished()
        {
            // Create signal execution information to be returned from the job tracker
            var signalExecution1 = new SignalExecutionInfo
            {
                AlertRule = new AlertRule
                {
                    Id         = "r1",
                    SignalId   = "s1",
                    ResourceId = "1",
                },
                LastExecutionTime = DateTime.UtcNow.AddHours(-1)
            };
            var signalExecution2 = new SignalExecutionInfo
            {
                AlertRule = new AlertRule
                {
                    Id         = "r2",
                    SignalId   = "s2",
                    ResourceId = "2",
                },
                LastExecutionTime = DateTime.UtcNow.AddHours(-1)
            };
            var signalExecutions = new List <SignalExecutionInfo> {
                signalExecution1, signalExecution2
            };

            this.signalRunTrackerMock.Setup(m => m.GetSignalsToRunAsync(It.IsAny <IList <AlertRule> >())).ReturnsAsync(signalExecutions);

            // each signal execution returns a result
            this.analysisExecuterMock.Setup(m => m.ExecuteSignalAsync(It.IsAny <SignalExecutionInfo>(), It.Is <IList <string> >(lst => lst.First() == "1" || lst.First() == "2")))
            .ReturnsAsync(new List <SmartSignalResultItemPresentation> {
                new TestResultItem("title")
            });

            await this.scheduleFlow.RunAsync();

            // Verify result items were published and signal tracker was updated for each signal execution
            this.alertRuleStoreMock.Verify(m => m.GetAllAlertRulesAsync(), Times.Once);
            this.publisherMock.Verify(m => m.PublishSignalResultItemsAsync(It.IsAny <string>(), It.IsAny <IList <SmartSignalResultItemPresentation> >()), Times.Exactly(2));
            this.emailSenderMock.Verify(m => m.SendSignalResultEmailAsync(It.IsAny <SignalExecutionInfo>(), It.IsAny <IList <SmartSignalResultItemPresentation> >()), Times.Exactly(2));
            this.signalRunTrackerMock.Verify(m => m.UpdateSignalRunAsync(It.IsAny <SignalExecutionInfo>()), Times.Exactly(2));
        }
        public void Setup()
        {
            var tracerMock = new Mock <ITracer>();

            this.sendgridClientMock  = new Mock <ISendGridClient>();
            this.emailSender         = new EmailSender(tracerMock.Object, this.sendgridClientMock.Object);
            this.signalExecutionInfo = new SignalExecutionInfo()
            {
                AlertRule = new AlertRule
                {
                    SignalId        = "s1",
                    Id              = "r1",
                    ResourceId      = "/subscriptions/1",
                    EmailRecipients = new List <string>()
                    {
                        "*****@*****.**"
                    }
                },
                CurrentExecutionTime = DateTime.UtcNow,
                LastExecutionTime    = DateTime.UtcNow.AddHours(-1)
            };
        }
Exemplo n.º 5
0
        public async Task WhenUpdatingSignalRunThenUpdateIsCalledCorrectly()
        {
            var signalExecution = new SignalExecutionInfo
            {
                AlertRule = new AlertRule
                {
                    Id       = "some_rule",
                    SignalId = "some_signal",
                },
                LastExecutionTime    = DateTime.UtcNow.AddHours(-1),
                CurrentExecutionTime = DateTime.UtcNow.AddMinutes(-1)
            };

            await this.signalRunsTracker.UpdateSignalRunAsync(signalExecution);

            this.tableMock.Verify(m => m.ExecuteAsync(
                                      It.Is <TableOperation>(operation =>
                                                             operation.OperationType == TableOperationType.InsertOrReplace &&
                                                             operation.Entity.RowKey.Equals(signalExecution.AlertRule.Id) &&
                                                             ((TrackSignalRunEntity)operation.Entity).SignalId.Equals(signalExecution.AlertRule.SignalId) &&
                                                             ((TrackSignalRunEntity)operation.Entity).LastSuccessfulExecutionTime.Equals(signalExecution.CurrentExecutionTime)),
                                      It.IsAny <CancellationToken>()));
        }
Exemplo n.º 6
0
        /// <summary>
        /// Sends the Smart Signal result Email.
        /// </summary>
        /// <param name="signalExecution">The signals execution information.</param>
        /// <param name="smartSignalResultItems">The Smart Signal result items.</param>
        /// <returns>The task object representing the asynchronous operation.</returns>
        public async Task SendSignalResultEmailAsync(SignalExecutionInfo signalExecution, IList <SmartSignalResultItemPresentation> smartSignalResultItems)
        {
            AlertRule alertRule = signalExecution.AlertRule;

            if (this.sendGridClient == null)
            {
                this.tracer.TraceWarning("SendGrid API key was not found, not sending email");
                return;
            }

            if (alertRule.EmailRecipients == null || !alertRule.EmailRecipients.Any())
            {
                this.tracer.TraceWarning("Email recipients were not provided, not sending email");
                return;
            }

            if (smartSignalResultItems == null || !smartSignalResultItems.Any())
            {
                this.tracer.TraceInformation($"no result items to publish for signal {alertRule.SignalId}");
                return;
            }

            this.tracer.TraceInformation($"Sending signal result email for signal {alertRule.SignalId}");

            var exceptions = new List <Exception>();

            foreach (SmartSignalResultItemPresentation resultItem in smartSignalResultItems)
            {
                ResourceIdentifier resource = ResourceIdentifier.CreateFromResourceId(alertRule.ResourceId);

                // TODO: Fix links
                string emailBody = Resources.SmartSignalEmailTemplate
                                   .Replace(SignalNamePlaceHolder, resultItem.SignalName)
                                   .Replace(ResourceNamePlaceHolder, resultItem.ResourceId)
                                   .Replace(LinkToPortalPlaceHolder, "LinkToPortal")
                                   .Replace(RuleNamePlaceHolder, alertRule.Name)
                                   .Replace(RuleDescriptionPlaceHolder, alertRule.Description)
                                   .Replace(ServiceNamePlaceHolder, $@"{resource.ResourceType}: {resource.ResourceName} ({resource.ResourceGroupName})")
                                   .Replace(AlertActivatedTimePlaceHolder, signalExecution.LastExecutionTime.ToString())
                                   .Replace(SubscriptionNamePlaceHolder, resource.SubscriptionId)
                                   .Replace(LinkToFeedbackPlaceHolder, "https://ms.portal.azure.com/");

                var msg = new SendGridMessage
                {
                    From             = new EmailAddress("*****@*****.**", "Smart Signals"),
                    Subject          = $"Azure Smart Alerts (preview) - {resultItem.SignalName} detected",
                    PlainTextContent = $@"{resultItem.SignalName} was detected for {resultItem.ResourceId}. 
                                        You can view more details for this alert here: {"LinkToPortal"}",
                    HtmlContent      = emailBody
                };

                var emailAddresses = alertRule.EmailRecipients.Select(email => new EmailAddress(email)).ToList();
                msg.AddTos(emailAddresses);

                try
                {
                    var response = await this.sendGridClient.SendEmailAsync(msg);

                    if (!IsSuccessStatusCode(response.StatusCode))
                    {
                        string content = response.Body != null ? await response.Body.ReadAsStringAsync() : string.Empty;

                        var message = $"Failed to send signal results Email for signal {alertRule.SignalId}. Fail StatusCode: {response.StatusCode}. Content: {content}.";
                        this.tracer.TraceError(message);
                        exceptions.Add(new EmailSendingException(message));
                    }
                }
                catch (Exception e)
                {
                    this.tracer.TraceError($"Failed to send email. Exception: {e}");
                    exceptions.Add(new EmailSendingException($"Exception was thrown fo sending signal results Email for signal {alertRule.SignalId}. Exception: {e}"));
                }
            }

            if (exceptions.Count > 0)
            {
                this.tracer.TraceError(
                    $"Failed to send one or more signal result emails." +
                    $"Number of exceptions thrown: {exceptions.Count()}.");
                throw new AggregateException("Failed to send one or more signal result emails", exceptions);
            }

            this.tracer.TraceInformation($"Sent signal result emails successfully for signal {alertRule.SignalId}");
        }