예제 #1
0
        private async Task GenerateAlarmsFor(AlertingGroup alertingGroup, bool dryRun)
        {
            if (alertingGroup.DynamoDb?.Tables == null || alertingGroup.DynamoDb.Tables.Count == 0)
            {
                return;
            }

            await _populator.PopulateDynamoTableNames(alertingGroup);

            var snsTopic = await _snsCreator.EnsureSnsTopic(alertingGroup, dryRun);

            var readAlarms = AlarmTablesHelper.FilterForRead(alertingGroup);

            readAlarms.SnsTopicArn         = snsTopic;
            readAlarms.DryRun              = dryRun;
            readAlarms.ThrottlingThreshold = alertingGroup.DynamoDb.ThrottlingThreshold ?? AwsConstants.ThrottlingThreshold;

            await EnsureReadAlarms(readAlarms);

            var writeAlarms = AlarmTablesHelper.FilterForWrite(alertingGroup);

            writeAlarms.SnsTopicArn         = snsTopic;
            writeAlarms.DryRun              = dryRun;
            writeAlarms.ThrottlingThreshold = alertingGroup.DynamoDb.ThrottlingThreshold ?? AwsConstants.ThrottlingThreshold;

            await EnsureWriteAlarms(writeAlarms);
        }
예제 #2
0
        private void LogAlertingGroup(string configFileName, AlertingGroup group)
        {
            var containedServiceCounts = CountContainedServices(group);

            _logger.Info($"Read alerting group {group.Name} containing {containedServiceCounts} from file {configFileName}");

            if (group.IsCatchAll)
            {
                _logger.Detail($"Alerting group {group.Name} is catch-all");
            }

            var monitorThrottling = group.DynamoDb.MonitorThrottling ?? true;

            if (monitorThrottling)
            {
                _logger.Detail($"Alerting group {group.Name} is monitoring throttled reads and writes");
            }

            if (group.DynamoDb?.Tables != null)
            {
                foreach (var table in group.DynamoDb.Tables)
                {
                    _logger.Detail(DescribeTable(table));
                }
            }

            if (group.Sqs?.Queues != null)
            {
                foreach (var queue in group.Sqs.Queues)
                {
                    _logger.Detail(DescribeQueue(queue));
                }
            }
        }
예제 #3
0
        private static string CountContainedServices(AlertingGroup group)
        {
            var tableCount   = group.DynamoDb.Tables?.Count ?? 0;
            var queueCount   = group.Sqs.Queues?.Count ?? 0;
            var serviceCount = group.Services?.AllServices?.Count ?? 0;

            if ((tableCount == 0) && (queueCount == 0) && (serviceCount == 0))
            {
                return("nothing");
            }

            var items = new List <string>();

            if (tableCount > 0)
            {
                items.Add($"{tableCount} tables");
            }

            if (queueCount > 0)
            {
                items.Add($"{queueCount} queues");
            }

            if (serviceCount > 0)
            {
                items.Add($"{serviceCount} service definitions");
            }

            return(string.Join(", ", items));
        }
예제 #4
0
        public async Task MatchAllTablesGetsTablesFromTableLoader()
        {
            var alertingGroup = new AlertingGroup
            {
                Name            = "test",
                AlarmNameSuffix = "test",
                DynamoDb        = new DynamoDb
                {
                    Tables = new List <Table>
                    {
                        new Table {
                            Pattern = ".*"
                        }
                    }
                }
            };

            var populator = CreatePopulator(true);

            await populator.PopulateDynamoTableNames(alertingGroup);

            Assert.That(alertingGroup.DynamoDb.Tables.Count, Is.EqualTo(3));
            ShouldHaveTable(alertingGroup.DynamoDb.Tables, "AutoTable1");
            ShouldHaveTable(alertingGroup.DynamoDb.Tables, "AutoTable2");
            ShouldHaveTable(alertingGroup.DynamoDb.Tables, "ATable3");

            _tableLoaderMock.Verify(t => t.GetResourceNamesAsync(), Times.Once);
        }
예제 #5
0
        public void ReadFilterIsAppliedForReadOnly()
        {
            var input = new AlertingGroup
            {
                DynamoDb = new DynamoDb
                {
                    Tables = new List <Table> {
                        "table1", "table2", "not_this_one"
                    },
                    ExcludeTablesPrefixedWith = new List <string> {
                        "not"
                    },
                    ExcludeReadsForTablesPrefixedWith = new List <string> {
                        "table1"
                    },
                    ExcludeWritesForTablesPrefixedWith = new List <string> {
                        "nomatch"
                    }
                }
            };

            var readTables  = AlarmTablesHelper.FilterForRead(input);
            var writeTables = AlarmTablesHelper.FilterForWrite(input);

            Assert.That(readTables.Tables, Is.EquivalentTo(new List <Table> {
                "table2"
            }));
            Assert.That(writeTables.Tables, Is.EquivalentTo(new List <Table> {
                "table1", "table2"
            }));
        }
