24 Commits
1.0.1 ... 1.0.6

Author SHA1 Message Date
e5685511ba [chore] Bump actions version 2024-03-22 16:56:37 +01:00
d1d01b242c Drop support for python 3.7 2024-03-22 16:49:17 +01:00
e2f0f4f63c chore: Bump python version to 3.11 2023-06-24 11:57:53 +02:00
b657c4a4d3 Fix linting warning 2022-09-30 21:07:44 +02:00
87618a960d Update README.md to enforce the use of pipx for installing 2022-03-26 20:33:27 +01:00
b1f48d5534 Fix badges links at README.md 2021-12-26 12:05:22 +01:00
ca98c678b2 Merge pull request #8 from dcarrillo/add-3.10-tests
Add python 3.10 to the tests matrix
2021-12-24 12:54:31 +01:00
5b58df2b3a Fix workflow yaml 2021-12-24 12:46:57 +01:00
ad80450442 Add python 3.10 to the tests matrix 2021-12-24 12:45:01 +01:00
61970f6679 Merge pull request #7 from dcarrillo/fix-mypy
Install missing stubs for mypy
2021-07-01 19:55:08 +02:00
613727ef5a Install missing stubs for mypy 2021-07-01 19:51:08 +02:00
a3517a25ab Merge pull request #6 from dcarrillo/1.0.4
Return an empty array when there is no data for json output
2021-02-19 17:51:56 +01:00
204ec8f9ff Return an empty array when there is no data for json output 2021-02-19 17:46:04 +01:00
ef31af865a Fix test 2021-02-19 17:45:07 +01:00
fda5957a04 Bump version to 1.0.3 2020-12-28 14:50:15 +01:00
0b2f98fa4f Fix license at setup.py 2020-12-28 14:01:44 +01:00
e6756888e7 Ensure tests run for every push 2020-12-26 19:00:42 +01:00
3e03e532fd Add badges to README.md 2020-12-26 18:50:48 +01:00
ae7bf28702 Fix github workflow 2020-12-26 17:02:04 +01:00
971ead7ed2 Bump version to 1.0.2 2020-12-26 13:55:48 +01:00
d9c8738e9a Merge pull request #4 from dcarrillo/output-fields
Add --output-fields parameter
2020-12-26 13:54:36 +01:00
1bd1f91e45 Add --output-fields parameter 2020-12-26 13:52:08 +01:00
352b9da211 Merge pull request #3 from dcarrillo/improve-ci
Improve CI flow
2020-12-26 12:41:14 +01:00
4edcc42440 Improve CI flow 2020-12-26 12:35:13 +01:00
9 changed files with 126 additions and 44 deletions

View File

@ -4,6 +4,8 @@ on:
push: push:
branches: branches:
- main - main
tags:
- '*'
pull_request: pull_request:
jobs: jobs:
@ -12,12 +14,12 @@ jobs:
strategy: strategy:
matrix: matrix:
os: [ubuntu-latest, macos-latest, windows-latest] os: [ubuntu-latest, macos-latest, windows-latest]
python-version: [3.7, 3.8, 3.9] python-version: ["3.8", "3.9", "3.10", "3.11"]
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }} - name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2 uses: actions/setup-python@v5
with: with:
python-version: ${{ matrix.python-version }} python-version: ${{ matrix.python-version }}
@ -36,19 +38,20 @@ jobs:
build_publish: build_publish:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: github.event_name == 'push' needs: tests
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v4
- name: Set up Python 3.8 - name: Set up Python 3.11
uses: actions/setup-python@v2 uses: actions/setup-python@v5
with: with:
python-version: 3.8 python-version: 3.11
- name: Install tools - name: Install tools
run: | run: |
python -m pip install --upgrade pip python -m pip install --upgrade pip
pip install twine pip install twine wheel
- name: Build - name: Build
run: | run: |

View File

