PK,学G\{tロ@@kio/__main__.pyfrom kio.cli import main if __name__ == '__main__': main() PKf詣Gンwオアkio/__init__.py__version__ = '0.1.11' PK,学Gロ%フt kio/time.pyimport datetime import re TIME_UNITS = { 's': 'seconds', 'm': 'minutes', 'h': 'hours', 'd': 'days', 'w': 'weeks', } TIME_PATTERN = \ re.compile(r""" (?x) ^ (?: (?P [+-]? \d+ ) (?P [smhdw] ) | (?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,学G碓,牋ョ kio/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,学G腱[## kio/cli.pyimport datetime import click import json import time import zign.api from clickclick import AliasedGroup, print_table, OutputFormat, Action import kio import stups_cli.config from kio.api import request, session from kio.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('Kio CLI {}'.format(kio.__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('kio') def get_token(scopes=None): try: token = zign.api.get_token('kio', scopes or ['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') def get_url(config: dict): url = config.get('url') if not url: raise click.ClickException('Missing configuration URL. Please run "stups configure".') return url @cli.group(cls=AliasedGroup, invoke_without_command=True) @output_option @click.option('-s', '--since') @click.option('-t', '--team', help='Filter by team') @click.option('-a', '--all', is_flag=True, help='List all applications (also disabled)') @click.option('-l', '--limit', help='Limit number of results', type=int, default=20) @click.pass_context def applications(ctx, output, since, team, limit, **kwargs): '''Show applications''' if ctx.invoked_subcommand: return config = ctx.obj url = get_url(config) token = get_token() since_str = parse_since(since) if since else '' params = {} r = request(url, '/apps', token, params=params) r.raise_for_status() data = r.json() rows = [] for row in data: if not row['active'] and not kwargs['all']: continue if team and row['team_id'] != team: continue if row['last_modified'] < since_str: continue row['last_modified_time'] = parse_time(row['last_modified']) rows.append(row) # we get the newest violations first, but we want to print them in order rows.sort(key=lambda r: r['id']) with OutputFormat(output): print_table(['id', 'team_id', 'name', 'subtitle', 'last_modified_time'], rows, titles={'last_modified_time': 'Modified'}, max_column_widths={'name': 32, 'subtitle': 32}) @applications.command('print') @click.pass_obj @click.argument('application_id') def print_app(config, application_id): url = get_url(config) token = get_token() r = request(url, '/apps/{}'.format(application_id), token) r.raise_for_status() print(r.json()) @cli.group(cls=AliasedGroup) def versions(): '''Manage application versions''' pass @versions.command('list') @output_option @click.argument('application_id') @click.option('-s', '--since', default='60d') @click.pass_obj def list_versions(config, application_id, output, since): '''Show application versions''' url = get_url(config) token = get_token() since_str = parse_since(since) params = {} r = request(url, '/apps/{}/versions'.format(application_id), token, params=params) r.raise_for_status() data = r.json() rows = [] for row in data: if row['last_modified'] < since_str: continue r = request(url, '/apps/{}/versions/{}/approvals'.format(application_id, row['id']), token) row['approvals'] = ', '.join(['{}: {}'.format(x['approval_type'], x['user_id']) for x in r.json()]) row['last_modified_time'] = parse_time(row['last_modified']) rows.append(row) # we get the newest violations first, but we want to print them in order rows.sort(key=lambda r: r['last_modified_time']) with OutputFormat(output): print_table(['application_id', 'id', 'artifact', 'approvals', 'last_modified_time'], rows, titles={'last_modified_time': 'Modified'}) @versions.command('create') @click.argument('application_id') @click.argument('version') @click.argument('artifact') @click.option('-m', '--notes', help='Notes', default='') @click.pass_obj def create_version(config, application_id, version, artifact, notes): '''Create a new application version''' url = get_url(config) token = get_token(['uid', 'application.write']) data = {'artifact': artifact, 'notes': notes} with Action('Creating version {} {}..'.format(application_id, version)): r = session.put('{}/apps/{}/versions/{}'.format(url, application_id, version), headers={'Authorization': 'Bearer {}'.format(token), 'Content-Type': 'application/json'}, timeout=10, data=json.dumps(data)) r.raise_for_status() @versions.command('approve') @click.argument('application_id') @click.argument('version') @click.option('-t', '--approval-type', help='Approval type', default='DEPLOY') @click.option('-m', '--notes', help='Notes', default='') @click.pass_obj def approve_version(config, application_id, version, approval_type, notes): '''Approve application version''' url = get_url(config) token = get_token() data = {'approval_type': approval_type, 'notes': notes} with Action('Approving version {} {}..'.format(application_id, version)): r = session.post('{}/apps/{}/versions/{}/approvals'.format(url, application_id, version), headers={'Authorization': 'Bearer {}'.format(token), 'Content-Type': 'application/json'}, timeout=10, data=json.dumps(data)) r.raise_for_status() @versions.command('show') @output_option @click.argument('application_id') @click.argument('version') @click.pass_obj def show_version(config, application_id, version, output): '''Show version details''' url = get_url(config) token = get_token() r = request(url, '/apps/{}/versions/{}'.format(application_id, version), token) r.raise_for_status() rows = [{'key': k, 'value': v} for k, v in sorted(r.json().items())] r = request(url, '/apps/{}/versions/{}/approvals'.format(application_id, version), token) r.raise_for_status() for approval in r.json(): txt = '{approval_type} by {user_id} on {approved_at}'.format(**approval) rows.append({'key': 'approvals', 'value': txt}) with OutputFormat(output): print_table(['key', 'value'], rows) def main(): cli() PKg詣Gウf。*stups_kio-0.1.11.dist-info/DESCRIPTION.rst======= Kio CLI ======= .. image:: https://travis-ci.org/zalando-stups/kio-cli.svg?branch=master :target: https://travis-ci.org/zalando-stups/kio-cli :alt: Build Status .. image:: https://coveralls.io/repos/zalando-stups/kio-cli/badge.svg :target: https://coveralls.io/r/zalando-stups/kio-cli :alt: Code Coverage .. image:: https://img.shields.io/pypi/dw/stups-kio.svg :target: https://pypi.python.org/pypi/stups-kio/ :alt: PyPI Downloads .. image:: https://img.shields.io/pypi/v/stups-kio.svg :target: https://pypi.python.org/pypi/stups-kio/ :alt: Latest PyPI version .. image:: https://img.shields.io/pypi/l/stups-kio.svg :target: https://pypi.python.org/pypi/stups-kio/ :alt: License Convenience command line tool for Kio application registry. .. code-block:: bash $ sudo pip3 install --upgrade stups-kio Usage ===== .. code-block:: bash $ kio app You can also run it locally from source: .. code-block:: bash $ python3 -m kio Running Unit Tests ================== .. code-block:: bash $ python3 setup.py test --cov-html=true Releasing ========= .. code-block:: bash $ ./release.sh PKg詣Gor&&+stups_kio-0.1.11.dist-info/entry_points.txt[console_scripts] kio = kio.cli:main PKg詣GX2(stups_kio-0.1.11.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": {"kio": "kio.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/kio-cli"}}, "python.exports": {"console_scripts": {"kio": "kio.cli:main"}}}, "extras": [], "generator": "bdist_wheel (0.26.0)", "keywords": ["stups", "kio", "application", "registry"], "license": "Apache License 2.0", "metadata_version": "2.0", "name": "stups-kio", "run_requires": [{"requires": ["clickclick (>=0.9)", "requests", "stups-cli-support (>=0.7)", "stups-zign (>=1.0.10)"]}], "summary": "Simple command line utility to manage Kio applications and application versions", "test_requires": [{"requires": ["pytest", "pytest-cov"]}], "version": "0.1.11"}PKg詣Gヌチユ(stups_kio-0.1.11.dist-info/top_level.txtkio PKg詣G}タぞ\\ stups_kio-0.1.11.dist-info/WHEELWheel-Version: 1.0 Generator: bdist_wheel (0.26.0) Root-Is-Purelib: true Tag: py3-none-any PKg詣Gタ]ツ99#stups_kio-0.1.11.dist-info/METADATAMetadata-Version: 2.0 Name: stups-kio Version: 0.1.11 Summary: Simple command line utility to manage Kio applications and application versions Home-page: https://github.com/zalando-stups/kio-cli Author: Henning Jacobs Author-email: henning.jacobs@zalando.de License: Apache License 2.0 Keywords: stups kio application registry 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 (>=0.7) Requires-Dist: stups-zign (>=1.0.10) ======= Kio CLI ======= .. image:: https://travis-ci.org/zalando-stups/kio-cli.svg?branch=master :target: https://travis-ci.org/zalando-stups/kio-cli :alt: Build Status .. image:: https://coveralls.io/repos/zalando-stups/kio-cli/badge.svg :target: https://coveralls.io/r/zalando-stups/kio-cli :alt: Code Coverage .. image:: https://img.shields.io/pypi/dw/stups-kio.svg :target: https://pypi.python.org/pypi/stups-kio/ :alt: PyPI Downloads .. image:: https://img.shields.io/pypi/v/stups-kio.svg :target: https://pypi.python.org/pypi/stups-kio/ :alt: Latest PyPI version .. image:: https://img.shields.io/pypi/l/stups-kio.svg :target: https://pypi.python.org/pypi/stups-kio/ :alt: License Convenience command line tool for Kio application registry. .. code-block:: bash $ sudo pip3 install --upgrade stups-kio Usage ===== .. code-block:: bash $ kio app You can also run it locally from source: .. code-block:: bash $ python3 -m kio Running Unit Tests ================== .. code-block:: bash $ python3 setup.py test --cov-html=true Releasing ========= .. code-block:: bash $ ./release.sh PKg詣Gm趨カシシ!stups_kio-0.1.11.dist-info/RECORDkio/__init__.py,sha256=pcQGPRp7RzLI5LdLr7oqGpfTXVf4n0nfR49AeX_SD8g,23 kio/__main__.py,sha256=KmmBGz6dU4rmidxwMLEszq2Wo_LJmllrlt2DlF2U-TI,64 kio/api.py,sha256=B0tl15CvST6KEnpj6SJ_1Xv2GzTOdERVoDBhk6qYaGs,430 kio/cli.py,sha256=i5uwK7dP6TH9WoeYVGfmmXTBG07k4gXNi6GNUtytkhk,7459 kio/time.py,sha256=Xr13Tfik50sWO1oxpzbZJyxCSRrJXde2kXIYlHsXY80,1908 stups_kio-0.1.11.dist-info/DESCRIPTION.rst,sha256=hXaDx91WmCisqQv-WUBVONTdjx2fKQDVVgDtHDHnUCc,1171 stups_kio-0.1.11.dist-info/METADATA,sha256=PRdE8d1kXbYGi5ZNBRCwmYJG76waMT9zxedVWphdEwE,2105 stups_kio-0.1.11.dist-info/RECORD,, stups_kio-0.1.11.dist-info/WHEEL,sha256=zX7PHtH_7K-lEzyK75et0UBa3Bj8egCBMXe1M4gc6SU,92 stups_kio-0.1.11.dist-info/entry_points.txt,sha256=wKfROwQ-D2bPyCyGU7uPzQd4Va6eYMTRv4o4scXW6Yc,38 stups_kio-0.1.11.dist-info/metadata.json,sha256=hS2_Fy308CZfy5oH2FHZ6vfoqXWBZHFcdsjVV9A9ZTY,1257 stups_kio-0.1.11.dist-info/top_level.txt,sha256=msxloqPfb5KyC4_KGJtuOhEG5OcwT5h0jxTt9DdFSCU,4 PK,学G\{tロ@@kio/__main__.pyPKf詣Gンwオアmkio/__init__.pyPK,学Gロ%フt アkio/time.pyPK,学G碓,牋ョ Nkio/api.pyPK,学G腱[## $ kio/cli.pyPKg詣Gウf。*o'stups_kio-0.1.11.dist-info/DESCRIPTION.rstPKg詣Gor&&+J,stups_kio-0.1.11.dist-info/entry_points.txtPKg詣GX2(ケ,stups_kio-0.1.11.dist-info/metadata.jsonPKg詣Gヌチユ(1stups_kio-0.1.11.dist-info/top_level.txtPKg詣G}タぞ\\ 22stups_kio-0.1.11.dist-info/WHEELPKg詣Gタ]ツ99#フ2stups_kio-0.1.11.dist-info/METADATAPKg詣Gm趨カシシ!F;stups_kio-0.1.11.dist-info/RECORDPK nA?