Example #1
0
        public async Task SaveChanges_SmallTemplate_SubmitsDirectly()
        {
            // arrange
            var alarm     = Alarm();
            var group     = Group();
            var stackName = ExpectedStackName(group);

            SetupListStacksToReturnStackNames();
            SetupStackStatusSequence(stackName, new List <string> {
                "CREATE_COMPLETE"
            });

            var s3Location = new S3Location("bucket", "s3/path");

            var deployer = MakeDeployer(s3Location, TimeSpan.Zero, TimeSpan.FromMinutes(1));

            var sut = new CloudFormationAlarmCreator(deployer, new ConsoleAlarmLogger(false));

            // act
            sut.AddAlarms(group, new[] { alarm });
            await sut.SaveChanges(false);

            // assert
            _s3Mock
            .Verify(x => x.PutObjectAsync(
                        It.IsAny <PutObjectRequest>(),
                        It.IsAny <CancellationToken>()), Times.Never);

            _cloudFormationMock
            .Verify(x => x.CreateStackAsync(
                        It.Is <CreateStackRequest>(s => s.StackName == stackName &&
                                                   s.TemplateURL == null &&
                                                   !string.IsNullOrWhiteSpace(s.TemplateBody)),
                        It.IsAny <CancellationToken>()), Times.Exactly(1));
        }
Example #2
0
        public async Task SaveChanges_StackDoesNotExist_StackIsCreated()
        {
            // arrange
            var alarm     = Alarm();
            var group     = Group();
            var stackName = ExpectedStackName(group);

            SetupListStacksToReturnStackNames();
            SetStatusForStackName(stackName, "CREATE_COMPLETE");

            var deployer = MakeDeployer(null,
                                        TimeSpan.FromMilliseconds(5),
                                        TimeSpan.FromMilliseconds(5));

            var sut = new CloudFormationAlarmCreator(deployer, new ConsoleAlarmLogger(false));

            sut.AddAlarms(group, new[] { alarm });

            // act
            await sut.SaveChanges(false);

            // assert
            _cloudFormationMock
            .Verify(x => x.CreateStackAsync(
                        It.Is <CreateStackRequest>(s => s.StackName == stackName),
                        It.IsAny <CancellationToken>()));
        }
Example #3
0
        public async Task SaveChanges_DryRun_NoStackChangesMade()
        {
            // arrange
            var alarm = Alarm();

            SetupListStacksToReturnStackNames();

            var sut = new CloudFormationAlarmCreator(
                new CloudformationStackDeployer(
                    new ConsoleAlarmLogger(false),
                    _cloudFormationMock.Object,
                    _s3Mock.Object,
                    null));

            sut.AddAlarm(alarm);

            // act
            await sut.SaveChanges(true);

            // assert
            _cloudFormationMock
            .Verify(x => x.CreateStackAsync(
                        It.IsAny <CreateStackRequest>(),
                        It.IsAny <CancellationToken>()), Times.Never());
        }
Example #4
0
        public async Task SaveChanges_NoAlarms_NoStackChangesMade()
        {
            // arrange
            var sut = new CloudFormationAlarmCreator(
                new CloudformationStackDeployer(
                    new ConsoleAlarmLogger(false),
                    _cloudFormationMock.Object,
                    _s3Mock.Object,
                    null
                    ));

            // act
            await sut.SaveChanges(false);

            // assert
            _cloudFormationMock
            .Verify(x => x.CreateStackAsync(
                        It.IsAny <CreateStackRequest>(),
                        It.IsAny <CancellationToken>()), Times.Never);

            _cloudFormationMock
            .Verify(x => x.UpdateStackAsync(
                        It.IsAny <UpdateStackRequest>(),
                        It.IsAny <CancellationToken>()), Times.Never);
        }