예제 #6
0
        public async Task TableListIsPassedThrough()
        {
            var alertingGroup = new AlertingGroup
            {
                Name            = "test",
                AlarmNameSuffix = "test",
                DynamoDb        = new DynamoDb
                {
                    Tables = new List <Table>
                    {
                        new Table {
                            Name = "tableA", Threshold = 0.5
                        },
                        new Table {
                            Name = "tableB", Threshold = 0.4
                        }
                    }
                }
            };

            var populator = CreatePopulator(false);

            await populator.PopulateDynamoTableNames(alertingGroup);

            Assert.That(alertingGroup.DynamoDb.Tables.Count, Is.EqualTo(2));
            ShouldHaveTable(alertingGroup.DynamoDb.Tables, "tableA");
            ShouldHaveTable(alertingGroup.DynamoDb.Tables, "tableB");

            _tableLoaderMock.Verify(t => t.GetResourceNamesAsync(), Times.Never);
        }
        private static WatchmanConfiguration MakePatternConfig()
        {
            var alertingGroup = new AlertingGroup
            {
                AlarmNameSuffix = "test1",
                Sqs             = new Configuration.Sqs
                {
                    Queues = new List <Queue>
                    {
                        new Queue
                        {
                            Pattern         = "pattern",
                            LengthThreshold = 10,
                            Errors          = new ErrorQueue
                            {
                                LengthThreshold = 1
                            }
                        }
                    }
                }
            };

            return(new WatchmanConfiguration
            {
                AlertingGroups = new List <AlertingGroup>
                {
                    alertingGroup
                }
            });
        }
예제 #8
0
        private static void Validate(AlertingGroup alertingGroup)
        {
            if (string.IsNullOrWhiteSpace(alertingGroup.Name))
            {
                throw new ConfigException("AlertingGroup must have a name");
            }

            if (!TextIsValidInSnsTopic(alertingGroup.Name))
            {
                throw new ConfigException($"AlertingGroup name '{alertingGroup.Name}' must be valid in SNS topics");
            }

            if (string.IsNullOrWhiteSpace(alertingGroup.AlarmNameSuffix))
            {
                throw new ConfigException($"AlertingGroup '{alertingGroup.Name}' must have an alarm suffix");
            }

            if (!TextIsValidInSnsTopic(alertingGroup.AlarmNameSuffix))
            {
                throw new ConfigException($"AlertingGroup '{alertingGroup.Name}' must have a suffix valid in SNS topics. '{alertingGroup.AlarmNameSuffix}' is not.");
            }

            ValidateTargets(alertingGroup);

            var hasAtLeastOneResource = false;

            if (HasAny(alertingGroup.DynamoDb?.Tables))
            {
                hasAtLeastOneResource = true;
                DynamoDbValidation.Validate(alertingGroup.Name, alertingGroup.DynamoDb);
            }

            if (HasAny(alertingGroup.Sqs?.Queues))
            {
                hasAtLeastOneResource = true;
                SqsValidation.Validate(alertingGroup.Name, alertingGroup.Sqs);
            }

            if (alertingGroup.Services != null)
            {
                foreach (var service in alertingGroup.Services.AllServicesByName)
                {
                    if (service.Value != null)
                    {
                        AwsServiceValidation.Validate(alertingGroup.Name, service.Key, service.Value);

                        if (HasAny(service.Value.Resources))
                        {
                            hasAtLeastOneResource = true;
                        }
                    }
                }
            }

            if (!hasAtLeastOneResource)
            {
                throw new ConfigException($"AlertingGroup '{alertingGroup.Name}' must contain resources to monitor. " +
                                          "Specify one or more of DynamoDb, Sqs or other resources");
            }
        }
예제 #9
0
        private void ReadIntoTargets(AlertingGroup result, JToken jToken)
        {
            if (!jToken.HasValues)
            {
                return;
            }

            foreach (var item in jToken.Children())
            {
                if (item["Email"] != null)
                {
                    result.Targets.Add(new AlertEmail {
                        Email = item["Email"].ToString()
                    });
                }
                else if (item["Url"] != null)
                {
                    result.Targets.Add(new AlertUrl {
                        Url = item["Url"].ToString()
                    });
                }
                else
                {
                    _logger.Warn($"The target {jToken} is unknown. Valid targets are 'Email' and 'Url'.");
                }
            }
        }
