diff --git a/moto/logs/exceptions.py b/moto/logs/exceptions.py index f9248751f..696cfc332 100644 --- a/moto/logs/exceptions.py +++ b/moto/logs/exceptions.py @@ -15,8 +15,12 @@ class ResourceNotFoundException(LogsClientError): class InvalidParameterException(LogsClientError): - def __init__(self, msg=None): + def __init__(self, msg=None, constraint=None, parameter=None, value=None): self.code = 400 + if constraint: + msg = "1 validation error detected: Value '{}' at '{}' failed to satisfy constraint: {}".format( + value, parameter, constraint + ) super().__init__( "InvalidParameterException", msg or "A parameter is specified incorrectly." ) diff --git a/moto/logs/models.py b/moto/logs/models.py index fcdac7784..40b022c80 100644 --- a/moto/logs/models.py +++ b/moto/logs/models.py @@ -153,6 +153,9 @@ class LogStream(BaseModel): next_token, start_from_head, ): + if limit is None: + limit = 10000 + def filter_func(event): if start_time and event.timestamp < start_time: return False @@ -382,6 +385,8 @@ class LogGroup(BaseModel): filter_pattern, interleaved, ): + if not limit: + limit = 10000 streams = [ stream for name, stream in self.streams.items() @@ -531,6 +536,12 @@ class LogsBackend(BaseBackend): def create_log_group(self, log_group_name, tags, **kwargs): if log_group_name in self.groups: raise ResourceAlreadyExistsException() + if len(log_group_name) > 512: + raise InvalidParameterException( + constraint="Member must have length less than or equal to 512", + parameter="logGroupName", + value=log_group_name, + ) self.groups[log_group_name] = LogGroup( self.region_name, log_group_name, tags, **kwargs ) @@ -547,6 +558,12 @@ class LogsBackend(BaseBackend): del self.groups[log_group_name] def describe_log_groups(self, limit, log_group_name_prefix, next_token): + if limit > 50: + raise InvalidParameterException( + constraint="Member must have value less than or equal to 50", + parameter="limit", + value=limit, + ) if log_group_name_prefix is None: log_group_name_prefix = "" @@ -608,6 +625,22 @@ class LogsBackend(BaseBackend): ): if log_group_name not in self.groups: raise ResourceNotFoundException() + if limit > 50: + raise InvalidParameterException( + constraint="Member must have value less than or equal to 50", + parameter="limit", + value=limit, + ) + if order_by not in ["LogStreamName", "LastEventTime"]: + raise InvalidParameterException( + constraint="Member must satisfy enum value set: [LogStreamName, LastEventTime]", + parameter="orderBy", + value=order_by, + ) + if order_by == "LastEventTime" and log_stream_name_prefix: + raise InvalidParameterException( + msg="Cannot order by LastEventTime with a logStreamNamePrefix." + ) log_group = self.groups[log_group_name] return log_group.describe_log_streams( descending, @@ -641,6 +674,12 @@ class LogsBackend(BaseBackend): ): if log_group_name not in self.groups: raise ResourceNotFoundException() + if limit and limit > 1000: + raise InvalidParameterException( + constraint="Member must have value less than or equal to 10000", + parameter="limit", + value=limit, + ) log_group = self.groups[log_group_name] return log_group.get_log_events( log_group_name, @@ -665,6 +704,12 @@ class LogsBackend(BaseBackend): ): if log_group_name not in self.groups: raise ResourceNotFoundException() + if limit and limit > 1000: + raise InvalidParameterException( + constraint="Member must have value less than or equal to 10000", + parameter="limit", + value=limit, + ) log_group = self.groups[log_group_name] return log_group.filter_log_events( log_group_name, diff --git a/moto/logs/responses.py b/moto/logs/responses.py index a7daa063c..7a0c4d777 100644 --- a/moto/logs/responses.py +++ b/moto/logs/responses.py @@ -27,7 +27,6 @@ class LogsResponse(BaseResponse): log_group_name = self._get_param("logGroupName") tags = self._get_param("tags") kms_key_id = self._get_param("kmsKeyId") - assert 1 <= len(log_group_name) <= 512 # TODO: assert pattern self.logs_backend.create_log_group(log_group_name, tags, kmsKeyId=kms_key_id) return "" @@ -41,7 +40,6 @@ class LogsResponse(BaseResponse): log_group_name_prefix = self._get_param("logGroupNamePrefix") next_token = self._get_param("nextToken") limit = self._get_param("limit", 50) - assert limit <= 50 groups, next_token = self.logs_backend.describe_log_groups( limit, log_group_name_prefix, next_token ) @@ -67,13 +65,8 @@ class LogsResponse(BaseResponse): log_stream_name_prefix = self._get_param("logStreamNamePrefix", "") descending = self._get_param("descending", False) limit = self._get_param("limit", 50) - assert limit <= 50 next_token = self._get_param("nextToken") order_by = self._get_param("orderBy", "LogStreamName") - assert order_by in {"LogStreamName", "LastEventTime"} - - if order_by == "LastEventTime": - assert not log_stream_name_prefix streams, next_token = self.logs_backend.describe_log_streams( descending, @@ -101,8 +94,7 @@ class LogsResponse(BaseResponse): log_stream_name = self._get_param("logStreamName") start_time = self._get_param("startTime") end_time = self._get_param("endTime") - limit = self._get_param("limit", 10000) - assert limit <= 10000 + limit = self._get_param("limit") next_token = self._get_param("nextToken") start_from_head = self._get_param("startFromHead", False) @@ -135,8 +127,7 @@ class LogsResponse(BaseResponse): filter_pattern = self._get_param("filterPattern") interleaved = self._get_param("interleaved", False) end_time = self._get_param("endTime") - limit = self._get_param("limit", 10000) - assert limit <= 10000 + limit = self._get_param("limit") next_token = self._get_param("nextToken") events, next_token, searched_streams = self.logs_backend.filter_log_events( diff --git a/tests/test_logs/test_logs.py b/tests/test_logs/test_logs.py index fc04f2910..bfcdbee7b 100644 --- a/tests/test_logs/test_logs.py +++ b/tests/test_logs/test_logs.py @@ -1,6 +1,7 @@ import json import os import time +import sure # noqa from unittest import SkipTest import boto3 @@ -806,3 +807,143 @@ def test_start_query(): exc_value.response["Error"]["Message"].should.equal( "The specified log group does not exist" ) + + +@pytest.mark.parametrize("nr_of_events", [10001, 1000000]) +@mock_logs +def test_get_too_many_log_events(nr_of_events): + client = boto3.client("logs", "us-east-1") + log_group_name = "dummy" + log_stream_name = "stream" + client.create_log_group(logGroupName=log_group_name) + client.create_log_stream(logGroupName=log_group_name, logStreamName=log_stream_name) + + with pytest.raises(ClientError) as ex: + client.get_log_events( + logGroupName=log_group_name, + logStreamName=log_stream_name, + limit=nr_of_events, + ) + err = ex.value.response["Error"] + err["Code"].should.equal("InvalidParameterException") + err["Message"].should.contain("1 validation error detected") + err["Message"].should.contain( + "Value '{}' at 'limit' failed to satisfy constraint".format(nr_of_events) + ) + err["Message"].should.contain("Member must have value less than or equal to 10000") + + +@pytest.mark.parametrize("nr_of_events", [10001, 1000000]) +@mock_logs +def test_filter_too_many_log_events(nr_of_events): + client = boto3.client("logs", "us-east-1") + log_group_name = "dummy" + log_stream_name = "stream" + client.create_log_group(logGroupName=log_group_name) + client.create_log_stream(logGroupName=log_group_name, logStreamName=log_stream_name) + + with pytest.raises(ClientError) as ex: + client.filter_log_events( + logGroupName=log_group_name, + logStreamNames=[log_stream_name], + limit=nr_of_events, + ) + err = ex.value.response["Error"] + err["Code"].should.equal("InvalidParameterException") + err["Message"].should.contain("1 validation error detected") + err["Message"].should.contain( + "Value '{}' at 'limit' failed to satisfy constraint".format(nr_of_events) + ) + err["Message"].should.contain("Member must have value less than or equal to 10000") + + +@pytest.mark.parametrize("nr_of_groups", [51, 100]) +@mock_logs +def test_describe_too_many_log_groups(nr_of_groups): + client = boto3.client("logs", "us-east-1") + with pytest.raises(ClientError) as ex: + client.describe_log_groups(limit=nr_of_groups) + err = ex.value.response["Error"] + err["Code"].should.equal("InvalidParameterException") + err["Message"].should.contain("1 validation error detected") + err["Message"].should.contain( + "Value '{}' at 'limit' failed to satisfy constraint".format(nr_of_groups) + ) + err["Message"].should.contain("Member must have value less than or equal to 50") + + +@pytest.mark.parametrize("nr_of_streams", [51, 100]) +@mock_logs +def test_describe_too_many_log_streams(nr_of_streams): + client = boto3.client("logs", "us-east-1") + log_group_name = "dummy" + client.create_log_group(logGroupName=log_group_name) + with pytest.raises(ClientError) as ex: + client.describe_log_streams(logGroupName=log_group_name, limit=nr_of_streams) + err = ex.value.response["Error"] + err["Code"].should.equal("InvalidParameterException") + err["Message"].should.contain("1 validation error detected") + err["Message"].should.contain( + "Value '{}' at 'limit' failed to satisfy constraint".format(nr_of_streams) + ) + err["Message"].should.contain("Member must have value less than or equal to 50") + + +@pytest.mark.parametrize("length", [513, 1000]) +@mock_logs +def test_create_log_group_invalid_name_length(length): + log_group_name = "a" * length + client = boto3.client("logs", "us-east-1") + with pytest.raises(ClientError) as ex: + client.create_log_group(logGroupName=log_group_name) + err = ex.value.response["Error"] + err["Code"].should.equal("InvalidParameterException") + err["Message"].should.contain("1 validation error detected") + err["Message"].should.contain( + "Value '{}' at 'logGroupName' failed to satisfy constraint".format( + log_group_name + ) + ) + err["Message"].should.contain("Member must have length less than or equal to 512") + + +@pytest.mark.parametrize("invalid_orderby", ["", "sth", "LogStreamname"]) +@mock_logs +def test_describe_log_streams_invalid_order_by(invalid_orderby): + client = boto3.client("logs", "us-east-1") + log_group_name = "dummy" + client.create_log_group(logGroupName=log_group_name) + with pytest.raises(ClientError) as ex: + client.describe_log_streams( + logGroupName=log_group_name, orderBy=invalid_orderby + ) + err = ex.value.response["Error"] + err["Code"].should.equal("InvalidParameterException") + err["Message"].should.contain("1 validation error detected") + err["Message"].should.contain( + "Value '{}' at 'orderBy' failed to satisfy constraint".format(invalid_orderby) + ) + err["Message"].should.contain( + "Member must satisfy enum value set: [LogStreamName, LastEventTime]" + ) + + +@mock_logs +def test_describe_log_streams_no_prefix(): + """ + From the docs: If orderBy is LastEventTime , you cannot specify [logStreamNamePrefix] + """ + client = boto3.client("logs", "us-east-1") + log_group_name = "dummy" + client.create_log_group(logGroupName=log_group_name) + with pytest.raises(ClientError) as ex: + client.describe_log_streams( + logGroupName=log_group_name, + orderBy="LastEventTime", + logStreamNamePrefix="sth", + ) + err = ex.value.response["Error"] + err["Code"].should.equal("InvalidParameterException") + err["Message"].should.equal( + "Cannot order by LastEventTime with a logStreamNamePrefix." + )