Introduce Github Actions to replace TravisCI (#3610)
This commit is contained in:
parent
5a41866f71
commit
8591eda9d6
@ -9,3 +9,4 @@ exclude_lines =
|
||||
[run]
|
||||
include = moto/*
|
||||
omit = moto/packages/*
|
||||
source = moto
|
||||
|
192
.github/workflows/build.yml
vendored
192
.github/workflows/build.yml
vendored
@ -1,7 +1,191 @@
|
||||
name: Example Action
|
||||
on: [push]
|
||||
name: TestNDeploy
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
job1:
|
||||
# Install and cache dependencies
|
||||
cache:
|
||||
name: Caching
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: [ 2.7, 3.6, 3.7, 3.8 ]
|
||||
|
||||
steps:
|
||||
- run: echo "Test"
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Get pip cache dir
|
||||
id: pip-cache-dir
|
||||
run: |
|
||||
echo "::set-output name=dir::$(pip cache dir)"
|
||||
- name: pip cache
|
||||
id: pip-cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ steps.pip-cache-dir.outputs.dir }}
|
||||
key: pip-${{ matrix.python-version }}-${{ hashFiles('**/requirements-dev.txt') }}
|
||||
- name: Install Linux dependencies
|
||||
if: ${{ matrix.python-version == 3.8 && steps.pip-cache.outputs.cache-hit != 'true' }}
|
||||
run: |
|
||||
sudo apt-get install libxslt-dev libxml2-dev -y
|
||||
- name: Install project dependencies
|
||||
if: ${{ steps.pip-cache.outputs.cache-hit != 'true' }}
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install -r requirements-dev.txt
|
||||
|
||||
lint:
|
||||
name: Linting
|
||||
runs-on: ubuntu-latest
|
||||
needs: cache
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: [3.7]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
# Retrieve the previously cached dependencies
|
||||
- name: Get pip cache dir
|
||||
id: pip-cache
|
||||
run: |
|
||||
echo "::set-output name=dir::$(pip cache dir)"
|
||||
- name: pip cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ steps.pip-cache.outputs.dir }}
|
||||
key: pip-${{ matrix.python-version }}-${{ hashFiles('**/requirements-dev.txt') }}
|
||||
# Still need to properly install the dependencies - it will only skip the download part
|
||||
- name: Install project dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install -r requirements-dev.txt
|
||||
- name: Lint with flake8
|
||||
run:
|
||||
make lint
|
||||
|
||||
test:
|
||||
name: Unit test
|
||||
runs-on: ubuntu-latest
|
||||
needs: lint
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
python-version: [2.7, 3.6, 3.7, 3.8]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Get pip cache dir
|
||||
id: pip-cache
|
||||
run: |
|
||||
echo "::set-output name=dir::$(pip cache dir)"
|
||||
- name: pip cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ steps.pip-cache.outputs.dir }}
|
||||
key: pip-${{ matrix.python-version }}-${{ hashFiles('**/requirements-dev.txt') }}
|
||||
- name: Install Linux dependencies
|
||||
if: ${{ matrix.python-version == 3.8 }}
|
||||
run: |
|
||||
sudo apt-get install libxslt-dev libxml2-dev -y
|
||||
- name: Install project dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install -r requirements-dev.txt
|
||||
pip install pytest-cov
|
||||
- name: Test with pytest
|
||||
if: ${{ matrix.python-version != 3.7 }}
|
||||
run: |
|
||||
make test-only
|
||||
# Only need to configure coverage once
|
||||
# Pytest-cov explicitly fails in Py2 for XRay tests
|
||||
- name: Test with pytest/coverage
|
||||
if: ${{ matrix.python-version == 3.7 }}
|
||||
run: |
|
||||
make test-coverage
|
||||
- name: "Upload coverage to Codecov"
|
||||
if: ${{ matrix.python-version == 3.7 }}
|
||||
uses: codecov/codecov-action@v1
|
||||
with:
|
||||
fail_ci_if_error: true
|
||||
|
||||
testserver:
|
||||
name: Unit tests in Server Mode
|
||||
runs-on: ubuntu-latest
|
||||
needs: lint
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: [2.7, 3.6, 3.7, 3.8]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Start MotoServer
|
||||
run: |
|
||||
python setup.py sdist
|
||||
docker run --rm -t --name motoserver -e TEST_SERVER_MODE=true -e AWS_SECRET_ACCESS_KEY=server_secret -e AWS_ACCESS_KEY_ID=server_key -v `pwd`:/moto -p 5000:5000 -v /var/run/docker.sock:/var/run/docker.sock python:3.7-buster /moto/travis_moto_server.sh &
|
||||
python wait_for.py
|
||||
- name: Get pip cache dir
|
||||
id: pip-cache
|
||||
run: |
|
||||
echo "::set-output name=dir::$(pip cache dir)"
|
||||
- name: pip cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ steps.pip-cache.outputs.dir }}
|
||||
key: pip-${{ matrix.python-version }}-${{ hashFiles('**/requirements-dev.txt') }}
|
||||
- name: Install Linux dependencies
|
||||
if: ${{ matrix.python-version == 3.8 }}
|
||||
run: |
|
||||
sudo apt-get install libxslt-dev libxml2-dev -y
|
||||
- name: Install project dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install -r requirements-dev.txt
|
||||
- name: Test ServerMode
|
||||
env:
|
||||
TEST_SERVER_MODE: ${{ true }}
|
||||
run: |
|
||||
make test-only
|
||||
|
||||
deploy:
|
||||
name: Deploy
|
||||
runs-on: ubuntu-latest
|
||||
needs: [test, testserver]
|
||||
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: [3.8]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Update project version
|
||||
run: |
|
||||
git fetch --unshallow
|
||||
python update_version_from_git.py
|
||||
- name: Build project
|
||||
run: |
|
||||
pip install wheel
|
||||
python setup.py sdist bdist_wheel
|
||||
- name: Publish to PyPI
|
||||
uses: pypa/gh-action-pypi-publish@master
|
||||
with:
|
||||
password: ${{ secrets.PYPI_API_TOKEN }}
|
||||
|
||||
|
68
.travis.yml
68
.travis.yml
@ -1,68 +0,0 @@
|
||||
dist: focal
|
||||
language: python
|
||||
services:
|
||||
- docker
|
||||
python:
|
||||
- 2.7
|
||||
- 3.6
|
||||
- 3.7
|
||||
- 3.8
|
||||
env:
|
||||
- TEST_SERVER_MODE=false
|
||||
- TEST_SERVER_MODE=true
|
||||
before_install:
|
||||
- export BOTO_CONFIG=/dev/null
|
||||
install:
|
||||
- |
|
||||
python setup.py sdist
|
||||
|
||||
if [ "$TEST_SERVER_MODE" = "true" ]; then
|
||||
if [ "$TRAVIS_PYTHON_VERSION" = "3.8" ]; then
|
||||
# Python 3.8 does not provide Stretch images yet [1]
|
||||
# [1] https://github.com/docker-library/python/issues/428
|
||||
PYTHON_DOCKER_TAG=${TRAVIS_PYTHON_VERSION}-buster
|
||||
else
|
||||
PYTHON_DOCKER_TAG=${TRAVIS_PYTHON_VERSION}-stretch
|
||||
fi
|
||||
docker run --rm -t --name motoserver -e TEST_SERVER_MODE=true -e AWS_SECRET_ACCESS_KEY=server_secret -e AWS_ACCESS_KEY_ID=server_key -v `pwd`:/moto -p 5000:5000 -v /var/run/docker.sock:/var/run/docker.sock python:${PYTHON_DOCKER_TAG} /moto/travis_moto_server.sh &
|
||||
fi
|
||||
travis_retry pip install -r requirements-dev.txt
|
||||
travis_retry pip install docker>=2.5.1
|
||||
travis_retry pip install boto==2.45.0
|
||||
travis_retry pip install boto3
|
||||
travis_retry pip install dist/moto*.gz
|
||||
travis_retry pip install coveralls==1.1
|
||||
travis_retry pip install coverage==4.5.4
|
||||
|
||||
if [ "$TEST_SERVER_MODE" = "true" ]; then
|
||||
python wait_for.py
|
||||
fi
|
||||
before_script:
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == "3.7" ]]; then make lint; fi
|
||||
script:
|
||||
- make test-only
|
||||
after_success:
|
||||
- coveralls
|
||||
before_deploy:
|
||||
- git checkout $TRAVIS_BRANCH
|
||||
- git fetch --unshallow
|
||||
- python update_version_from_git.py
|
||||
deploy:
|
||||
- provider: pypi
|
||||
distributions: sdist bdist_wheel
|
||||
user: spulec
|
||||
password:
|
||||
secure: NxnPylnTfekJmGyoufCw0lMoYRskSMJzvAIyAlJJVYKwEhmiCPOrdy5qV8i8mRZ1AkUsqU3jBZ/PD56n96clHW0E3d080UleRDj6JpyALVdeLfMqZl9kLmZ8bqakWzYq3VSJKw2zGP/L4tPGf8wTK1SUv9yl/YNDsBdCkjDverw=
|
||||
on:
|
||||
branch:
|
||||
- master
|
||||
skip_cleanup: true
|
||||
skip_existing: true
|
||||
# - provider: pypi
|
||||
# distributions: sdist bdist_wheel
|
||||
# user: spulec
|
||||
# password:
|
||||
# secure: NxnPylnTfekJmGyoufCw0lMoYRskSMJzvAIyAlJJVYKwEhmiCPOrdy5qV8i8mRZ1AkUsqU3jBZ/PD56n96clHW0E3d080UleRDj6JpyALVdeLfMqZl9kLmZ8bqakWzYq3VSJKw2zGP/L4tPGf8wTK1SUv9yl/YNDsBdCkjDverw=
|
||||
# on:
|
||||
# tags: true
|
||||
# skip_existing: true
|
7
Makefile
7
Makefile
@ -26,7 +26,12 @@ format:
|
||||
test-only:
|
||||
rm -f .coverage
|
||||
rm -rf cover
|
||||
@pytest -sv --cov=moto --cov-report html ./tests/ $(TEST_EXCLUDE)
|
||||
pytest -sv ./tests/ $(TEST_EXCLUDE)
|
||||
|
||||
test-coverage:
|
||||
rm -f .coverage
|
||||
rm -rf cover
|
||||
pytest -sv --cov=moto --cov-report xml ./tests/ $(TEST_EXCLUDE)
|
||||
|
||||
test: lint test-only
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
[![Join the chat at https://gitter.im/awsmoto/Lobby](https://badges.gitter.im/awsmoto/Lobby.svg)](https://gitter.im/awsmoto/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
|
||||
[![Build Status](https://travis-ci.org/spulec/moto.svg?branch=master)](https://travis-ci.org/spulec/moto)
|
||||
[![Build Status](https://github.com/spulec/moto/workflows/TestNDeploy/badge.svg)](https://github.com/spulec/moto/actions)
|
||||
[![Coverage Status](https://coveralls.io/repos/spulec/moto/badge.svg?branch=master)](https://coveralls.io/r/spulec/moto)
|
||||
[![Docs](https://readthedocs.org/projects/pip/badge/?version=stable)](http://docs.getmoto.org)
|
||||
![PyPI](https://img.shields.io/pypi/v/moto.svg)
|
||||
@ -441,8 +441,8 @@ As a result, you need to add that entry to your host file for your tests to func
|
||||
|
||||
## Releases
|
||||
|
||||
Releases are done from travisci. Fairly closely following this:
|
||||
https://docs.travis-ci.com/user/deployment/pypi/
|
||||
Releases are done from Gitlab Actions. Fairly closely following this:
|
||||
https://packaging.python.org/guides/publishing-package-distribution-releases-using-github-actions-ci-cd-workflows/
|
||||
|
||||
- Commits to `master` branch do a dev deploy to pypi.
|
||||
- Commits to a tag do a real deploy to pypi.
|
||||
|
@ -4,3 +4,6 @@ universal=1
|
||||
[tool:pytest]
|
||||
markers =
|
||||
network: marks tests which require network connection
|
||||
|
||||
[coverage:run]
|
||||
relative_files = True
|
||||
|
@ -2523,6 +2523,16 @@ def test_create_open_id_connect_provider():
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("url", ["example.org", "example"])
|
||||
@mock_iam
|
||||
def test_create_open_id_connect_provider_invalid_url(url):
|
||||
client = boto3.client("iam", region_name="us-east-1")
|
||||
with pytest.raises(ClientError) as e:
|
||||
client.create_open_id_connect_provider(Url=url, ThumbprintList=[])
|
||||
msg = e.value.response["Error"]["Message"]
|
||||
msg.should.contain("Invalid Open ID Connect Provider URL")
|
||||
|
||||
|
||||
@mock_iam
|
||||
def test_create_open_id_connect_provider_errors():
|
||||
client = boto3.client("iam", region_name="us-east-1")
|
||||
@ -2532,49 +2542,65 @@ def test_create_open_id_connect_provider_errors():
|
||||
Url="https://example.com", ThumbprintList=[]
|
||||
).should.throw(ClientError, "Unknown")
|
||||
|
||||
client.create_open_id_connect_provider.when.called_with(
|
||||
Url="example.org", ThumbprintList=[]
|
||||
).should.throw(ClientError, "Invalid Open ID Connect Provider URL")
|
||||
|
||||
client.create_open_id_connect_provider.when.called_with(
|
||||
Url="example", ThumbprintList=[]
|
||||
).should.throw(ClientError, "Invalid Open ID Connect Provider URL")
|
||||
@mock_iam
|
||||
def test_create_open_id_connect_provider_too_many_entries():
|
||||
client = boto3.client("iam", region_name="us-east-1")
|
||||
|
||||
client.create_open_id_connect_provider.when.called_with(
|
||||
Url="http://example.org",
|
||||
ThumbprintList=["a" * 40, "b" * 40, "c" * 40, "d" * 40, "e" * 40, "f" * 40,],
|
||||
).should.throw(ClientError, "Thumbprint list must contain fewer than 5 entries.")
|
||||
with pytest.raises(ClientError) as e:
|
||||
client.create_open_id_connect_provider(
|
||||
Url="http://example.org",
|
||||
ThumbprintList=[
|
||||
"a" * 40,
|
||||
"b" * 40,
|
||||
"c" * 40,
|
||||
"d" * 40,
|
||||
"e" * 40,
|
||||
"f" * 40,
|
||||
],
|
||||
)
|
||||
msg = e.value.response["Error"]["Message"]
|
||||
msg.should.contain("Thumbprint list must contain fewer than 5 entries.")
|
||||
|
||||
|
||||
@mock_iam
|
||||
def test_create_open_id_connect_provider_quota_error():
|
||||
client = boto3.client("iam", region_name="us-east-1")
|
||||
|
||||
too_many_client_ids = ["{}".format(i) for i in range(101)]
|
||||
client.create_open_id_connect_provider.when.called_with(
|
||||
Url="http://example.org", ThumbprintList=[], ClientIDList=too_many_client_ids,
|
||||
).should.throw(
|
||||
ClientError, "Cannot exceed quota for ClientIdsPerOpenIdConnectProvider: 100",
|
||||
)
|
||||
with pytest.raises(ClientError) as e:
|
||||
client.create_open_id_connect_provider(
|
||||
Url="http://example.org",
|
||||
ThumbprintList=[],
|
||||
ClientIDList=too_many_client_ids,
|
||||
)
|
||||
msg = e.value.response["Error"]["Message"]
|
||||
msg.should.contain("Cannot exceed quota for ClientIdsPerOpenIdConnectProvider: 100")
|
||||
|
||||
|
||||
@mock_iam
|
||||
def test_create_open_id_connect_provider_multiple_errors():
|
||||
client = boto3.client("iam", region_name="us-east-1")
|
||||
|
||||
too_long_url = "b" * 256
|
||||
too_long_thumbprint = "b" * 41
|
||||
too_long_client_id = "b" * 256
|
||||
client.create_open_id_connect_provider.when.called_with(
|
||||
Url=too_long_url,
|
||||
ThumbprintList=[too_long_thumbprint],
|
||||
ClientIDList=[too_long_client_id],
|
||||
).should.throw(
|
||||
ClientError,
|
||||
"3 validation errors detected: "
|
||||
'Value "{0}" at "clientIDList" failed to satisfy constraint: '
|
||||
"Member must satisfy constraint: "
|
||||
"[Member must have length less than or equal to 255, "
|
||||
"Member must have length greater than or equal to 1]; "
|
||||
'Value "{1}" at "thumbprintList" failed to satisfy constraint: '
|
||||
"Member must satisfy constraint: "
|
||||
"[Member must have length less than or equal to 40, "
|
||||
"Member must have length greater than or equal to 40]; "
|
||||
'Value "{2}" at "url" failed to satisfy constraint: '
|
||||
"Member must have length less than or equal to 255".format(
|
||||
[too_long_client_id], [too_long_thumbprint], too_long_url
|
||||
),
|
||||
)
|
||||
with pytest.raises(ClientError) as e:
|
||||
client.create_open_id_connect_provider(
|
||||
Url=too_long_url,
|
||||
ThumbprintList=[too_long_thumbprint],
|
||||
ClientIDList=[too_long_client_id],
|
||||
)
|
||||
msg = e.value.response["Error"]["Message"]
|
||||
msg.should.contain("3 validation errors detected:")
|
||||
msg.should.contain('"clientIDList" failed to satisfy constraint:')
|
||||
msg.should.contain("Member must have length less than or equal to 255")
|
||||
msg.should.contain("Member must have length greater than or equal to 1")
|
||||
msg.should.contain('"thumbprintList" failed to satisfy constraint:')
|
||||
msg.should.contain("Member must have length less than or equal to 40")
|
||||
msg.should.contain("Member must have length greater than or equal to 40")
|
||||
msg.should.contain('"url" failed to satisfy constraint:')
|
||||
msg.should.contain("Member must have length less than or equal to 255")
|
||||
|
||||
|
||||
@mock_iam
|
||||
|
@ -831,9 +831,8 @@ def test_describe_parameters_with_parameter_filters_path():
|
||||
|
||||
|
||||
@mock_ssm
|
||||
def test_describe_parameters_invalid_parameter_filters():
|
||||
def test_describe_parameters_needs_param():
|
||||
client = boto3.client("ssm", region_name="us-east-1")
|
||||
|
||||
client.describe_parameters.when.called_with(
|
||||
Filters=[{"Key": "Name", "Values": ["test"]}],
|
||||
ParameterFilters=[{"Key": "Name", "Values": ["test"]}],
|
||||
@ -842,145 +841,119 @@ def test_describe_parameters_invalid_parameter_filters():
|
||||
"You can use either Filters or ParameterFilters in a single request.",
|
||||
)
|
||||
|
||||
client.describe_parameters.when.called_with(ParameterFilters=[{}]).should.throw(
|
||||
ParamValidationError,
|
||||
'Parameter validation failed:\nMissing required parameter in ParameterFilters[0]: "Key"',
|
||||
)
|
||||
|
||||
client.describe_parameters.when.called_with(
|
||||
ParameterFilters=[{"Key": "key"}]
|
||||
).should.throw(
|
||||
ClientError,
|
||||
'1 validation error detected: Value "key" at "parameterFilters.1.member.key" failed to satisfy constraint: '
|
||||
"Member must satisfy regular expression pattern: tag:.+|Name|Type|KeyId|Path|Label|Tier",
|
||||
)
|
||||
|
||||
long_key = "tag:" + "t" * 129
|
||||
client.describe_parameters.when.called_with(
|
||||
ParameterFilters=[{"Key": long_key}]
|
||||
).should.throw(
|
||||
ClientError,
|
||||
'1 validation error detected: Value "{value}" at "parameterFilters.1.member.key" failed to satisfy constraint: '
|
||||
"Member must have length less than or equal to 132".format(value=long_key),
|
||||
)
|
||||
|
||||
client.describe_parameters.when.called_with(
|
||||
ParameterFilters=[{"Key": "Name", "Option": "over 10 chars"}]
|
||||
).should.throw(
|
||||
ClientError,
|
||||
'1 validation error detected: Value "over 10 chars" at "parameterFilters.1.member.option" failed to satisfy constraint: '
|
||||
"Member must have length less than or equal to 10",
|
||||
)
|
||||
|
||||
many_values = ["test"] * 51
|
||||
client.describe_parameters.when.called_with(
|
||||
ParameterFilters=[{"Key": "Name", "Values": many_values}]
|
||||
).should.throw(
|
||||
ClientError,
|
||||
'1 validation error detected: Value "{value}" at "parameterFilters.1.member.values" failed to satisfy constraint: '
|
||||
"Member must have length less than or equal to 50".format(value=many_values),
|
||||
)
|
||||
|
||||
long_value = ["t" * 1025]
|
||||
client.describe_parameters.when.called_with(
|
||||
ParameterFilters=[{"Key": "Name", "Values": long_value}]
|
||||
).should.throw(
|
||||
ClientError,
|
||||
'1 validation error detected: Value "{value}" at "parameterFilters.1.member.values" failed to satisfy constraint: '
|
||||
"[Member must have length less than or equal to 1024, Member must have length greater than or equal to 1]".format(
|
||||
value=long_value
|
||||
@pytest.mark.parametrize(
|
||||
"filters,error_msg",
|
||||
[
|
||||
(
|
||||
[{"Key": "key"}],
|
||||
"Member must satisfy regular expression pattern: tag:.+|Name|Type|KeyId|Path|Label|Tier",
|
||||
),
|
||||
)
|
||||
(
|
||||
[{"Key": "tag:" + "t" * 129}],
|
||||
"Member must have length less than or equal to 132",
|
||||
),
|
||||
(
|
||||
[{"Key": "Name", "Option": "over 10 chars"}],
|
||||
"Member must have length less than or equal to 10",
|
||||
),
|
||||
(
|
||||
[{"Key": "Name", "Values": ["test"] * 51}],
|
||||
"Member must have length less than or equal to 50",
|
||||
),
|
||||
(
|
||||
[{"Key": "Name", "Values": ["t" * 1025]}],
|
||||
"Member must have length less than or equal to 1024, Member must have length greater than or equal to 1",
|
||||
),
|
||||
(
|
||||
[{"Key": "Name", "Option": "over 10 chars"}, {"Key": "key"}],
|
||||
"2 validation errors detected:",
|
||||
),
|
||||
(
|
||||
[{"Key": "Label"}],
|
||||
"The following filter key is not valid: Label. Valid filter keys include: [Path, Name, Type, KeyId, Tier]",
|
||||
),
|
||||
(
|
||||
[{"Key": "Name"}],
|
||||
"The following filter values are missing : null for filter key Name",
|
||||
),
|
||||
(
|
||||
[
|
||||
{"Key": "Name", "Values": ["test"]},
|
||||
{"Key": "Name", "Values": ["test test"]},
|
||||
],
|
||||
"The following filter is duplicated in the request: Name. A request can contain only one occurrence of a specific filter.",
|
||||
),
|
||||
(
|
||||
[{"Key": "Path", "Values": ["/aws", "/ssm"]}],
|
||||
'Filters for common parameters can\'t be prefixed with "aws" or "ssm" (case-insensitive).',
|
||||
),
|
||||
(
|
||||
[{"Key": "Path", "Option": "Equals", "Values": ["test"]}],
|
||||
"The following filter option is not valid: Equals. Valid options include: [Recursive, OneLevel]",
|
||||
),
|
||||
(
|
||||
[{"Key": "Tier", "Values": ["test"]}],
|
||||
"The following filter value is not valid: test. Valid values include: [Standard, Advanced, Intelligent-Tiering]",
|
||||
),
|
||||
(
|
||||
[{"Key": "Type", "Values": ["test"]}],
|
||||
"The following filter value is not valid: test. Valid values include: [String, StringList, SecureString]",
|
||||
),
|
||||
(
|
||||
[{"Key": "Name", "Option": "option", "Values": ["test"]}],
|
||||
"The following filter option is not valid: option. Valid options include: [BeginsWith, Equals].",
|
||||
),
|
||||
],
|
||||
)
|
||||
@mock_ssm
|
||||
def test_describe_parameters_invalid_parameter_filters(filters, error_msg):
|
||||
client = boto3.client("ssm", region_name="us-east-1")
|
||||
|
||||
client.describe_parameters.when.called_with(
|
||||
ParameterFilters=[{"Key": "Name", "Option": "over 10 chars"}, {"Key": "key"}]
|
||||
).should.throw(
|
||||
ClientError,
|
||||
"2 validation errors detected: "
|
||||
'Value "over 10 chars" at "parameterFilters.1.member.option" failed to satisfy constraint: '
|
||||
"Member must have length less than or equal to 10; "
|
||||
'Value "key" at "parameterFilters.2.member.key" failed to satisfy constraint: '
|
||||
"Member must satisfy regular expression pattern: tag:.+|Name|Type|KeyId|Path|Label|Tier",
|
||||
)
|
||||
with pytest.raises(ClientError) as e:
|
||||
client.describe_parameters(ParameterFilters=filters)
|
||||
e.value.response["Error"]["Message"].should.contain(error_msg)
|
||||
|
||||
client.describe_parameters.when.called_with(
|
||||
ParameterFilters=[{"Key": "Label"}]
|
||||
).should.throw(
|
||||
ClientError,
|
||||
"The following filter key is not valid: Label. Valid filter keys include: [Path, Name, Type, KeyId, Tier].",
|
||||
)
|
||||
|
||||
client.describe_parameters.when.called_with(
|
||||
ParameterFilters=[{"Key": "Name"}]
|
||||
).should.throw(
|
||||
ClientError,
|
||||
"The following filter values are missing : null for filter key Name.",
|
||||
)
|
||||
@pytest.mark.parametrize("value", ["/###", "//", "test"])
|
||||
@mock_ssm
|
||||
def test_describe_parameters_invalid_path(value):
|
||||
client = boto3.client("ssm", region_name="us-east-1")
|
||||
|
||||
client.describe_parameters.when.called_with(
|
||||
ParameterFilters=[{"Key": "Name", "Values": []}]
|
||||
).should.throw(
|
||||
ParamValidationError,
|
||||
"Invalid length for parameter ParameterFilters[0].Values, value: 0, valid range: 1-inf",
|
||||
)
|
||||
|
||||
client.describe_parameters.when.called_with(
|
||||
ParameterFilters=[
|
||||
{"Key": "Name", "Values": ["test"]},
|
||||
{"Key": "Name", "Values": ["test test"]},
|
||||
]
|
||||
).should.throw(
|
||||
ClientError,
|
||||
"The following filter is duplicated in the request: Name. A request can contain only one occurrence of a specific filter.",
|
||||
)
|
||||
|
||||
for value in ["/###", "//", "test"]:
|
||||
client.describe_parameters.when.called_with(
|
||||
with pytest.raises(ClientError) as e:
|
||||
client.describe_parameters(
|
||||
ParameterFilters=[{"Key": "Path", "Values": [value]}]
|
||||
).should.throw(
|
||||
ClientError,
|
||||
'The parameter doesn\'t meet the parameter name requirements. The parameter name must begin with a forward slash "/". '
|
||||
'It can\'t be prefixed with "aws" or "ssm" (case-insensitive). '
|
||||
"It must use only letters, numbers, or the following symbols: . (period), - (hyphen), _ (underscore). "
|
||||
'Special characters are not allowed. All sub-paths, if specified, must use the forward slash symbol "/". '
|
||||
"Valid example: /get/parameters2-/by1./path0_.",
|
||||
)
|
||||
|
||||
client.describe_parameters.when.called_with(
|
||||
ParameterFilters=[{"Key": "Path", "Values": ["/aws", "/ssm"]}]
|
||||
).should.throw(
|
||||
ClientError,
|
||||
'Filters for common parameters can\'t be prefixed with "aws" or "ssm" (case-insensitive). '
|
||||
"When using global parameters, please specify within a global namespace.",
|
||||
msg = e.value.response["Error"]["Message"]
|
||||
msg.should.contain("The parameter doesn't meet the parameter name requirements")
|
||||
msg.should.contain('The parameter name must begin with a forward slash "/".')
|
||||
msg.should.contain('It can\'t be prefixed with "aws" or "ssm" (case-insensitive).')
|
||||
msg.should.contain(
|
||||
"It must use only letters, numbers, or the following symbols: . (period), - (hyphen), _ (underscore)."
|
||||
)
|
||||
|
||||
client.describe_parameters.when.called_with(
|
||||
ParameterFilters=[{"Key": "Path", "Option": "Equals", "Values": ["test"]}]
|
||||
).should.throw(
|
||||
ClientError,
|
||||
"The following filter option is not valid: Equals. Valid options include: [Recursive, OneLevel].",
|
||||
msg.should.contain(
|
||||
'Special characters are not allowed. All sub-paths, if specified, must use the forward slash symbol "/".'
|
||||
)
|
||||
msg.should.contain("Valid example: /get/parameters2-/by1./path0_.")
|
||||
|
||||
client.describe_parameters.when.called_with(
|
||||
ParameterFilters=[{"Key": "Tier", "Values": ["test"]}]
|
||||
).should.throw(
|
||||
ClientError,
|
||||
"The following filter value is not valid: test. Valid values include: [Standard, Advanced, Intelligent-Tiering]",
|
||||
)
|
||||
|
||||
client.describe_parameters.when.called_with(
|
||||
ParameterFilters=[{"Key": "Type", "Values": ["test"]}]
|
||||
).should.throw(
|
||||
ClientError,
|
||||
"The following filter value is not valid: test. Valid values include: [String, StringList, SecureString]",
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
"filters,error_msg",
|
||||
[
|
||||
([{}], 'Missing required parameter in ParameterFilters[0]: "Key"',),
|
||||
(
|
||||
[{"Key": "Name", "Values": []}],
|
||||
"Invalid length for parameter ParameterFilters[0].Values, value: 0, valid range: 1-inf",
|
||||
),
|
||||
],
|
||||
)
|
||||
@mock_ssm
|
||||
def test_describe_parameters_parameter_validation(filters, error_msg):
|
||||
client = boto3.client("ssm", region_name="us-east-1")
|
||||
|
||||
client.describe_parameters.when.called_with(
|
||||
ParameterFilters=[{"Key": "Name", "Option": "option", "Values": ["test"]}]
|
||||
).should.throw(
|
||||
ClientError,
|
||||
"The following filter option is not valid: option. Valid options include: [BeginsWith, Equals].",
|
||||
)
|
||||
with pytest.raises(ParamValidationError) as e:
|
||||
client.describe_parameters(ParameterFilters=filters)
|
||||
e.value.kwargs["report"].should.contain(error_msg)
|
||||
|
||||
|
||||
@mock_ssm
|
||||
|
@ -25,7 +25,7 @@ while True:
|
||||
break
|
||||
except EXCEPTIONS:
|
||||
elapsed_s = time.time() - start_ts
|
||||
if elapsed_s > 60:
|
||||
if elapsed_s > 120:
|
||||
raise
|
||||
|
||||
print(".")
|
||||
|
Loading…
Reference in New Issue
Block a user