From 64b2a64f45887d5115c94f39ae710c0976932958 Mon Sep 17 00:00:00 2001 From: Adar Nimrod <nimrod@shore.co.il> Date: Tue, 18 Jun 2019 15:36:03 +0300 Subject: [PATCH] A big cleanup. - Use Pipenv for local development tasks in a virtualenv. - Replace fabfile with Pipfile "scripts". - Update .gitignore. - Add lots of checks using pre-commit. - Format code using Black. - Use PEP 396 version tracking. - Use bumpversion for tagging new versions. - Fix issues found with Flake8, pylint. --- .gitignore | 25 ++++- .pre-commit-config.yaml | 52 +++++++++ MANIFEST.in | 1 - Pipfile | 20 ++++ Pipfile.lock | 215 ++++++++++++++++++++++++++++++++++++ VERSION | 1 - check_s3_bucket/__init__.py | 124 +++++++++++++-------- fabfile.py | 26 ----- setup.cfg | 11 +- setup.py | 53 +++++---- 10 files changed, 428 insertions(+), 100 deletions(-) create mode 100644 .pre-commit-config.yaml create mode 100644 Pipfile create mode 100644 Pipfile.lock delete mode 100644 VERSION delete mode 100644 fabfile.py diff --git a/.gitignore b/.gitignore index 2c6ecf8..3e66e1d 100644 --- a/.gitignore +++ b/.gitignore @@ -11,22 +11,39 @@ Thumbs.db .svn/ .sass-cache/ *.log -a.out -node-modules/ +*.out +*.so +node_modules/ +.npm/ nbproject/ *.ipynb .idea/ *.egg-info/ -*.o +*.[ao] .classpath .cache/ bower_components/ *.class -*.jar +*.[ewj]ar secring.* .*.kate-swp .swp.* .directory .Trash-* build/ +_build/ dist/ +.tox/ +*.pdf +*.exe +*.dll +*.gz +*.tgz +*.tar +*.rar +*.zip +*.pid +*.lock +*.env +.bundle/ +!Pipfile.lock diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..f185f9e --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,52 @@ +# vim:ff=unix ts=2 sw=2 ai expandtab +--- +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v2.2.3 + hooks: + - id: check-added-large-files + - id: check-executables-have-shebangs + - id: check-merge-conflict + - id: detect-private-key + - id: trailing-whitespace + - repo: https://github.com/ambv/black + rev: 18.9b0 + hooks: + - id: black + args: + - | + --line-length=79 + - repo: https://github.com/Lucas-C/pre-commit-hooks-markup + rev: v1.0.0 + hooks: + - id: rst-linter + - repo: https://github.com/PyCQA/prospector + rev: 1.1.6.4 + hooks: + - id: prospector + args: + - |- + --max-line-length=79 + - |- + --tool=pyroma + - |- + --tool=dodgy + additional_dependencies: + - pyroma + - dodgy + - repo: https://gitlab.com/pycqa/flake8 + rev: 3.7.7 + hooks: + - id: flake8 + args: + - '--max-line-length=79' + additional_dependencies: + - flake8-bugbear + - repo: https://github.com/pre-commit/mirrors-pylint + rev: v2.3.1 + hooks: + - id: pylint + - repo: https://github.com/adrienverge/yamllint + rev: v1.16.0 + hooks: + - id: yamllint diff --git a/MANIFEST.in b/MANIFEST.in index d43dd18..e0361b2 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,5 +1,4 @@ recursive-include check_s3_bucket *.py exclude .pre-commit-config.yaml include *.rst -include VERSION include *.txt diff --git a/Pipfile b/Pipfile new file mode 100644 index 0000000..8e1c9c8 --- /dev/null +++ b/Pipfile @@ -0,0 +1,20 @@ +[[source]] +name = "pypi" +url = "https://pypi.org/simple" +verify_ssl = true + +[dev-packages] +pre-commit = "*" +bumpversion = "*" +twine = "*" + +[packages] + +[requires] +python_version = "3.7" + +[scripts] +ci = "pre-commit run --all-files" +build = "python setup.py bdist_wheel" +clean = "git clean -fdX" +upload = "twine upload -s dist/*" diff --git a/Pipfile.lock b/Pipfile.lock new file mode 100644 index 0000000..2020b4d --- /dev/null +++ b/Pipfile.lock @@ -0,0 +1,215 @@ +{ + "_meta": { + "hash": { + "sha256": "bbe23c390e858bdb748ab771e9f922f21dfbb7ae59874e97a2bf467d9a693272" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.7" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": {}, + "develop": { + "aspy.yaml": { + "hashes": [ + "sha256:463372c043f70160a9ec950c3f1e4c3a82db5fca01d334b6bc89c7164d744bdc", + "sha256:e7c742382eff2caed61f87a39d13f99109088e5e93f04d76eb8d4b28aa143f45" + ], + "version": "==1.3.0" + }, + "bleach": { + "hashes": [ + "sha256:213336e49e102af26d9cde77dd2d0397afabc5a6bf2fed985dc35b5d1e285a16", + "sha256:3fdf7f77adcf649c9911387df51254b813185e32b2c6619f690b593a617e19fa" + ], + "version": "==3.1.0" + }, + "bumpversion": { + "hashes": [ + "sha256:6744c873dd7aafc24453d8b6a1a0d6d109faf63cd0cd19cb78fd46e74932c77e", + "sha256:6753d9ff3552013e2130f7bc03c1007e24473b4835952679653fb132367bdd57" + ], + "index": "pypi", + "version": "==0.5.3" + }, + "certifi": { + "hashes": [ + "sha256:046832c04d4e752f37383b628bc601a7ea7211496b4638f6514d0e5b9acc4939", + "sha256:945e3ba63a0b9f577b1395204e13c3a231f9bc0223888be653286534e5873695" + ], + "version": "==2019.6.16" + }, + "cfgv": { + "hashes": [ + "sha256:32edbe09de6f4521224b87822103a8c16a614d31a894735f7a5b3bcf0eb3c37e", + "sha256:3bd31385cd2bebddbba8012200aaf15aa208539f1b33973759b4d02fc2148da5" + ], + "version": "==2.0.0" + }, + "chardet": { + "hashes": [ + "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", + "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" + ], + "version": "==3.0.4" + }, + "docutils": { + "hashes": [ + "sha256:02aec4bd92ab067f6ff27a38a38a41173bf01bed8f89157768c1573f53e474a6", + "sha256:51e64ef2ebfb29cae1faa133b3710143496eca21c530f3f71424d77687764274", + "sha256:7a4bd47eaf6596e1295ecb11361139febe29b084a87bf005bf899f9a42edc3c6" + ], + "version": "==0.14" + }, + "identify": { + "hashes": [ + "sha256:0a11379b46d06529795442742a043dc2fa14cd8c995ae81d1febbc5f1c014c87", + "sha256:43a5d24ffdb07bc7e21faf68b08e9f526a1f41f0056073f480291539ef961dfd" + ], + "version": "==1.4.5" + }, + "idna": { + "hashes": [ + "sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407", + "sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c" + ], + "version": "==2.8" + }, + "importlib-metadata": { + "hashes": [ + "sha256:6dfd58dfe281e8d240937776065dd3624ad5469c835248219bd16cf2e12dbeb7", + "sha256:cb6ee23b46173539939964df59d3d72c3e0c1b5d54b84f1d8a7e912fe43612db" + ], + "version": "==0.18" + }, + "nodeenv": { + "hashes": [ + "sha256:ad8259494cf1c9034539f6cced78a1da4840a4b157e23640bc4a0c0546b0cb7a" + ], + "version": "==1.3.3" + }, + "pkginfo": { + "hashes": [ + "sha256:7424f2c8511c186cd5424bbf31045b77435b37a8d604990b79d4e70d741148bb", + "sha256:a6d9e40ca61ad3ebd0b72fbadd4fba16e4c0e4df0428c041e01e06eb6ee71f32" + ], + "version": "==1.5.0.1" + }, + "pre-commit": { + "hashes": [ + "sha256:92e406d556190503630fd801958379861c94884693a032ba66629d0351fdccd4", + "sha256:cccc39051bc2457b0c0f7152a411f8e05e3ba2fe1a5613e4ee0833c1c1985ce3" + ], + "index": "pypi", + "version": "==1.17.0" + }, + "pygments": { + "hashes": [ + "sha256:71e430bc85c88a430f000ac1d9b331d2407f681d6f6aec95e8bcfbc3df5b0127", + "sha256:881c4c157e45f30af185c1ffe8d549d48ac9127433f2c380c24b84572ad66297" + ], + "version": "==2.4.2" + }, + "pyyaml": { + "hashes": [ + "sha256:57acc1d8533cbe51f6662a55434f0dbecfa2b9eaf115bede8f6fd00115a0c0d3", + "sha256:588c94b3d16b76cfed8e0be54932e5729cc185caffaa5a451e7ad2f7ed8b4043", + "sha256:68c8dd247f29f9a0d09375c9c6b8fdc64b60810ebf07ba4cdd64ceee3a58c7b7", + "sha256:70d9818f1c9cd5c48bb87804f2efc8692f1023dac7f1a1a5c61d454043c1d265", + "sha256:86a93cccd50f8c125286e637328ff4eef108400dd7089b46a7be3445eecfa391", + "sha256:a0f329125a926876f647c9fa0ef32801587a12328b4a3c741270464e3e4fa778", + "sha256:a3c252ab0fa1bb0d5a3f6449a4826732f3eb6c0270925548cac342bc9b22c225", + "sha256:b4bb4d3f5e232425e25dda21c070ce05168a786ac9eda43768ab7f3ac2770955", + "sha256:cd0618c5ba5bda5f4039b9398bb7fb6a317bb8298218c3de25c47c4740e4b95e", + "sha256:ceacb9e5f8474dcf45b940578591c7f3d960e82f926c707788a570b51ba59190", + "sha256:fe6a88094b64132c4bb3b631412e90032e8cfe9745a58370462240b8cb7553cd" + ], + "version": "==5.1.1" + }, + "readme-renderer": { + "hashes": [ + "sha256:bb16f55b259f27f75f640acf5e00cf897845a8b3e4731b5c1a436e4b8529202f", + "sha256:c8532b79afc0375a85f10433eca157d6b50f7d6990f337fa498c96cd4bfc203d" + ], + "version": "==24.0" + }, + "requests": { + "hashes": [ + "sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4", + "sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31" + ], + "version": "==2.22.0" + }, + "requests-toolbelt": { + "hashes": [ + "sha256:380606e1d10dc85c3bd47bf5a6095f815ec007be7a8b69c878507068df059e6f", + "sha256:968089d4584ad4ad7c171454f0a5c6dac23971e9472521ea3b6d49d610aa6fc0" + ], + "version": "==0.9.1" + }, + "six": { + "hashes": [ + "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", + "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73" + ], + "version": "==1.12.0" + }, + "toml": { + "hashes": [ + "sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c", + "sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e" + ], + "version": "==0.10.0" + }, + "tqdm": { + "hashes": [ + "sha256:0a860bf2683fdbb4812fe539a6c22ea3f1777843ea985cb8c3807db448a0f7ab", + "sha256:e288416eecd4df19d12407d0c913cbf77aa8009d7fddb18f632aded3bdbdda6b" + ], + "version": "==4.32.1" + }, + "twine": { + "hashes": [ + "sha256:0fb0bfa3df4f62076cab5def36b1a71a2e4acb4d1fa5c97475b048117b1a6446", + "sha256:d6c29c933ecfc74e9b1d9fa13aa1f87c5d5770e119f5a4ce032092f0ff5b14dc" + ], + "index": "pypi", + "version": "==1.13.0" + }, + "urllib3": { + "hashes": [ + "sha256:b246607a25ac80bedac05c6f282e3cdaf3afb65420fd024ac94435cabe6e18d1", + "sha256:dbe59173209418ae49d485b87d1681aefa36252ee85884c31346debd19463232" + ], + "version": "==1.25.3" + }, + "virtualenv": { + "hashes": [ + "sha256:b7335cddd9260a3dd214b73a2521ffc09647bde3e9457fcca31dc3be3999d04a", + "sha256:d28ca64c0f3f125f59cabf13e0a150e1c68e5eea60983cc4395d88c584495783" + ], + "version": "==16.6.1" + }, + "webencodings": { + "hashes": [ + "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78", + "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923" + ], + "version": "==0.5.1" + }, + "zipp": { + "hashes": [ + "sha256:8c1019c6aad13642199fbe458275ad6a84907634cc9f0989877ccc4a2840139d", + "sha256:ca943a7e809cc12257001ccfb99e3563da9af99d52f261725e96dfe0f9275bc3" + ], + "version": "==0.5.1" + } + } +} diff --git a/VERSION b/VERSION deleted file mode 100644 index 6e8bf73..0000000 --- a/VERSION +++ /dev/null @@ -1 +0,0 @@ -0.1.0 diff --git a/check_s3_bucket/__init__.py b/check_s3_bucket/__init__.py index da18086..f08a77e 100755 --- a/check_s3_bucket/__init__.py +++ b/check_s3_bucket/__init__.py @@ -1,82 +1,120 @@ #!/usr/bin/env python -from __future__ import (absolute_import, division, print_function, - unicode_literals) +"""Check that a filename matching the regex was added to the bucket in the +given time window.""" +from __future__ import ( + absolute_import, + division, + print_function, + unicode_literals, +) import datetime import re import argparse + try: import botocore.session except ImportError: - print('Failed to import botocore.') + print("Failed to import botocore.") exit(3) try: from dateutil.tz import tzlocal except ImportError: - print('Failed to import dateutil.') + print("Failed to import dateutil.") exit(3) +__version__ = "0.1.0" + -def getFileList(bucket): +def get_file_list(bucket): + """Return a list of files in the S3 bucket.""" session = botocore.session.get_session() - s3client = session.create_client('s3') + s3client = session.create_client("s3") # I'm not concerened with the limitation of number of keys in the # response as the buckets have a lifecycle rule enabled and files are # automatically moved of the bucket. response = s3client.list_objects(Bucket=bucket) - return response['Contents'] + return response["Contents"] def main(): + """Main entrypoint.""" parser = argparse.ArgumentParser( - description='''Check that a filename matching the regex was added to the - bucket in the given time window.''') - parser.add_argument('bucket', help='S3 bucket to check') - parser.add_argument('regex', - help='Filename regex to check (defaults to *)', - nargs='?', - default='*') - parser.add_argument('warning_threshold', - help='Warning threshold in hours (defaults to 25)', - default=24, - type=int, - nargs='?') - parser.add_argument('critical_threshold', - help='Critical threshold in hours (defaults to 49)', - default=48, - type=int, - nargs='?') + description="""Check that a filename matching the regex was added to the + bucket in the given time window.""" + ) + parser.add_argument("bucket", help="S3 bucket to check") + parser.add_argument( + "regex", + help="Filename regex to check (defaults to *)", + nargs="?", + default="*", + ) + parser.add_argument( + "warning_threshold", + help="Warning threshold in hours (defaults to 25)", + default=24, + type=int, + nargs="?", + ) + parser.add_argument( + "critical_threshold", + help="Critical threshold in hours (defaults to 49)", + default=48, + type=int, + nargs="?", + ) args = parser.parse_args() try: - filelist = getFileList(args.bucket) - except BaseException as e: - print('Failed to list file in bucket.') + filelist = get_file_list(args.bucket) + # pylint: disable=broad-except + except BaseException as exception: + assert exception + print("Failed to list files in bucket.") exit(3) - if args.regex != '*': - p = re.compile(args.regex) - filelist = filter(lambda x: p.search(x['Key']) is not None, filelist) - if len(filelist) == 0: - print('No files matching "{}" found in {}.'.format(args.regex, - args.bucket)) + if args.regex != "*": + regex = re.compile(args.regex) + filelist = filter( + lambda x: regex.search(x["Key"]) is not None, filelist + ) + if not filelist: + print( + 'No files matching "{}" found in {}.'.format( + args.regex, args.bucket + ) + ) exit(1) now = datetime.datetime.now(tz=tzlocal()) - LastModifiedDeltas = map( - lambda x: int((now - x['LastModified']).total_seconds() / 3600), - filelist) + # pylint: disable=invalid-name + LastModifiedDeltas = list( + map( + lambda x: int((now - x["LastModified"]).total_seconds() / 3600), + filelist, + ) + ) LastModifiedDeltas.sort() delta = LastModifiedDeltas[0] if delta >= args.critical_threshold: - print('Last file modified is older than {} hours.'.format( - args.critical_threshold)) + print( + "Last file modified is older than {} hours.".format( + args.critical_threshold + ) + ) exit(2) elif delta >= args.warning_threshold: - print('Last file modified is older than {} hours.'.format( - args.warning_threshold)) + print( + "Last file modified is older than {} hours.".format( + args.warning_threshold + ) + ) exit(1) else: - print('Last file modified is newer than {} hours.'.format( - args.warning_threshold)) + print( + "Last file modified is newer than {} hours.".format( + args.warning_threshold + ) + ) exit(0) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/fabfile.py b/fabfile.py deleted file mode 100644 index 85079b4..0000000 --- a/fabfile.py +++ /dev/null @@ -1,26 +0,0 @@ -from __future__ import (absolute_import, division, print_function, - unicode_literals) -from fabric.api import (local, task, sudo, env, settings) - -env.use_ssh_config = True - - -@task -def build(): - '''Build wheel.''' - local('''python setup.py sdist bdist_wheel''') - - -@task -def clean(): - '''Clean cache, build files.''' - local('''rm -rf *.egg-info build dist''') - local('''find -name *.pyc -delete''') - local('''find -name __pycache__ -type d -delete''') - - -@task -def upload(): - '''Build and upload to PyPI.''' - build() - local('''twine upload -s dist/*''') diff --git a/setup.cfg b/setup.cfg index a26fcc2..dc2c01f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,15 @@ [bdist_wheel] -universal=1 +universal = 1 [flake8] exclude = .tox,*.egg,build,data select = E,W,F + +[bumpversion] +current_version = 0.1.0 +commit = True +tag = True + +[bumpversion:file:setup.py] + +[bumpversion:file:check_s3_bucket/__init__.py] diff --git a/setup.py b/setup.py index 81ef96d..9a63736 100644 --- a/setup.py +++ b/setup.py @@ -1,27 +1,32 @@ #!/usr/bin/env python +# pylint: disable=missing-docstring from setuptools import setup, find_packages -setup(name='check_s3_bucket', - version=open('VERSION', 'r').read(), - description='''Check that a filename matching the regex was added to the - bucket in the given time window.''', - long_description=open('README.rst', 'r').read(), - url='https://www.shore.co.il/git/check_s3_bucket', - author='Nimrod Adar', - author_email='nimrod@shore.co.il', - license='MIT', - classifiers=[ - 'Development Status :: 4 - Beta', - 'Intended Audience :: System Administrators', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 2', 'Topic :: Utilities', - 'License :: OSI Approved :: MIT License' - ], - keywords='nagios s3 aws monitoring', - packages=find_packages(), - install_requires=['python-dateutil', 'botocore'], - entry_points={ - 'console_scripts': [ - 'check_s3_bucket=check_s3_bucket:main' - ], - }, ) +setup( + name="check_s3_bucket", + version="0.1.0", + description="""Check that a filename matching the regex was added to the + bucket in the given time window.""", + long_description=open("README.rst", "r").read(), + url="https://www.shore.co.il/git/check_s3_bucket", + author="Nimrod Adar", + author_email="nimrod@shore.co.il", + license="MIT", + classifiers=[ + "Development Status :: 4 - Beta", + "Intended Audience :: System Administrators", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.4", + "Programming Language :: Python :: 3.5", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 2", + "Programming Language :: Python :: 2.7", + "Topic :: Utilities", + "License :: OSI Approved :: MIT License", + ], + keywords="nagios s3 aws monitoring", + packages=find_packages(), + install_requires=["python-dateutil", "botocore"], + entry_points={"console_scripts": ["check_s3_bucket=check_s3_bucket:main"]}, +) -- GitLab