예제 #10
0
 private static void SetErrorDefaultsOnQueue(AlertingGroup group, Queue queue)
 {
     if (queue.Errors == null)
     {
         queue.Errors = new ErrorQueue();
     }
     queue.Errors.ReadDefaults(group.Sqs.Errors);
 }
예제 #11
0
 private static void SetErrorDefaultsOnAlertingGroup(AlertingGroup alertingGroup)
 {
     if (alertingGroup.Sqs.Errors == null)
     {
         alertingGroup.Sqs.Errors = new ErrorQueue();
     }
     alertingGroup.Sqs.Errors.ReadDefaults(ErrorQueueDefaults);
 }
예제 #12
0
 private static void SetErrorDefaults(AlertingGroup alertingGroup)
 {
     SetErrorDefaultsOnAlertingGroup(alertingGroup);
     foreach (var configuredQueue in alertingGroup.Sqs.Queues)
     {
         SetErrorDefaultsOnQueue(alertingGroup, configuredQueue);
     }
 }
예제 #13
0
 public static WatchmanConfiguration WrapGroup(AlertingGroup group)
 {
     return(new WatchmanConfiguration
     {
         AlertingGroups = new List <AlertingGroup> {
             group
         }
     });
 }
예제 #14
0
 private WatchmanConfiguration MakeConfigFor(AlertingGroup group)
 {
     return(new WatchmanConfiguration
     {
         AlertingGroups = new List <AlertingGroup> {
             group
         }
     });
 }
예제 #15
0
        private int QueueLengthThreshold(Queue queue, AlertingGroup group)
        {
            if (queue.IsErrorQueue())
            {
                return(queue.Errors.LengthThreshold.Value);
            }

            return(queue.LengthThreshold ?? group.Sqs.LengthThreshold ?? AwsConstants.QueueLengthThreshold);
        }
예제 #16
0
        private int?OldestMessageThreshold(Queue queue, AlertingGroup group)
        {
            if (queue.IsErrorQueue())
            {
                return(queue.Errors.OldestMessageThreshold);
            }

            return(queue.OldestMessageThreshold ?? group.Sqs.OldestMessageThreshold ?? AwsConstants.OldestMessageThreshold);
        }
        private static AwsServiceAlarms GetService(AlertingGroup group, string serviceIdentifier)
        {
            if (!group.Services.ContainsKey(serviceIdentifier))
            {
                return(null);
            }

            return(group.Services[serviceIdentifier]);
        }
        private static void AssertGroupDataIsLoaded(AlertingGroup alertingGroup)
        {
            Assert.That(alertingGroup.Name, Is.Not.Empty);
            Assert.That(alertingGroup.Targets, Is.Not.Empty);
            Assert.That(alertingGroup.AlarmNameSuffix, Is.Not.Empty);

            var alertEmail = (AlertEmail)alertingGroup.Targets.First();

            Assert.That(alertEmail.Email, Is.Not.Empty);
        }
예제 #19
0
        public async Task <string> EnsureSnsTopic(AlertingGroup alertingGroup, bool dryRun)
        {
            var snsTopicArn = await _snsTopicCreator.EnsureSnsTopic(alertingGroup.Name, dryRun);

            if (!dryRun)
            {
                await _snsSubscriptionCreator.EnsureSnsSubscriptions(alertingGroup, snsTopicArn);
            }
            return(snsTopicArn);
        }
예제 #20
0
        private static int CountGenericServices(AlertingGroup group)
        {
            if (group.Services == null)
            {
                return(0);
            }

            return(group.Services.Values
                   .Select(v => v.Resources?.Count ?? 0)
                   .Sum());
        }
        private static ServiceAlertingGroup ServiceAlertingGroup(AlertingGroup ag, Func <AlertingGroup, AwsServiceAlarms> readServiceFromGroup)
        {
            var service = readServiceFromGroup(ag);

            if (service == null)
            {
                return(null);
            }

            return(Map(ag, service));
        }