Example #5
0
        public async Task SaveChanges_StackExists_StackIsUpdated()
        {
            // arrange
            var alarm     = Alarm();
            var stackName = ExpectedStackName(alarm.AlertingGroup);

            SetupListStacksToReturnStackNames(stackName);
            SetStatusForStackName(stackName, "UPDATE_COMPLETE");

            var sut = new CloudFormationAlarmCreator(
                new CloudformationStackDeployer(
                    new ConsoleAlarmLogger(false),
                    _cloudFormationMock.Object,
                    _s3Mock.Object,
                    null,
                    TimeSpan.FromMilliseconds(5),
                    TimeSpan.FromMilliseconds(5))
                );

            sut.AddAlarm(alarm);

            // act
            await sut.SaveChanges(false);

            // assert
            _cloudFormationMock
            .Verify(x => x.UpdateStackAsync(
                        It.Is <UpdateStackRequest>(s => s.StackName == stackName),
                        It.IsAny <CancellationToken>()));
        }
        public async Task IgnoresNamedEntitiesThatDoNotExist()
        {
            // arrange

            var stack = new Mock <ICloudformationStackDeployer>();

            var dynamoClient = CreateDynamoClientForTables(new[]
            {
                new TableDescription()
                {
                    TableName             = "first-dynamo-table",
                    ProvisionedThroughput = new ProvisionedThroughputDescription()
                    {
                        ReadCapacityUnits  = 10,
                        WriteCapacityUnits = 10
                    }
                }
            });

            var source = new TableDescriptionSource(dynamoClient);

            var creator = new CloudFormationAlarmCreator(stack.Object, new ConsoleAlarmLogger(true));

            var config = ConfigHelper.CreateBasicConfiguration("test", "group-suffix", "DynamoDb", new List <ResourceThresholds>()
            {
                new ResourceThresholds()
                {
                    Name = "non-existant-table"
                }
            });

            var sut = IoCHelper.CreateSystemUnderTest(
                source,
                new DynamoDbDataProvider(),
                new DynamoDbDataProvider(),
                WatchmanServiceConfigurationMapper.MapDynamoDb,
                creator,
                ConfigHelper.ConfigLoaderFor(config)
                );



            // act

            await sut.LoadAndGenerateAlarms(RunMode.GenerateAlarms);

            // assert

            stack
            .Verify(x => x.DeployStack(
                        It.IsAny <string>(),
                        It.IsAny <string>(),
                        It.IsAny <bool>()
                        ), Times.Never);
        }
Example #7
0
        public async Task SaveChanges_LargeTemplate_SubmitsViaS3()
        {
            // arrange

            var alarms = Enumerable.Range(0, 120)
                         .Select(x => Alarm($"alarm-{x}"))
                         .ToList();

            var stackName = ExpectedStackName(alarms.First().AlertingGroup);

            SetupListStacksToReturnStackNames();
            SetupStackStatusSequence(stackName, new List <string> {
                "CREATE_COMPLETE"
            });

            var s3Location = new S3Location("bucket", "s3/path");

            var sut = new CloudFormationAlarmCreator(
                new CloudformationStackDeployer(
                    new ConsoleAlarmLogger(false),
                    _cloudFormationMock.Object,
                    _s3Mock.Object,
                    s3Location,
                    TimeSpan.Zero,
                    TimeSpan.FromMinutes(1)
                    ));

            foreach (var alarm in alarms)
            {
                sut.AddAlarm(alarm);
            }

            // act
            await sut.SaveChanges(false);

            // assert
            var s3Path = $"{s3Location.Path}/{stackName}.json";

            _s3Mock
            .Verify(x => x.PutObjectAsync(
                        It.Is <PutObjectRequest>(r => r.BucketName == s3Location.BucketName && r.Key == s3Path),
                        It.IsAny <CancellationToken>()));

            var expectedStackUrl = $"https://s3.amazonaws.com/{s3Location.BucketName}/{s3Path}";

            _cloudFormationMock
            .Verify(x => x.CreateStackAsync(
                        It.Is <CreateStackRequest>(s => s.StackName == stackName &&
                                                   s.TemplateURL == expectedStackUrl &&
                                                   s.TemplateBody == null),
                        It.IsAny <CancellationToken>()), Times.Exactly(1));
        }