@ -1,25 +1,36 @@
# digaws # digaws
The digaws lookup tool displays information for a given IP address (v4 o v6) or a CIDR, sourced from the AWS official IP ranges. The digaws lookup tool displays information for a given IP address (both v4 and v6) or a CIDR, sourced from the AWS official IP ranges.
In order to save bandwidth and time this tool requests the [AWS IP ranges](https://ip-ranges.amazonaws.com/ip-ranges.json) and keeps In order to save bandwidth and time this tool requests the [AWS IP ranges](https://ip-ranges.amazonaws.com/ip-ranges.json) and keeps
a cached version until a new version is published. a cached version until a new version is published.
[![PyPI version](https://badge.fury.io/py/digaws.svg)](https://pypi.org/project/digaws/)
[![CI](https://github.com/dcarrillo/digaws/workflows/CI/badge.svg)](https://github.com/dcarrillo/digaws/actions)
## Requirements ## Requirements
Python >= 3.7 Python >= 3.8
Tests are verified on Linux, macos and Windows. Tests are verified on Linux, macos and Windows.
## Install ## Install
### Using [pipx](https://pypa.github.io/pipx/#install-pipx) (this is the preferred way)
```bash ```bash
pip install digaws pipx install digaws
```
### Using pip
```bash
pip install digaws --user
``` ```
## Usage ## Usage
```text ```text
usage: digaws [-h] [--output <plain|json>] [--debug] <ip address|cidr> [<ip address|cidr> ...] usage: digaws [-h] [--output <plain|json>] [--output-fields [{prefix,region,service,network_border_group} ...]] [--debug] [--version] <ip address|cidr> [<ip address|cidr> ...]
Look up canonical information for AWS IP addresses and networks Look up canonical information for AWS IP addresses and networks
@ -30,7 +41,10 @@ optional arguments:
-h, --help show this help message and exit -h, --help show this help message and exit
--output <plain|json> --output <plain|json>
Formatting style for command output, by default plain Formatting style for command output, by default plain
--output-fields [{prefix,region,service,network_border_group} ...]
Print only the given fields
--debug Enable debug --debug Enable debug
--version show program's version number and exit
``` ```
## Examples ## Examples
@ -104,3 +118,14 @@ Network border group: sa-east-1
} }
] ]
``` ```
- Choose output fields
```text
~ » digaws 34.255.166.63 --output-fields service
Service: AMAZON
Service: EC2
```

View File

@ -1,2 +1,2 @@
__version__ = '1.0.1' __version__ = '1.0.6'
__description__ = 'Look up canonical information for AWS IP addresses and networks' __description__ = 'Look up canonical information for AWS IP addresses and networks'

View File

@ -20,6 +20,7 @@ from . import __version__
AWS_IP_RANGES_URL = 'https://ip-ranges.amazonaws.com/ip-ranges.json' AWS_IP_RANGES_URL = 'https://ip-ranges.amazonaws.com/ip-ranges.json'
CACHE_DIR = Path(Path.home() / '.digaws') CACHE_DIR = Path(Path.home() / '.digaws')
CACHE_FILE = CACHE_DIR / 'ip-ranges.json' CACHE_FILE = CACHE_DIR / 'ip-ranges.json'
OUTPUT_FIELDS = ['prefix', 'region', 'service', 'network_border_group']
logger = logging.getLogger() logger = logging.getLogger()
handler = logging.StreamHandler(sys.stderr) handler = logging.StreamHandler(sys.stderr)
@ -87,19 +88,23 @@ class UnexpectedRequestException(Exception):
class DigAWSPrettyPrinter: class DigAWSPrettyPrinter:
def __init__(self, data: List[Dict], output_filter: List[str] = []): def __init__(self, data: List[Dict], output_fields: List[str] = []):
self.data = data self.data = data
self.output_filter = output_filter self.output_fields = output_fields
def plain_print(self) -> None: def plain_print(self) -> None:
for prefix in self.data: for prefix in self.data:
try: if 'prefix' in self.output_fields:
print(f'Prefix: {prefix["ip_prefix"]}') try:
except KeyError: print(f'Prefix: {prefix["ip_prefix"]}')
print(f'IPv6 Prefix: {prefix["ipv6_prefix"]}') except KeyError:
print(f'Region: {prefix["region"]}') print(f'IPv6 Prefix: {prefix["ipv6_prefix"]}')
print(f'Service: {prefix["service"]}') if 'region' in self.output_fields:
print(f'Network border group: {prefix["network_border_group"]}') print(f'Region: {prefix["region"]}')
if 'service' in self.output_fields:
print(f'Service: {prefix["service"]}')
if 'network_border_group' in self.output_fields:
print(f'Network border group: {prefix["network_border_group"]}')
print('') print('')
def json_print(self) -> None: def json_print(self) -> None:
@ -110,22 +115,25 @@ class DigAWSPrettyPrinter:
prefix_type = 'ip_prefix' prefix_type = 'ip_prefix'
except KeyError: except KeyError:
prefix_type = 'ipv6_prefix' prefix_type = 'ipv6_prefix'
data.append(
{ item_dict = {}
prefix_type: str(prefix[prefix_type]), if 'prefix' in self.output_fields:
'region': prefix['region'], item_dict.update({prefix_type: str(prefix[prefix_type])})
'service': prefix['service'], if 'region' in self.output_fields:
'network_border_group': prefix['network_border_group'] item_dict.update({'region': prefix['region']})
} if 'service' in self.output_fields:
) item_dict.update({'service': prefix['service']})
if data: if 'network_border_group' in self.output_fields:
print(json.dumps(data, indent=2)) item_dict.update({'network_border_group': prefix['network_border_group']})
data.append(item_dict)
print(json.dumps(data, indent=2))
class DigAWS(): class DigAWS():
def __init__(self, ip_ranges: Dict, output: str = 'plain', output_filter: List[str] = []): def __init__(self, *, ip_ranges: Dict, output: str = 'plain', output_fields: List[str] = []):
self.output = output self.output = output
self.output_filter = output_filter self.output_fields = output_fields
self.ip_prefixes = [ self.ip_prefixes = [
{ {
'ip_prefix': ipaddress.IPv4Network(prefix['ip_prefix']), 'ip_prefix': ipaddress.IPv4Network(prefix['ip_prefix']),
@ -148,7 +156,7 @@ class DigAWS():
def lookup(self, address: str) -> DigAWSPrettyPrinter: def lookup(self, address: str) -> DigAWSPrettyPrinter:
return DigAWSPrettyPrinter( return DigAWSPrettyPrinter(
self._lookup_data(address), self._lookup_data(address),
self.output_filter self.output_fields
) )
def _lookup_data(self, address: str) -> List[Dict]: def _lookup_data(self, address: str) -> List[Dict]:
@ -173,7 +181,7 @@ class DigAWS():
data = [prefix for prefix in self.ipv6_prefixes data = [prefix for prefix in self.ipv6_prefixes
if addr.subnet_of(prefix['ipv6_prefix'])] if addr.subnet_of(prefix['ipv6_prefix'])]
except (ipaddress.AddressValueError, ValueError): except (ipaddress.AddressValueError, ValueError):
raise(ValueError(f'Wrong IP or CIDR format: {address}')) raise ValueError(f'Wrong IP or CIDR format: {address}')
return data return data
@ -193,6 +201,15 @@ def arguments_parser() -> argparse.ArgumentParser:
default='plain', default='plain',
help='Formatting style for command output, by default %(default)s' help='Formatting style for command output, by default %(default)s'
) )
parser.add_argument(
'--output-fields',
nargs='*',
choices=OUTPUT_FIELDS,
required=False,
dest='output_fields',
default=OUTPUT_FIELDS,
help='Print only the given fields'
)
parser.add_argument( parser.add_argument(
'--debug', '--debug',
action='store_true', action='store_true',
@ -225,7 +242,7 @@ def main():
try: try:
ip_ranges = get_aws_ip_ranges() ip_ranges = get_aws_ip_ranges()
dig = DigAWS(ip_ranges) dig = DigAWS(ip_ranges=ip_ranges, output_fields=args.output_fields)
responses = [] responses = []
for address in args.addresses: for address in args.addresses:
@ -242,7 +259,7 @@ def main():
for response in responses: for response in responses:
joined += response.data joined += response.data
DigAWSPrettyPrinter(joined).json_print() DigAWSPrettyPrinter(joined, args.output_fields).json_print()
except ( except (
RequestException, RequestException,
ipaddress.AddressValueError, ipaddress.AddressValueError,

View File

@ -1,10 +1,12 @@
import nox import nox
nox.options.sessions = ['lint', 'typing', 'tests'] nox.options.sessions = ['lint', 'typing', 'tests']
locations = ['noxfile.py', 'setup.py', 'digaws/', 'tests/'] locations = ['noxfile.py', 'setup.py', 'digaws/', 'tests/']
lint_common_args = ['--max-line-length', '120'] lint_common_args = ['--max-line-length', '120']
mypy_args = ['--ignore-missing-imports'] mypy_args = ['--ignore-missing-imports', '--install-types', '--non-interactive']
pytest_args = ['--cov=digaws', '--cov-report=', 'tests/'] pytest_args = ['--cov=digaws', '--cov-report=', 'tests/']
coverage_args = ['report', '--show-missing', '--fail-under=80'] coverage_args = ['report', '--show-missing', '--fail-under=80']

View File

@ -17,7 +17,7 @@ setup(
url='http://github.com/dcarrillo/digaws', url='http://github.com/dcarrillo/digaws',
author='Daniel Carrillo', author='Daniel Carrillo',
author_email='daniel.carrillo@gmail.com', author_email='daniel.carrillo@gmail.com',
license='MIT', license='Apache Software License',
packages=['digaws'], packages=['digaws'],
zip_safe=False, zip_safe=False,
classifiers=[ classifiers=[
@ -25,7 +25,7 @@ setup(
'License :: OSI Approved :: Apache Software License', 'License :: OSI Approved :: Apache Software License',
'Operating System :: OS Independent', 'Operating System :: OS Independent',
], ],
python_requires='>=3.7', python_requires='>=3.8',
entry_points={ entry_points={
'console_scripts': ['digaws=digaws.digaws:main'] 'console_scripts': ['digaws=digaws.digaws:main']
}, },

View File

@ -111,6 +111,18 @@ RESPONSE_JSON_PRINT = '''[
] ]
''' '''
RESPONSE_JSON_FIELDS_PRINT = '''[
{
"service": "ROUTE53_HEALTHCHECKS",
"network_border_group": "us-west-2"
},
{
"service": "EC2",
"network_border_group": "us-west-2"
}
]
'''
RESPONSE_JSON_JOINED_PRINT = '''[ RESPONSE_JSON_JOINED_PRINT = '''[
{ {
"ip_prefix": "52.94.76.0/22", "ip_prefix": "52.94.76.0/22",

View File

@ -11,7 +11,10 @@ import tests
@pytest.fixture @pytest.fixture
def test_dig(): def test_dig():
return digaws.DigAWS(json.loads(tests.AWS_IP_RANGES)) return digaws.DigAWS(ip_ranges=json.loads(
tests.AWS_IP_RANGES),
output_fields=digaws.OUTPUT_FIELDS
)
def test_cli(capsys): def test_cli(capsys):
@ -44,6 +47,25 @@ def test_cli_invocation(capsys, mocker):
assert out == tests.RESPONSE_JSON_JOINED_PRINT assert out == tests.RESPONSE_JSON_JOINED_PRINT
def test_cli_output_plain_fields_invocation(capsys, mocker):
sys.argv = ['digaws', '52.94.76.0/22', '--output=plain', '--output-fields', 'region']
mocker.patch('digaws.digaws.get_aws_ip_ranges', return_value=json.loads(tests.AWS_IP_RANGES))
digaws.main()
out, _ = capsys.readouterr()
assert out == 'Region: us-west-2\n\n'
def test_cli_output_json_fields_invocation(capsys, mocker):
sys.argv = ['digaws', '2600:1f14:fff:f810:a1c1:f507:a2d1:2dd8', '--output=json',
'--output-fields', 'service', 'network_border_group']
mocker.patch('digaws.digaws.get_aws_ip_ranges', return_value=json.loads(tests.AWS_IP_RANGES))
digaws.main()
out, _ = capsys.readouterr()
assert out == tests.RESPONSE_JSON_FIELDS_PRINT
def test_dig_aws_construct(test_dig): def test_dig_aws_construct(test_dig):
assert test_dig.ip_prefixes == tests.AWS_IPV4_RANGES_OBJ assert test_dig.ip_prefixes == tests.AWS_IPV4_RANGES_OBJ
assert test_dig.ipv6_prefixes == tests.AWS_IPV6_RANGES_OBJ assert test_dig.ipv6_prefixes == tests.AWS_IPV6_RANGES_OBJ

View File

@ -84,4 +84,5 @@ def test_get_aws_ip_ranges_invalid_status(mocker, fs, create_cache_dir) -> None:
with pytest.raises(digaws.UnexpectedRequestException) as e: with pytest.raises(digaws.UnexpectedRequestException) as e:
digaws.get_aws_ip_ranges() digaws.get_aws_ip_ranges()
assert e.startswith('Unexpected response from')
assert e.match('^Unexpected response from')