PK]wG`EEfullstop/__main__.pyfrom fullstop.cli import main if __name__ == '__main__': main() PKΑwG>yu|fullstop/__init__.py__version__ = '1.0.8' PK]wG$aafullstop/time.pyimport datetime import re TIME_UNITS = { 's': 'seconds', 'm': 'minutes', 'h': 'hours', 'd': 'days', } TIME_PATTERN = \ re.compile(r""" (?x) ^ (?: (?P [+-]? \d+ ) (?P [smhd] ) | (?P \d{4} - \d{2} - \d{2} (?: [ ] \d{2} : \d{2} : \d{2} (?: \. \d+)? ) ) ) $ """ ) def normalize_time(s, default=None, past=False): """ If a time delta of the form /[+-]?\d+[smdh]/ is given, returns an ISO date string representing the current time offset by that delta. If an ISO date string is given, returns it unaltered. If another string is given and a default is specified, returns the result of normalizing the default. Assumes all ISO strings omit the "T" between date and time. >>> normalize_time('2014-06-25 16:02:52').strftime('%Y-%m-%d %H:%M:%S') '2014-06-25 16:02:52' >>> normalize_time(None, default='2014-06-25 16:02:52').strftime('%Y-%m-%d %H:%M:%S') '2014-06-25 16:02:52' >>> normalize_time('-2h').strftime('%Y-%m-%d %H:%M:%S') \ == (datetime.datetime.utcnow() - datetime.timedelta(hours=2)).strftime('%Y-%m-%d %H:%M:%S') True >>> normalize_time('foo', '-2h').strftime('%Y-%m-%d %H:%M:%S') \ == normalize_time('-2h').strftime('%Y-%m-%d %H:%M:%S') True """ matcher = TIME_PATTERN.search(s or '') if matcher: if matcher.group('isodate'): return datetime.datetime.strptime(s, '%Y-%m-%d %H:%M:%S') else: delta = datetime.timedelta(**{TIME_UNITS[matcher.group('unit')]: int(matcher.group('magnitude'))}) if past: return datetime.datetime.utcnow() - delta else: return datetime.datetime.utcnow() + delta elif default: return normalize_time(default) else: raise ValueError(s) PK]wGO,fullstop/api.pyimport requests adapter = requests.adapters.HTTPAdapter(pool_connections=10, pool_maxsize=10) session = requests.Session() session.mount('http://', adapter) session.mount('https://', adapter) def request(url, path, access_token, params=None): return session.get('{}{}'.format(url, path), headers={'Authorization': 'Bearer {}'.format(access_token)}, timeout=10, params=params) PK]wGB fullstop/cli.pyimport datetime import click import time import zign.api from clickclick import AliasedGroup, print_table, OutputFormat import fullstop import stups_cli.config from fullstop.api import request from fullstop.time import normalize_time CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help']) output_option = click.option('-o', '--output', type=click.Choice(['text', 'json', 'tsv']), default='text', help='Use alternative output format') def parse_time(s: str) -> float: ''' >>> parse_time('2015-04-14T19:09:01.000Z') > 0 True ''' try: utc = datetime.datetime.strptime(s, '%Y-%m-%dT%H:%M:%S.%fZ') ts = time.time() utc_offset = datetime.datetime.fromtimestamp(ts) - datetime.datetime.utcfromtimestamp(ts) local = utc + utc_offset return local.timestamp() except Exception as e: print(e) return None def print_version(ctx, param, value): if not value or ctx.resilient_parsing: return click.echo('Fullstop CLI {}'.format(fullstop.__version__)) ctx.exit() @click.group(cls=AliasedGroup, context_settings=CONTEXT_SETTINGS) @click.option('-V', '--version', is_flag=True, callback=print_version, expose_value=False, is_eager=True, help='Print the current version number and exit.') @click.pass_context def cli(ctx): ctx.obj = stups_cli.config.load_config('fullstop') def get_token(): try: token = zign.api.get_token('fullstop', ['uid']) except Exception as e: raise click.UsageError(str(e)) return token def parse_since(s): return normalize_time(s, past=True).strftime('%Y-%m-%dT%H:%M:%S.%fZ') @cli.command() @output_option @click.option('--accounts', metavar='ACCOUNT_IDS', help='AWS account IDs to filter for') @click.option('-s', '--since', default='1d', metavar='TIME_SPEC', help='Only show violations newer than') @click.option('--severity') @click.option('-t', '--type', metavar='VIOLATION_TYPE', help='Only show violations of given type') @click.option('-l', '--limit', metavar='N', help='Limit number of results', type=int, default=20) @click.pass_obj def violations(config, output, since, limit, **kwargs): '''Show violations''' url = config.get('url') if not url: raise click.ClickException('Missing configuration URL. Please run "stups configure".') token = get_token() params = {'size': limit, 'sort': 'id,DESC'} params['since'] = parse_since(since) params.update(kwargs) r = request(url, '/api/violations', token, params=params) r.raise_for_status() data = r.json() rows = [] for row in data['content']: row['violation_type'] = row['violation_type']['id'] row['created_time'] = parse_time(row['created']) row['meta_info'] = (row['meta_info'] or '').replace('\n', ' ') rows.append(row) # we get the newest violations first, but we want to print them in order rows.reverse() with OutputFormat(output): print_table(['account_id', 'region', 'violation_type', 'instance_id', 'meta_info', 'comment', 'created_time'], rows, titles={'created_time': 'Created'}) def main(): cli() PKΑwGi.stups_fullstop-1.0.8.dist-info/DESCRIPTION.rst============= fullstop. CLI ============= .. image:: https://travis-ci.org/zalando-stups/fullstop-cli.svg?branch=master :target: https://travis-ci.org/zalando-stups/fullstop-cli :alt: Build Status .. image:: https://coveralls.io/repos/zalando-stups/fullstop-cli/badge.svg :target: https://coveralls.io/r/zalando-stups/fullstop-cli :alt: Code Coverage .. image:: https://img.shields.io/pypi/dw/stups-fullstop.svg :target: https://pypi.python.org/pypi/stups-fullstop/ :alt: PyPI Downloads .. image:: https://img.shields.io/pypi/v/stups-fullstop.svg :target: https://pypi.python.org/pypi/stups-fullstop/ :alt: Latest PyPI version .. image:: https://img.shields.io/pypi/l/stups-fullstop.svg :target: https://pypi.python.org/pypi/stups-fullstop/ :alt: License Convenience command line tool for fullstop. audit reporting. .. code-block:: bash $ sudo pip3 install --upgrade stups-fullstop Usage ===== .. code-block:: bash $ fullstop login $ fullstop violations You can also run it locally from source: .. code-block:: bash $ python3 -m fullstop Running Unit Tests ================== .. code-block:: bash $ python3 setup.py test --cov-html=true Releasing ========= .. code-block:: bash $ ./release.sh PKΑwG;00/stups_fullstop-1.0.8.dist-info/entry_points.txt[console_scripts] fullstop = fullstop.cli:main PKΑwG,stups_fullstop-1.0.8.dist-info/metadata.json{"classifiers": ["Development Status :: 4 - Beta", "Environment :: Console", "Intended Audience :: Developers", "Intended Audience :: System Administrators", "License :: OSI Approved :: Apache Software License", "Operating System :: POSIX :: Linux", "Programming Language :: Python", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: Implementation :: CPython"], "extensions": {"python.commands": {"wrap_console": {"fullstop": "fullstop.cli:main"}}, "python.details": {"contacts": [{"email": "henning.jacobs@zalando.de", "name": "Henning Jacobs", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "https://github.com/zalando-stups/fullstop-cli"}}, "python.exports": {"console_scripts": {"fullstop": "fullstop.cli:main"}}}, "extras": [], "generator": "bdist_wheel (0.26.0)", "keywords": ["fullstop", "audit", "violations"], "license": "Apache License 2.0", "metadata_version": "2.0", "name": "stups-fullstop", "run_requires": [{"requires": ["clickclick (>=0.9)", "requests", "stups-cli-support", "stups-zign (>=1.0.10)"]}], "summary": "Simple command line utility to view Fullstop violations", "test_requires": [{"requires": ["pytest", "pytest-cov"]}], "version": "1.0.8"}PKΑwG+ ,stups_fullstop-1.0.8.dist-info/top_level.txtfullstop PKΑwG}\\$stups_fullstop-1.0.8.dist-info/WHEELWheel-Version: 1.0 Generator: bdist_wheel (0.26.0) Root-Is-Purelib: true Tag: py3-none-any PKΑwGhH'stups_fullstop-1.0.8.dist-info/METADATAMetadata-Version: 2.0 Name: stups-fullstop Version: 1.0.8 Summary: Simple command line utility to view Fullstop violations Home-page: https://github.com/zalando-stups/fullstop-cli Author: Henning Jacobs Author-email: henning.jacobs@zalando.de License: Apache License 2.0 Keywords: fullstop audit violations Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Environment :: Console Classifier: Intended Audience :: Developers Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: Implementation :: CPython Requires-Dist: clickclick (>=0.9) Requires-Dist: requests Requires-Dist: stups-cli-support Requires-Dist: stups-zign (>=1.0.10) ============= fullstop. CLI ============= .. image:: https://travis-ci.org/zalando-stups/fullstop-cli.svg?branch=master :target: https://travis-ci.org/zalando-stups/fullstop-cli :alt: Build Status .. image:: https://coveralls.io/repos/zalando-stups/fullstop-cli/badge.svg :target: https://coveralls.io/r/zalando-stups/fullstop-cli :alt: Code Coverage .. image:: https://img.shields.io/pypi/dw/stups-fullstop.svg :target: https://pypi.python.org/pypi/stups-fullstop/ :alt: PyPI Downloads .. image:: https://img.shields.io/pypi/v/stups-fullstop.svg :target: https://pypi.python.org/pypi/stups-fullstop/ :alt: Latest PyPI version .. image:: https://img.shields.io/pypi/l/stups-fullstop.svg :target: https://pypi.python.org/pypi/stups-fullstop/ :alt: License Convenience command line tool for fullstop. audit reporting. .. code-block:: bash $ sudo pip3 install --upgrade stups-fullstop Usage ===== .. code-block:: bash $ fullstop login $ fullstop violations You can also run it locally from source: .. code-block:: bash $ python3 -m fullstop Running Unit Tests ================== .. code-block:: bash $ python3 setup.py test --cov-html=true Releasing ========= .. code-block:: bash $ ./release.sh PKΑwGKr%stups_fullstop-1.0.8.dist-info/RECORDfullstop/__init__.py,sha256=mFFUUCx5TqyW1TTFRrWDhXXVMJDMRxXWrkHanVtp9oY,22 fullstop/__main__.py,sha256=rIs5v-zAdQvGgnuYOg7UFwPJ1AB_j-NddNtaxlRQMPg,69 fullstop/api.py,sha256=B0tl15CvST6KEnpj6SJ_1Xv2GzTOdERVoDBhk6qYaGs,430 fullstop/cli.py,sha256=40RFHlAAjYyGvhG27TOZCCdooQJLzkJWwqsedcPOuSA,3214 fullstop/time.py,sha256=093OueBDBs5dcPig1jXJgGN1nJ9_6erihrhvrAzFoPA,1889 stups_fullstop-1.0.8.dist-info/DESCRIPTION.rst,sha256=M-D_SJDeAoBddh6H_0Il8Qp5pp36wlkiBintSnHfxbQ,1283 stups_fullstop-1.0.8.dist-info/METADATA,sha256=LJRxtkg-SxcKryycMACFIoi5DXzxOvpAScw_ssXGNB8,2189 stups_fullstop-1.0.8.dist-info/RECORD,, stups_fullstop-1.0.8.dist-info/WHEEL,sha256=zX7PHtH_7K-lEzyK75et0UBa3Bj8egCBMXe1M4gc6SU,92 stups_fullstop-1.0.8.dist-info/entry_points.txt,sha256=E7d-9qealLGAFq-xgKm1Pbuaxn94GupGmxiGh52T_JM,48 stups_fullstop-1.0.8.dist-info/metadata.json,sha256=HuYJfQJlMCfRY5zoz6pgnTrOMpjX-gJXqL8ma7-lRBQ,1246 stups_fullstop-1.0.8.dist-info/top_level.txt,sha256=kREhEBbafKmd5MlU3EdEsv3ue3fWdq65sHhhiZERjro,9 PK]wG`EEfullstop/__main__.pyPKΑwG>yu|wfullstop/__init__.pyPK]wG$aafullstop/time.pyPK]wGO,Nfullstop/api.pyPK]wGB ) fullstop/cli.pyPKΑwGi.stups_fullstop-1.0.8.dist-info/DESCRIPTION.rstPKΑwG;00/3stups_fullstop-1.0.8.dist-info/entry_points.txtPKΑwG,stups_fullstop-1.0.8.dist-info/metadata.jsonPKΑwG+ ,!stups_fullstop-1.0.8.dist-info/top_level.txtPKΑwG}\\$+"stups_fullstop-1.0.8.dist-info/WHEELPKΑwGhH'"stups_fullstop-1.0.8.dist-info/METADATAPKΑwGKr%+stups_fullstop-1.0.8.dist-info/RECORDPK /