Example #8
0
        public async Task SaveChanges_ChangesToStackSubmitted_WaitsForTargetStatus()
        {
            // arrange
            var alarm     = Alarm();
            var stackName = ExpectedStackName(alarm.AlertingGroup);

            SetupListStacksToReturnStackNames();

            SetupStackStatusSequence(stackName, new List <string> {
                "CREATE_IN_PROGRESS", "CREATE_IN_PROGRESS", "CREATE_COMPLETE"
            });

            var statusCheckDelay = TimeSpan.FromMilliseconds(200);
            var sut = new CloudFormationAlarmCreator(
                new CloudformationStackDeployer(
                    new ConsoleAlarmLogger(false),
                    _cloudFormationMock.Object,
                    _s3Mock.Object,
                    null,
                    statusCheckDelay,
                    TimeSpan.FromMinutes(5)));

            sut.AddAlarm(alarm);

            var start = DateTime.UtcNow;

            // act
            await sut.SaveChanges(false);

            // assert

            var actualWaitMillis    = (DateTime.UtcNow - start).TotalMilliseconds;
            var expectedDelayMillis = statusCheckDelay.TotalMilliseconds * 3;

            Assert.That(actualWaitMillis, Is.GreaterThanOrEqualTo(expectedDelayMillis));

            // this is pretty rough because other parts of the method takes a while, just want to check the time isn't stupidly long
            var maxDelayMillis = expectedDelayMillis * 4;

            Assert.That(actualWaitMillis, Is.LessThanOrEqualTo(maxDelayMillis));

            _cloudFormationMock
            .Verify(x => x.DescribeStacksAsync(
                        It.Is <DescribeStacksRequest>(s => s.StackName == stackName),
                        It.IsAny <CancellationToken>()), Times.Exactly(3));
        }
Example #9
0
        public void SaveChanges_CloudformationFails_Throws()
        {
            // arrange
            var alarm = Alarm();

            SetupListStacksToReturnStackNames();
            SetupCreateStackAsyncToFail();

            var deployer = MakeDeployer();

            var sut = new CloudFormationAlarmCreator(deployer, new ConsoleAlarmLogger(false));

            sut.AddAlarms(Group(), new[] { alarm });

            // act
            var ex = Assert.ThrowsAsync <WatchmanException>(() => sut.SaveChanges(false));

            Assert.That(ex.Message, Is.EqualTo("1 stacks failed to deploy"));
        }