예제 #22
0
        private static void ReadServiceDefinitions(JObject jsonObject, AlertingGroup result, JsonSerializer serializer)
        {
            if (jsonObject["DynamoDb"] != null)
            {
                result.DynamoDb = jsonObject["DynamoDb"].ToObject <DynamoDb>(serializer);
            }

            if (jsonObject["Sqs"] != null)
            {
                result.Sqs = jsonObject["Sqs"].ToObject <Sqs>(serializer);
            }
        }
 private static ServiceAlertingGroup Map(AlertingGroup input, AwsServiceAlarms service)
 {
     return(new ServiceAlertingGroup
     {
         AlarmNameSuffix = input.AlarmNameSuffix,
         IsCatchAll = input.IsCatchAll,
         Name = input.Name,
         ReportTargets = input.ReportTargets,
         Service = service,
         Targets = input.Targets
     });
 }
        private static ServiceAlertingGroup <T> ServiceAlertingGroup <T>(AlertingGroup ag,
                                                                         Func <AlertingGroup, AwsServiceAlarms <T> > readServiceFromGroup) where T : class
        {
            var service = readServiceFromGroup(ag);

            if (service == null)
            {
                return(null);
            }

            return(Map <T>(ag, service));
        }
 private static ServiceAlertingGroup <T> Map <T>(AlertingGroup input, AwsServiceAlarms <T> service) where T : class
 {
     return(new ServiceAlertingGroup <T>
     {
         GroupParameters = new AlertingGroupParameters(
             input.Name,
             input.AlarmNameSuffix,
             input.Targets,
             input.IsCatchAll
             ),
         Service = service
     });
 }
예제 #26
0
        public static AlarmTables FilterForRead(AlertingGroup alertingGroup)
        {
            var filteredTables = alertingGroup.DynamoDb.Tables
                                 .ExcludePrefixes(alertingGroup.DynamoDb.ExcludeTablesPrefixedWith, t => t.Name)
                                 .ExcludePrefixes(alertingGroup.DynamoDb.ExcludeReadsForTablesPrefixedWith, t => t.Name);

            return(new AlarmTables
            {
                AlarmNameSuffix = alertingGroup.AlarmNameSuffix,
                Threshold = alertingGroup.DynamoDb.Threshold ?? AwsConstants.DefaultCapacityThreshold,
                MonitorThrottling = alertingGroup.DynamoDb.MonitorThrottling ?? true,
                Tables = filteredTables.ToList()
            });
        }
예제 #27
0
        private static ServiceAlertingGroup <T> ServiceAlertingGroup <T>(AlertingGroup ag,
                                                                         Func <AlertingGroup, AwsServiceAlarms <T> > readServiceFromGroup) where T : class
        {
            var service = readServiceFromGroup(ag);

            if (service == null)
            {
                return(Map <T>(ag, new AwsServiceAlarms <T>()
                {
                    Resources = new List <ResourceThresholds <T> >()
                }));
            }

            return(Map <T>(ag, service));
        }
 private static ServiceAlertingGroup <T> Map <T>(AlertingGroup input, AwsServiceAlarms <T> service) where T : class
 {
     return(new ServiceAlertingGroup <T>
     {
         GroupParameters = new AlertingGroupParameters(
             input.Name,
             input.AlarmNameSuffix,
             input.Targets,
             input.IsCatchAll,
             input.Description,
             input.NumberOfCloudFormationStacks
             ),
         Service = service
     });
 }
예제 #29
0
        private static void ReadServiceDefinitions(JObject jsonObject, AlertingGroup result, JsonSerializer serializer)
        {
            var readDynamo = false;
            var readSqs    = false;

            if (jsonObject["DynamoDb"] != null)
            {
                result.DynamoDb = jsonObject["DynamoDb"].ToObject <DynamoDb>(serializer);
                readDynamo      = true;
            }

            if (jsonObject["Sqs"] != null)
            {
                result.Sqs = jsonObject["Sqs"].ToObject <Sqs>(serializer);
                readSqs    = true;
            }

            var allServices = (JObject)jsonObject["Services"];

            if (allServices != null)
            {
                foreach (var prop in allServices)
                {
                    if (prop.Key == "DynamoDb")
                    {
                        if (readDynamo)
                        {
                            throw new JsonReaderException("DynamoDb block can only defined once");
                        }

                        result.DynamoDb = prop.Value.ToObject <DynamoDb>(serializer);
                    }
                    else if (prop.Key == "Sqs")
                    {
                        if (readSqs)
                        {
                            throw new JsonReaderException("Sqs block can only defined once");
                        }

                        result.Sqs = prop.Value.ToObject <Sqs>(serializer);
                    }
                    else
                    {
                        result.Services[prop.Key] = prop.Value.ToObject <AwsServiceAlarms>(serializer);
                    }
                }
            }
        }
예제 #30
0
        public static WatchmanConfiguration WrapDynamo(DynamoDb dynamo)
        {
            var ag = new AlertingGroup
            {
                Name            = "TestGroup",
                AlarmNameSuffix = "TestGroup",
                IsCatchAll      = true,
                Targets         = new List <AlertTarget>
                {
                    new AlertEmail("*****@*****.**")
                },
                DynamoDb = dynamo
            };

            return(WrapGroup(ag));
        }