Spaces:
Running
Running
| import io | |
| import json | |
| import os | |
| import time | |
| import zipfile | |
| from urllib.parse import urlparse | |
| import pytest | |
| from botocore.exceptions import ClientError | |
| import uuid as _uuid_mod | |
| def test_cloudwatch_metrics(cw): | |
| cw.put_metric_data( | |
| Namespace="MyApp", | |
| MetricData=[ | |
| {"MetricName": "RequestCount", "Value": 42.0, "Unit": "Count"}, | |
| {"MetricName": "Latency", "Value": 123.5, "Unit": "Milliseconds"}, | |
| ], | |
| ) | |
| resp = cw.list_metrics(Namespace="MyApp") | |
| names = [m["MetricName"] for m in resp["Metrics"]] | |
| assert "RequestCount" in names | |
| assert "Latency" in names | |
| def test_cloudwatch_alarm(cw): | |
| cw.put_metric_alarm( | |
| AlarmName="high-latency", | |
| MetricName="Latency", | |
| Namespace="MyApp", | |
| Statistic="Average", | |
| Period=60, | |
| EvaluationPeriods=1, | |
| Threshold=500.0, | |
| ComparisonOperator="GreaterThanThreshold", | |
| ) | |
| resp = cw.describe_alarms(AlarmNames=["high-latency"]) | |
| assert len(resp["MetricAlarms"]) == 1 | |
| def test_cloudwatch_logs_metric_filter(logs): | |
| logs.create_log_group(logGroupName="/test/mf") | |
| logs.put_metric_filter( | |
| logGroupName="/test/mf", | |
| filterName="err-count", | |
| filterPattern="ERROR", | |
| metricTransformations=[{"metricName": "ErrorCount", "metricNamespace": "Test", "metricValue": "1"}], | |
| ) | |
| resp = logs.describe_metric_filters(logGroupName="/test/mf") | |
| assert len(resp["metricFilters"]) == 1 | |
| assert resp["metricFilters"][0]["filterName"] == "err-count" | |
| logs.delete_metric_filter(logGroupName="/test/mf", filterName="err-count") | |
| resp2 = logs.describe_metric_filters(logGroupName="/test/mf") | |
| assert len(resp2["metricFilters"]) == 0 | |
| def test_cloudwatch_logs_insights_stub(logs): | |
| logs.create_log_group(logGroupName="/test/insights") | |
| resp = logs.start_query( | |
| logGroupName="/test/insights", | |
| startTime=0, | |
| endTime=9999999999, | |
| queryString="fields @timestamp | limit 10", | |
| ) | |
| query_id = resp["queryId"] | |
| assert query_id | |
| results = logs.get_query_results(queryId=query_id) | |
| assert results["status"] in ("Complete", "Running") | |
| def test_cloudwatch_dashboard(cw): | |
| body = json.dumps({"widgets": [{"type": "text", "properties": {"markdown": "Hello"}}]}) | |
| cw.put_dashboard(DashboardName="test-dash", DashboardBody=body) | |
| resp = cw.get_dashboard(DashboardName="test-dash") | |
| assert resp["DashboardName"] == "test-dash" | |
| assert "DashboardBody" in resp | |
| listed = cw.list_dashboards() | |
| assert any(d["DashboardName"] == "test-dash" for d in listed["DashboardEntries"]) | |
| cw.delete_dashboards(DashboardNames=["test-dash"]) | |
| # Migrated from test_cw.py | |
| def test_cloudwatch_put_list_metrics_v2(cw): | |
| cw.put_metric_data( | |
| Namespace="CWv2", | |
| MetricData=[ | |
| { | |
| "MetricName": "Reqs", | |
| "Value": 100.0, | |
| "Unit": "Count", | |
| "Dimensions": [{"Name": "API", "Value": "/users"}], | |
| }, | |
| {"MetricName": "Errs", "Value": 5.0, "Unit": "Count"}, | |
| ], | |
| ) | |
| resp = cw.list_metrics(Namespace="CWv2") | |
| names = [m["MetricName"] for m in resp["Metrics"]] | |
| assert "Reqs" in names | |
| assert "Errs" in names | |
| resp_filtered = cw.list_metrics(Namespace="CWv2", MetricName="Reqs") | |
| assert all(m["MetricName"] == "Reqs" for m in resp_filtered["Metrics"]) | |
| def test_cloudwatch_get_metric_statistics_v2(cw): | |
| cw.put_metric_data( | |
| Namespace="CWStat2", | |
| MetricData=[ | |
| {"MetricName": "Duration", "Value": 100.0, "Unit": "Milliseconds"}, | |
| {"MetricName": "Duration", "Value": 200.0, "Unit": "Milliseconds"}, | |
| ], | |
| ) | |
| resp = cw.get_metric_statistics( | |
| Namespace="CWStat2", | |
| MetricName="Duration", | |
| Period=60, | |
| StartTime=time.time() - 600, | |
| EndTime=time.time() + 600, | |
| Statistics=["Average", "Sum", "SampleCount", "Minimum", "Maximum"], | |
| ) | |
| assert len(resp["Datapoints"]) >= 1 | |
| dp = resp["Datapoints"][0] | |
| assert "Average" in dp | |
| assert "Sum" in dp | |
| assert "SampleCount" in dp | |
| assert "Minimum" in dp | |
| assert "Maximum" in dp | |
| def test_cloudwatch_put_metric_alarm_v2(cw): | |
| cw.put_metric_alarm( | |
| AlarmName="cw-v2-high-err", | |
| MetricName="Errors", | |
| Namespace="CWv2Alarms", | |
| Statistic="Sum", | |
| Period=300, | |
| EvaluationPeriods=2, | |
| Threshold=10.0, | |
| ComparisonOperator="GreaterThanOrEqualToThreshold", | |
| AlarmActions=["arn:aws:sns:us-east-1:000000000000:alarm-topic"], | |
| AlarmDescription="Fires when errors >= 10", | |
| ) | |
| resp = cw.describe_alarms(AlarmNames=["cw-v2-high-err"]) | |
| alarm = resp["MetricAlarms"][0] | |
| assert alarm["AlarmName"] == "cw-v2-high-err" | |
| assert alarm["Threshold"] == 10.0 | |
| assert alarm["ComparisonOperator"] == "GreaterThanOrEqualToThreshold" | |
| assert alarm["EvaluationPeriods"] == 2 | |
| def test_cloudwatch_describe_alarms_v2(cw): | |
| for i in range(3): | |
| cw.put_metric_alarm( | |
| AlarmName=f"cw-da-v2-{i}", | |
| MetricName="M", | |
| Namespace="N", | |
| Statistic="Sum", | |
| Period=60, | |
| EvaluationPeriods=1, | |
| Threshold=float(i), | |
| ComparisonOperator="GreaterThanThreshold", | |
| ) | |
| resp = cw.describe_alarms(AlarmNamePrefix="cw-da-v2-") | |
| names = [a["AlarmName"] for a in resp["MetricAlarms"]] | |
| for i in range(3): | |
| assert f"cw-da-v2-{i}" in names | |
| def test_cloudwatch_delete_alarms_v2(cw): | |
| cw.put_metric_alarm( | |
| AlarmName="cw-del-v2", | |
| MetricName="M", | |
| Namespace="N", | |
| Statistic="Sum", | |
| Period=60, | |
| EvaluationPeriods=1, | |
| Threshold=1.0, | |
| ComparisonOperator="GreaterThanThreshold", | |
| ) | |
| cw.delete_alarms(AlarmNames=["cw-del-v2"]) | |
| resp = cw.describe_alarms(AlarmNames=["cw-del-v2"]) | |
| assert len(resp["MetricAlarms"]) == 0 | |
| def test_cloudwatch_set_alarm_state_v2(cw): | |
| cw.put_metric_alarm( | |
| AlarmName="cw-state-v2", | |
| MetricName="M", | |
| Namespace="N", | |
| Statistic="Sum", | |
| Period=60, | |
| EvaluationPeriods=1, | |
| Threshold=1.0, | |
| ComparisonOperator="GreaterThanThreshold", | |
| ) | |
| initial = cw.describe_alarms(AlarmNames=["cw-state-v2"])["MetricAlarms"][0] | |
| assert initial["StateValue"] == "INSUFFICIENT_DATA" | |
| cw.set_alarm_state( | |
| AlarmName="cw-state-v2", | |
| StateValue="ALARM", | |
| StateReason="Manual trigger for testing", | |
| ) | |
| after = cw.describe_alarms(AlarmNames=["cw-state-v2"])["MetricAlarms"][0] | |
| assert after["StateValue"] == "ALARM" | |
| assert after["StateReason"] == "Manual trigger for testing" | |
| def test_cloudwatch_get_metric_data_v2(cw): | |
| cw.put_metric_data( | |
| Namespace="CWData2", | |
| MetricData=[{"MetricName": "Hits", "Value": 42.0, "Unit": "Count"}], | |
| ) | |
| resp = cw.get_metric_data( | |
| MetricDataQueries=[ | |
| { | |
| "Id": "q1", | |
| "MetricStat": { | |
| "Metric": {"Namespace": "CWData2", "MetricName": "Hits"}, | |
| "Period": 60, | |
| "Stat": "Sum", | |
| }, | |
| "ReturnData": True, | |
| } | |
| ], | |
| StartTime=time.time() - 600, | |
| EndTime=time.time() + 600, | |
| ) | |
| assert len(resp["MetricDataResults"]) == 1 | |
| assert resp["MetricDataResults"][0]["Id"] == "q1" | |
| assert resp["MetricDataResults"][0]["StatusCode"] == "Complete" | |
| assert len(resp["MetricDataResults"][0]["Values"]) >= 1 | |
| def test_cloudwatch_tags_v2(cw): | |
| cw.put_metric_alarm( | |
| AlarmName="cw-tag-v2", | |
| MetricName="M", | |
| Namespace="N", | |
| Statistic="Sum", | |
| Period=60, | |
| EvaluationPeriods=1, | |
| Threshold=1.0, | |
| ComparisonOperator="GreaterThanThreshold", | |
| ) | |
| arn = cw.describe_alarms(AlarmNames=["cw-tag-v2"])["MetricAlarms"][0]["AlarmArn"] | |
| cw.tag_resource( | |
| ResourceARN=arn, | |
| Tags=[ | |
| {"Key": "env", "Value": "prod"}, | |
| {"Key": "team", "Value": "sre"}, | |
| ], | |
| ) | |
| resp = cw.list_tags_for_resource(ResourceARN=arn) | |
| tag_map = {t["Key"]: t["Value"] for t in resp["Tags"]} | |
| assert tag_map["env"] == "prod" | |
| assert tag_map["team"] == "sre" | |
| cw.untag_resource(ResourceARN=arn, TagKeys=["env"]) | |
| resp2 = cw.list_tags_for_resource(ResourceARN=arn) | |
| assert not any(t["Key"] == "env" for t in resp2["Tags"]) | |
| assert any(t["Key"] == "team" for t in resp2["Tags"]) | |
| def test_cloudwatch_composite_alarm(cw): | |
| import uuid as _uuid | |
| child = f"intg-child-alarm-{_uuid.uuid4().hex[:8]}" | |
| composite = f"intg-comp-alarm-{_uuid.uuid4().hex[:8]}" | |
| cw.put_metric_alarm( | |
| AlarmName=child, | |
| ComparisonOperator="GreaterThanThreshold", | |
| EvaluationPeriods=1, | |
| MetricName="CPUUtilization", | |
| Namespace="AWS/EC2", | |
| Period=60, | |
| Statistic="Average", | |
| Threshold=80.0, | |
| ) | |
| child_arn = cw.describe_alarms(AlarmNames=[child])["MetricAlarms"][0]["AlarmArn"] | |
| cw.put_composite_alarm( | |
| AlarmName=composite, | |
| AlarmRule=f"ALARM({child_arn})", | |
| AlarmDescription="composite test", | |
| ) | |
| resp = cw.describe_alarms(AlarmNames=[composite], AlarmTypes=["CompositeAlarm"]) | |
| assert any(a["AlarmName"] == composite for a in resp.get("CompositeAlarms", [])) | |
| cw.delete_alarms(AlarmNames=[child, composite]) | |
| def test_cloudwatch_describe_alarms_for_metric(cw): | |
| import uuid as _uuid | |
| alarm_name = f"intg-afm-{_uuid.uuid4().hex[:8]}" | |
| cw.put_metric_alarm( | |
| AlarmName=alarm_name, | |
| ComparisonOperator="GreaterThanThreshold", | |
| EvaluationPeriods=1, | |
| MetricName="NetworkIn", | |
| Namespace="AWS/EC2", | |
| Period=60, | |
| Statistic="Sum", | |
| Threshold=1000.0, | |
| ) | |
| resp = cw.describe_alarms_for_metric( | |
| MetricName="NetworkIn", | |
| Namespace="AWS/EC2", | |
| ) | |
| assert any(a["AlarmName"] == alarm_name for a in resp.get("MetricAlarms", [])) | |
| cw.delete_alarms(AlarmNames=[alarm_name]) | |
| def test_cloudwatch_describe_alarm_history(cw): | |
| import uuid as _uuid | |
| alarm_name = f"intg-hist-{_uuid.uuid4().hex[:8]}" | |
| cw.put_metric_alarm( | |
| AlarmName=alarm_name, | |
| ComparisonOperator="GreaterThanThreshold", | |
| EvaluationPeriods=1, | |
| MetricName="DiskReadOps", | |
| Namespace="AWS/EC2", | |
| Period=60, | |
| Statistic="Average", | |
| Threshold=50.0, | |
| ) | |
| cw.set_alarm_state(AlarmName=alarm_name, StateValue="ALARM", StateReason="test") | |
| resp = cw.describe_alarm_history(AlarmName=alarm_name) | |
| assert "AlarmHistoryItems" in resp | |
| cw.delete_alarms(AlarmNames=[alarm_name]) | |
| def test_cloudwatch_get_metric_data_time_range(cw): | |
| """GetMetricData respects StartTime/EndTime filtering.""" | |
| import datetime | |
| now = datetime.datetime.utcnow() | |
| past = now - datetime.timedelta(hours=2) | |
| cw.put_metric_data( | |
| Namespace="qa/cw", | |
| MetricData=[{"MetricName": "Requests", "Value": 100.0, "Unit": "Count"}], | |
| ) | |
| resp = cw.get_metric_data( | |
| MetricDataQueries=[ | |
| { | |
| "Id": "m1", | |
| "MetricStat": { | |
| "Metric": {"Namespace": "qa/cw", "MetricName": "Requests"}, | |
| "Period": 60, | |
| "Stat": "Sum", | |
| }, | |
| } | |
| ], | |
| StartTime=past, | |
| EndTime=now + datetime.timedelta(minutes=5), | |
| ) | |
| result = next((r for r in resp["MetricDataResults"] if r["Id"] == "m1"), None) | |
| assert result is not None | |
| assert result["StatusCode"] == "Complete" | |
| assert len(result["Values"]) >= 1 | |
| assert sum(result["Values"]) >= 100.0 | |
| def test_cloudwatch_alarm_state_transitions(cw): | |
| """SetAlarmState changes alarm state correctly.""" | |
| cw.put_metric_alarm( | |
| AlarmName="qa-cw-state-alarm", | |
| MetricName="Errors", | |
| Namespace="qa/cw", | |
| Statistic="Sum", | |
| Period=60, | |
| EvaluationPeriods=1, | |
| Threshold=10.0, | |
| ComparisonOperator="GreaterThanThreshold", | |
| ) | |
| cw.set_alarm_state(AlarmName="qa-cw-state-alarm", StateValue="ALARM", StateReason="Testing") | |
| alarms = cw.describe_alarms(AlarmNames=["qa-cw-state-alarm"])["MetricAlarms"] | |
| assert alarms[0]["StateValue"] == "ALARM" | |
| cw.set_alarm_state(AlarmName="qa-cw-state-alarm", StateValue="OK", StateReason="Resolved") | |
| alarms2 = cw.describe_alarms(AlarmNames=["qa-cw-state-alarm"])["MetricAlarms"] | |
| assert alarms2[0]["StateValue"] == "OK" | |
| def test_cloudwatch_list_metrics_namespace_filter(cw): | |
| """ListMetrics with Namespace filter returns only matching metrics.""" | |
| cw.put_metric_data(Namespace="qa/ns-a", MetricData=[{"MetricName": "MetA", "Value": 1.0}]) | |
| cw.put_metric_data(Namespace="qa/ns-b", MetricData=[{"MetricName": "MetB", "Value": 1.0}]) | |
| resp = cw.list_metrics(Namespace="qa/ns-a") | |
| names = [m["MetricName"] for m in resp["Metrics"]] | |
| assert "MetA" in names | |
| assert "MetB" not in names | |
| def test_cloudwatch_put_metric_data_statistics_values(cw): | |
| """PutMetricData with Values/Counts array stores multiple data points.""" | |
| cw.put_metric_data( | |
| Namespace="qa/cw-multi", | |
| MetricData=[ | |
| { | |
| "MetricName": "Latency", | |
| "Values": [10.0, 20.0, 30.0], | |
| "Counts": [1.0, 2.0, 1.0], | |
| "Unit": "Milliseconds", | |
| } | |
| ], | |
| ) | |
| resp = cw.list_metrics(Namespace="qa/cw-multi") | |
| assert any(m["MetricName"] == "Latency" for m in resp["Metrics"]) | |
| def test_cloudwatch_enable_alarm_actions(cw): | |
| cw.put_metric_alarm( | |
| AlarmName="heimdall-enable-actions", | |
| MetricName="M", | |
| Namespace="N", | |
| Statistic="Sum", | |
| Period=60, | |
| EvaluationPeriods=1, | |
| Threshold=1.0, | |
| ComparisonOperator="GreaterThanThreshold", | |
| ActionsEnabled=False, | |
| ) | |
| alarm = cw.describe_alarms(AlarmNames=["heimdall-enable-actions"])["MetricAlarms"][0] | |
| assert alarm["ActionsEnabled"] is False | |
| cw.enable_alarm_actions(AlarmNames=["heimdall-enable-actions"]) | |
| alarm = cw.describe_alarms(AlarmNames=["heimdall-enable-actions"])["MetricAlarms"][0] | |
| assert alarm["ActionsEnabled"] is True | |
| cw.delete_alarms(AlarmNames=["heimdall-enable-actions"]) | |
| def test_cloudwatch_disable_alarm_actions(cw): | |
| cw.put_metric_alarm( | |
| AlarmName="heimdall-disable-actions", | |
| MetricName="M", | |
| Namespace="N", | |
| Statistic="Sum", | |
| Period=60, | |
| EvaluationPeriods=1, | |
| Threshold=1.0, | |
| ComparisonOperator="GreaterThanThreshold", | |
| ActionsEnabled=True, | |
| ) | |
| alarm = cw.describe_alarms(AlarmNames=["heimdall-disable-actions"])["MetricAlarms"][0] | |
| assert alarm["ActionsEnabled"] is True | |
| cw.disable_alarm_actions(AlarmNames=["heimdall-disable-actions"]) | |
| alarm = cw.describe_alarms(AlarmNames=["heimdall-disable-actions"])["MetricAlarms"][0] | |
| assert alarm["ActionsEnabled"] is False | |
| cw.delete_alarms(AlarmNames=["heimdall-disable-actions"]) | |