Example #10
0
        public async Task SaveChanges_ConfigResultsInMultipleStacks_Aborts()
        {
            //arrange
            var alarm1 = Alarm("alarm 1");
            var alarm2 = Alarm("alarm 2");

            // two groups with the same name but different suffix, so that the equality/hash compares will fail
            // but would result in two cloudformation stacks with the same name
            var group1 = Group("name", "suffix1");
            var group2 = Group("name", "suffix2");

            SetupListStacksToReturnStackNames();
            SetupStackStatusSequence(ExpectedStackName(group1), new List <string> {
                "CREATE_COMPLETE", "CREATE_COMPLETE"
            });

            var s3Location = new S3Location("bucket", "s3/path");

            var deployer = MakeDeployer(s3Location, TimeSpan.Zero, TimeSpan.FromMinutes(1));

            var sut = new CloudFormationAlarmCreator(deployer, new ConsoleAlarmLogger(false));

            //act

            Exception caught = null;

            try
            {
                sut.AddAlarms(group1, new[] { alarm1 });
                sut.AddAlarms(group2, new[] { alarm2 });
                await sut.SaveChanges(false);;
            }
            catch (Exception ex)
            {
                caught = ex;
            }

            //assert

            Assert.That(caught, Is.Not.Null);
            Assert.That(caught.Message, Contains.Substring("Cannot deploy: multiple stacks would be created with the same name"));
        }
        public async Task UsesDelayedScalingThresholdFromCloudWatch()
        {
            // arrange

            var stack = new FakeStackDeployer();

            var autoScalingClient = FakeAwsClients.CreateAutoScalingClientForGroups(new[]
            {
                new AutoScalingGroup()
                {
                    AutoScalingGroupName = "group-delay-20"
                },
                new AutoScalingGroup()
                {
                    AutoScalingGroupName = "group-delay-100"
                }
            });

            var source = new AutoScalingGroupSource(autoScalingClient);

            var creator = new CloudFormationAlarmCreator(stack, new ConsoleAlarmLogger(true));

            var config = ConfigHelper.CreateBasicConfiguration("test", "group-suffix",
                                                               new AlertingGroupServices()
            {
                AutoScaling = new AwsServiceAlarms <AutoScalingResourceConfig>()
                {
                    Resources = new List <ResourceThresholds <AutoScalingResourceConfig> >()
                    {
                        new ResourceThresholds <AutoScalingResourceConfig>()
                        {
                            Name    = "group-delay-20",
                            Options = new AutoScalingResourceConfig()
                            {
                                InstanceCountIncreaseDelayMinutes = 20
                            }
                        },
                        new ResourceThresholds <AutoScalingResourceConfig>()
                        {
                            Name = "group-delay-100"
                        }
                    },

                    Options = new AutoScalingResourceConfig()
                    {
                        InstanceCountIncreaseDelayMinutes = 100
                    }
                }
            });

            var now = DateTime.Parse("2018-01-26");

            var fakeTime = new Mock <ICurrentTimeProvider>();

            fakeTime.Setup(f => f.UtcNow).Returns(now);

            var cloudWatch = new Mock <IAmazonCloudWatch>();

            SetupCloudWatchDesiredMetric(cloudWatch, 100 * 60, now, "group-delay-100", 90);
            SetupCloudWatchDesiredMetric(cloudWatch, 20 * 60, now, "group-delay-20", 80);

            var provider = new AutoScalingGroupAlarmDataProvider(cloudWatch.Object, fakeTime.Object);

            var sut = IoCHelper.CreateSystemUnderTest(
                source,
                provider,
                provider,
                WatchmanServiceConfigurationMapper.MapAutoScaling,
                creator,
                ConfigHelper.ConfigLoaderFor(config)
                );


            // act

            await sut.LoadAndGenerateAlarms(RunMode.GenerateAlarms);

            // assert

            var alarms = stack
                         .Stack("Watchman-test")
                         .AlarmsByDimension("AutoScalingGroupName");

            var alarm100 = alarms["group-delay-100"].Single(a => a.Properties["AlarmName"].ToString().Contains("InService"));
            var alarm20  = alarms["group-delay-20"].Single(a => a.Properties["AlarmName"].ToString().Contains("InService"));

            var defaultAlarmThreshold = 0.5m;

            Assert.That((decimal)alarm100.Properties["Threshold"], Is.EqualTo(90 * defaultAlarmThreshold));
            Assert.That((decimal)alarm20.Properties["Threshold"], Is.EqualTo(80 * defaultAlarmThreshold));
        }
        public async Task TimesSuppliedToCloudWatchAreUtc()
        {
            // arrange

            var stack = new FakeStackDeployer();

            var autoScalingClient = FakeAwsClients.CreateAutoScalingClientForGroups(new[]
            {
                new AutoScalingGroup()
                {
                    AutoScalingGroupName = "group-1",
                    DesiredCapacity      = 40
                }
            });

            var source = new AutoScalingGroupSource(autoScalingClient);

            var creator = new CloudFormationAlarmCreator(stack, new ConsoleAlarmLogger(true));

            var config = ConfigHelper.CreateBasicConfiguration("test", "group-suffix",
                                                               new AlertingGroupServices()
            {
                AutoScaling = new AwsServiceAlarms <AutoScalingResourceConfig>()
                {
                    Resources = new List <ResourceThresholds <AutoScalingResourceConfig> >()
                    {
                        new ResourceThresholds <AutoScalingResourceConfig>()
                        {
                            Name    = "group-1",
                            Options = new AutoScalingResourceConfig()
                            {
                                InstanceCountIncreaseDelayMinutes = 5
                            }
                        }
                    }
                }
            });


            var cloudWatch = new Mock <IAmazonCloudWatch>();

            cloudWatch
            .Setup(c => c.GetMetricStatisticsAsync(It.IsAny <GetMetricStatisticsRequest>(),
                                                   It.IsAny <CancellationToken>()))
            .ReturnsAsync(new GetMetricStatisticsResponse()
            {
                Datapoints = new List <Datapoint>()
                {
                    new Datapoint()
                    {
                        Minimum = 5
                    }
                }
            });


            var provider = new AutoScalingGroupAlarmDataProvider(cloudWatch.Object, new CurrentTimeProvider());

            var sut = IoCHelper.CreateSystemUnderTest(
                source,
                provider,
                provider,
                WatchmanServiceConfigurationMapper.MapAutoScaling,
                creator,
                ConfigHelper.ConfigLoaderFor(config)
                );


            // act

            await sut.LoadAndGenerateAlarms(RunMode.GenerateAlarms);

            // assert
            cloudWatch.Verify(x => x.GetMetricStatisticsAsync(
                                  It.Is <GetMetricStatisticsRequest>(
                                      r => r.StartTime.Kind == DateTimeKind.Utc &&
                                      r.EndTime.Kind == DateTimeKind.Utc
                                      ), It.IsAny <CancellationToken>())
                              );
        }
        public async Task UsesDesiredInstancesForThresholdByDefault()
        {
            // arrange

            var stack = new FakeStackDeployer();

            var autoScalingClient = FakeAwsClients.CreateAutoScalingClientForGroups(new[]
            {
                new AutoScalingGroup()
                {
                    AutoScalingGroupName = "group-1",
                    DesiredCapacity      = 40
                }
            });

            var source = new AutoScalingGroupSource(autoScalingClient);

            var creator = new CloudFormationAlarmCreator(stack, new ConsoleAlarmLogger(true));

            var config = ConfigHelper.CreateBasicConfiguration("test", "group-suffix",
                                                               new AlertingGroupServices()
            {
                AutoScaling = new AwsServiceAlarms <AutoScalingResourceConfig>()
                {
                    Resources = new List <ResourceThresholds <AutoScalingResourceConfig> >()
                    {
                        new ResourceThresholds <AutoScalingResourceConfig>()
                        {
                            Name = "group-1"
                        }
                    }
                }
            });


            var fakeTime   = new Mock <ICurrentTimeProvider>();
            var cloudWatch = new Mock <IAmazonCloudWatch>();

            var provider = new AutoScalingGroupAlarmDataProvider(cloudWatch.Object, fakeTime.Object);

            var sut = IoCHelper.CreateSystemUnderTest(
                source,
                provider,
                provider,
                WatchmanServiceConfigurationMapper.MapAutoScaling,
                creator,
                ConfigHelper.ConfigLoaderFor(config)
                );


            // act

            await sut.LoadAndGenerateAlarms(RunMode.GenerateAlarms);

            // assert

            var alarms = stack
                         .Stack("Watchman-test")
                         .AlarmsByDimension("AutoScalingGroupName");

            var alarm = alarms["group-1"].Single(a => a.Properties["AlarmName"].ToString().Contains("InService"));

            var defaultAlarmThreshold = 0.5m;

            Assert.That((decimal)alarm.Properties["Threshold"], Is.EqualTo(40 * defaultAlarmThreshold));
        }
Example #14
0
        public async Task ContinuesWhenAlarmGenerationFailsForOneAlertingGroup()
        {
            // arrange

            var stack = new FakeStackDeployer();

            var autoScalingClient = FakeAwsClients.CreateAutoScalingClientForGroups(new[]
            {
                new AutoScalingGroup()
                {
                    AutoScalingGroupName = "group-1",
                    DesiredCapacity      = 40
                },
                new AutoScalingGroup()
                {
                    AutoScalingGroupName = "group-2",
                    DesiredCapacity      = 10
                }
            });

            var source = new AutoScalingGroupSource(autoScalingClient);

            var creator = new CloudFormationAlarmCreator(stack, new ConsoleAlarmLogger(true));

            var config1 = new AlertingGroup()
            {
                Name            = "group-1",
                AlarmNameSuffix = "suffix-1",
                Targets         = new List <AlertTarget>()
                {
                    new AlertEmail("*****@*****.**")
                },
                Services = new AlertingGroupServices()
                {
                    AutoScaling = new AwsServiceAlarms <AutoScalingResourceConfig>()
                    {
                        Resources = new List <ResourceThresholds <AutoScalingResourceConfig> >()
                        {
                            new ResourceThresholds <AutoScalingResourceConfig>()
                            {
                                Name    = "group-1",
                                Options = new AutoScalingResourceConfig()
                                {
                                    // this will trigger the failure
                                    InstanceCountIncreaseDelayMinutes = 5
                                }
                            }
                        }
                    }
                }
            };

            var config2 = new AlertingGroup()
            {
                Name            = "group-2",
                AlarmNameSuffix = "suffix-2",
                Targets         = new List <AlertTarget>()
                {
                    new AlertEmail("*****@*****.**")
                },
                Services = new AlertingGroupServices()
                {
                    AutoScaling = new AwsServiceAlarms <AutoScalingResourceConfig>()
                    {
                        Resources = new List <ResourceThresholds <AutoScalingResourceConfig> >()
                        {
                            new ResourceThresholds <AutoScalingResourceConfig>()
                            {
                                Name = "group-2"
                            }
                        }
                    }
                }
            };

            var cloudWatch = new Mock <IAmazonCloudWatch>();

            cloudWatch
            .Setup(c => c.GetMetricStatisticsAsync(It.IsAny <GetMetricStatisticsRequest>(),
                                                   It.IsAny <CancellationToken>()))
            .ThrowsAsync(new Exception("something bad"));


            var provider = new AutoScalingGroupAlarmDataProvider(cloudWatch.Object, new CurrentTimeProvider());

            var sut = IoCHelper.CreateSystemUnderTest(
                source,
                provider,
                provider,
                WatchmanServiceConfigurationMapper.MapAutoScaling,
                creator,
                ConfigHelper.ConfigLoaderFor(config1, config2)
                );

            Exception caught = null;

            // act
            try
            {
                await sut.LoadAndGenerateAlarms(RunMode.GenerateAlarms);
            }
            catch (Exception ex)
            {
                caught = ex;
            }

            // assert
            Assert.That(stack.StackWasDeployed("Watchman-group-1"), Is.EqualTo(false));
            Assert.That(stack.StackWasDeployed("Watchman-group-2"), Is.EqualTo(true));
            Assert.That(caught, Is.Not.Null);
        }
        public async Task AlarmCreatedWithCorrectProperties()
        {
            // arrange

            var stack = new FakeStackDeployer();

            var dynamoClient = CreateDynamoClientForTables(new[]
            {
                new TableDescription()
                {
                    TableName             = "first-dynamo-table",
                    ProvisionedThroughput = new ProvisionedThroughputDescription()
                    {
                        ReadCapacityUnits  = 100,
                        WriteCapacityUnits = 200
                    }
                }
            });

            var source  = new TableDescriptionSource(dynamoClient);
            var creator = new CloudFormationAlarmCreator(stack, new ConsoleAlarmLogger(true));

            var config = ConfigHelper.CreateBasicConfiguration("test", "group-suffix", "DynamoDb", new List <ResourceThresholds>()
            {
                new ResourceThresholds()
                {
                    Name = "first-dynamo-table"
                }
            });

            var sut = IoCHelper.CreateSystemUnderTest(
                source,
                new DynamoDbDataProvider(),
                new DynamoDbDataProvider(),
                WatchmanServiceConfigurationMapper.MapDynamoDb,
                creator, ConfigHelper.ConfigLoaderFor(config)
                );

            // act

            await sut.LoadAndGenerateAlarms(RunMode.GenerateAlarms);

            // assert

            const decimal defaultCapacityThresholdFraction = 0.8m;
            const int     defaultThrottleThreshold         = 2;

            var alarmsByTable = stack
                                .Stack("Watchman-test")
                                .AlarmsByDimension("TableName");

            Assert.That(alarmsByTable.ContainsKey("first-dynamo-table"), Is.True);
            var alarms = alarmsByTable["first-dynamo-table"];

            Assert.That(alarms.Exists(
                            alarm =>
                            alarm.Properties["MetricName"].Value <string>() == "ConsumedReadCapacityUnits" &&
                            alarm.Properties["AlarmName"].Value <string>().Contains("ConsumedReadCapacityUnitsHigh") &&
                            alarm.Properties["Threshold"].Value <int>() == 100 * defaultCapacityThresholdFraction &&
                            alarm.Properties["Period"].Value <int>() == 60 &&
                            alarm.Properties["ComparisonOperator"].Value <string>() == "GreaterThanOrEqualToThreshold" &&
                            alarm.Properties["Statistic"].Value <string>() == "Sum" &&
                            alarm.Properties["Namespace"].Value <string>() == AwsNamespace.DynamoDb
                            )
                        );

            Assert.That(alarms.Exists(
                            alarm =>
                            alarm.Properties["MetricName"].Value <string>() == "ConsumedWriteCapacityUnits" &&
                            alarm.Properties["AlarmName"].Value <string>().Contains("ConsumedWriteCapacityUnitsHigh") &&
                            alarm.Properties["Threshold"].Value <int>() == 200 * defaultCapacityThresholdFraction &&
                            alarm.Properties["Period"].Value <int>() == 60 &&
                            alarm.Properties["ComparisonOperator"].Value <string>() == "GreaterThanOrEqualToThreshold" &&
                            alarm.Properties["Statistic"].Value <string>() == "Sum" &&
                            alarm.Properties["Namespace"].Value <string>() == AwsNamespace.DynamoDb
                            )
                        );

            Assert.That(alarms.Exists(
                            alarm =>
                            alarm.Properties["MetricName"].Value <string>() == "ThrottledRequests" &&
                            alarm.Properties["AlarmName"].Value <string>().Contains("ThrottledRequestsHigh") &&
                            alarm.Properties["Threshold"].Value <int>() == defaultThrottleThreshold &&
                            alarm.Properties["Period"].Value <int>() == 60 &&
                            alarm.Properties["ComparisonOperator"].Value <string>() == "GreaterThanOrEqualToThreshold" &&
                            alarm.Properties["Statistic"].Value <string>() == "Sum" &&
                            alarm.Properties["Namespace"].Value <string>() == AwsNamespace.DynamoDb
                            )
                        );
        }
Example #16
0
        public async Task AlarmCreatedWithCorrectProperties()
        {
            // arrange

            var fakeStackDeployer = new FakeStackDeployer();

            var dynamoClient = FakeAwsClients.CreateDynamoClientForTables(new[]
            {
                new TableDescription()
                {
                    TableName             = "first-dynamo-table",
                    ProvisionedThroughput = new ProvisionedThroughputDescription()
                    {
                        ReadCapacityUnits  = 100,
                        WriteCapacityUnits = 200
                    }
                }
            });

            var lambdaClient = FakeAwsClients.CreateLambdaClientForFunctions(new[]
            {
                new FunctionConfiguration()
                {
                    FunctionName = "first-lambda-function"
                }
            });

            var creator = new CloudFormationAlarmCreator(fakeStackDeployer, new ConsoleAlarmLogger(true));

            var config = ConfigHelper.CreateBasicConfiguration(
                "test",
                "group-suffix",
                new AlertingGroupServices()
            {
                DynamoDb = new AwsServiceAlarms <ResourceConfig>()
                {
                    Resources = new List <ResourceThresholds <ResourceConfig> >()
                    {
                        new ResourceThresholds <ResourceConfig>()
                        {
                            Name = "first-dynamo-table"
                        }
                    }
                },
                Lambda = new AwsServiceAlarms <ResourceConfig>()
                {
                    Resources = new List <ResourceThresholds <ResourceConfig> >()
                    {
                        new ResourceThresholds <ResourceConfig>()
                        {
                            Name = "first-lambda-function"
                        }
                    }
                }
            }
                );

            var sutBuilder = new Builder(ConfigHelper.ConfigLoaderFor(config), creator);

            sutBuilder.AddService(
                new TableDescriptionSource(dynamoClient),
                new DynamoDbDataProvider(),
                new DynamoDbDataProvider(),
                WatchmanServiceConfigurationMapper.MapDynamoDb
                );

            sutBuilder.AddService(
                new LambdaSource(lambdaClient),
                new LambdaAlarmDataProvider(),
                new LambdaAlarmDataProvider(),
                WatchmanServiceConfigurationMapper.MapLambda
                );

            var sut = sutBuilder.Build();

            // act

            await sut.LoadAndGenerateAlarms(RunMode.GenerateAlarms);

            // assert

            var alarmsByTable = fakeStackDeployer
                                .Stack("Watchman-test")
                                .AlarmsByDimension("TableName");

            Assert.That(alarmsByTable.ContainsKey("first-dynamo-table"), Is.True);

            var alarmsByFunction = fakeStackDeployer
                                   .Stack("Watchman-test")
                                   .AlarmsByDimension("FunctionName");

            Assert.That(alarmsByFunction.ContainsKey("first-lambda-function"), Is.True);
        }
Example #17
0
        public async Task SaveChanges_ConfigSetsNumberOfStacksGreaterThan1_DeploysMultipleStacks()
        {
            // arrange
            var alarms = new List <Alarm>();

            for (int i = 0; i < 20; i++)
            {
                alarms.Add(Alarm($"Test alarm {i}"));
            }

            var group = Group(numberOfStacks: 2);

            var stackName1 = $"Watchman-{group.Name.ToLowerInvariant()}";
            var stackName2 = $"Watchman-{group.Name.ToLowerInvariant()}-1";

            SetupListStacksToReturnStackNames();

            _cloudFormationMock.Setup(x => x.DescribeStacksAsync(
                                          It.Is <DescribeStacksRequest>(s => s.StackName == stackName1),
                                          It.IsAny <CancellationToken>()))
            .ReturnsAsync(new DescribeStacksResponse
            {
                Stacks = new List <Stack>
                {
                    new Stack
                    {
                        StackStatus = "CREATE_COMPLETE",
                        StackName   = stackName1
                    }
                }
            });
            _cloudFormationMock.Setup(x => x.DescribeStacksAsync(
                                          It.Is <DescribeStacksRequest>(s => s.StackName == stackName2),
                                          It.IsAny <CancellationToken>()))
            .ReturnsAsync(new DescribeStacksResponse
            {
                Stacks = new List <Stack>
                {
                    new Stack
                    {
                        StackStatus = "CREATE_COMPLETE",
                        StackName   = stackName2
                    }
                }
            });


            var deployer = MakeDeployer(null,
                                        TimeSpan.FromMilliseconds(5),
                                        TimeSpan.FromMilliseconds(5));

            var sut = new CloudFormationAlarmCreator(deployer, new ConsoleAlarmLogger(false));

            sut.AddAlarms(group, alarms);

            // act
            await sut.SaveChanges(false);

            // assert
            _cloudFormationMock
            .Verify(x => x.CreateStackAsync(
                        It.Is <CreateStackRequest>(s => s.StackName == stackName1),
                        It.IsAny <CancellationToken>()));

            _cloudFormationMock
            .Verify(x => x.CreateStackAsync(
                        It.Is <CreateStackRequest>(s => s.StackName == stackName2),
                        It.IsAny <CancellationToken>()));
        }
Example #18
0
        public async Task CanSetExtendedStatisticAtResourceOrServiceLevel()
        {
            // arrange

            var fakeStackDeployer = new FakeStackDeployer();

            var elbClient = FakeAwsClients.CreateElbClientForLoadBalancers(new[]
            {
                new LoadBalancerDescription()
                {
                    LoadBalancerName = "elb-1"
                },
                new LoadBalancerDescription()
                {
                    LoadBalancerName = "elb-2"
                }
            });


            var creator = new CloudFormationAlarmCreator(fakeStackDeployer, new ConsoleAlarmLogger(true));

            var config = ConfigHelper.CreateBasicConfiguration(
                "test",
                "group-suffix",
                new AlertingGroupServices()
            {
                Elb = new AwsServiceAlarms <ResourceConfig>()
                {
                    Resources = new List <ResourceThresholds <ResourceConfig> >()
                    {
                        new ResourceThresholds <ResourceConfig>()
                        {
                            Name   = "elb-1",
                            Values = new Dictionary <string, AlarmValues>()
                            {
                                {
                                    "LatencyHigh", new AlarmValues(null, null, "p97")
                                }
                            }
                        },

                        new ResourceThresholds <ResourceConfig>()
                        {
                            Name = "elb-2"
                        }
                    },

                    // set default for whole group
                    Values = new Dictionary <string, AlarmValues>()
                    {
                        {
                            "LatencyHigh", new AlarmValues(null, null, "p99")
                        }
                    }
                }
            }
                );

            var sutBuilder = new Builder(ConfigHelper.ConfigLoaderFor(config), creator);

            sutBuilder.AddService(
                new ElbSource(elbClient),
                new ElbAlarmDataProvider(),
                new ElbAlarmDataProvider(),
                WatchmanServiceConfigurationMapper.MapElb
                );

            var sut = sutBuilder.Build();

            // act

            await sut.LoadAndGenerateAlarms(RunMode.GenerateAlarms);

            // assert

            var alarmsByElb = fakeStackDeployer
                              .Stack("Watchman-test")
                              .AlarmsByDimension("LoadBalancerName");

            // should take override
            var alarm = alarmsByElb["elb-1"].FirstOrDefault(a => a.Properties["AlarmName"].ToString().Contains("LatencyHigh"));

            Assert.That(alarm, Is.Not.Null);
            Assert.That(alarm.Properties["ExtendedStatistic"].ToString(), Is.EqualTo("p97"));

            // should take default of p99
            var alarm2 = alarmsByElb["elb-2"].FirstOrDefault(a => a.Properties["AlarmName"].ToString().Contains("LatencyHigh"));

            Assert.That(alarm2, Is.Not.Null);
            Assert.That(alarm2.Properties["ExtendedStatistic"].ToString(), Is.EqualTo("p99"));
        }