зеркало из
1
0
Форкнуть 0
- Update version schema and add version command
- Enable user to limit results during run query
- Enable user to query runs belongs to her
This commit is contained in:
Troy Dai 2018-03-07 14:46:48 -08:00
Родитель 543114d8c7
Коммит f8e2878dbe
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 9608535492BEDAC8
10 изменённых файлов: 103 добавлений и 40 удалений

Просмотреть файл

@ -12,4 +12,4 @@ script:
- pylint src
- export
- ./setup.py bdist_wheel
- ./publish.sh
- ./scripts/publish.sh

14
HISTORY.rst Normal file
Просмотреть файл

@ -0,0 +1,14 @@
Micrsoft ADX A01 Automation System CLI
======================================
.. :changelog:
Release History
===============
0.14
++++
* New Features: Allow specify --last and --skip while query runs to limit the return data set.
* New Features: Allow --me option while query runs to list runs belong to the current user only.

Просмотреть файл

@ -13,4 +13,5 @@ version=${version/-py3-none-any.whl/}
echo $version
az storage blob upload -c client -f $wheel_file -n archive/$wheel_file --validate-content --no-progress
az storage blob url -c client -n archive/$wheel_file -otsv | az storage blob upload -c client -f /dev/stdin -n latest --validate-content --no-progress
az storage blob url -c client -n archive/$wheel_file -otsv | tee ./blob_path
az storage blob upload -c client -f ./blob_path -n latest --validate-content --no-progress

Просмотреть файл

@ -1,20 +1,27 @@
#!/usr/bin/env python3
import os
import sys
import datetime
from setuptools import setup
def get_version() -> str:
if "TRAVIS" in os.environ:
tag = os.environ.get('TRAVIS_TAG', None)
if tag:
return tag
else:
return f'0.0.0.{os.environ["TRAVIS_BUILD_NUMBER"]}'
return f'0.0.0.{datetime.datetime.now().strftime("%Y%m%d%H%M%S")}'
ROOT_INIT = os.path.join(os.path.dirname(__file__), 'src', 'a01', '__init__.py')
VERSION = os.environ.get('TRAVIS_TAG')
try:
if VERSION:
with open(ROOT_INIT, 'w') as file_handler:
file_handler.write(f'__version__ = {VERSION}')
else:
with open(ROOT_INIT, 'r') as file_handler:
line = file_handler.readline()
VERSION = line.split('=')[1].strip()
except (ValueError, IOError):
print('Fail to pass version string.', file=sys.stderr, flush=True)
sys.exit(1)
VERSION = get_version()
with open('HISTORY.rst', 'r', encoding='utf-8') as f:
HISTORY = f.read()
CLASSIFIERS = [
'Development Status :: 4 - Beta',
@ -40,7 +47,7 @@ setup(
name='adx-automation-cli',
version=VERSION,
description='ADX Automation CLI',
long_description='Command line tools for ADX automation system',
long_description=HISTORY,
license='MIT',
author='Microsoft Corporation',
author_email='trdai@microsoft.com',

Просмотреть файл

@ -0,0 +1 @@
__version__ = '1.0.0+local'

Просмотреть файл

@ -1,3 +1,11 @@
from a01 import __version__
from a01.cli import cmd
@cmd('version', desc='Print version information')
def version() -> None:
print(__version__)
def main() -> None:
from a01.cli import setup_commands

Просмотреть файл

@ -141,6 +141,22 @@ class AuthSettings(object):
self.logger.exception(f'Fail to save the file {TOKEN_FILE}')
raise
def get_user_name(self) -> str:
"""Returns the name of current user. For a user account, it returns its user id. For a service principal, it
returns its service principal ID."""
if not self.has_login:
print('You need to login. Usage: a01 login.')
sys.exit(1)
try:
return self.user_id
except AuthenticationError:
try:
return self.service_principal_id
except AuthenticationError:
print("Fail to find user name. Tried both user id and service principal.", file=sys.stderr)
sys.exit(1)
@staticmethod
def _get_auth_context() -> adal.AuthenticationContext:
return adal.AuthenticationContext(AUTHORITY_URL, api_version=None)
@ -209,18 +225,3 @@ def whoami() -> None:
print(AuthSettings().summary)
except AuthenticationError:
print('You need to login. Usage: a01 login.')
def get_user_id() -> str:
try:
return AuthSettings().user_id
except AuthenticationError:
print('You need to login. Usage: a01 login.')
sys.exit(1)
def get_service_principal_id() -> str:
try:
return AuthSettings().service_principal_id
except AuthenticationError:
print('You need to login. Usage: a01 login.')
sys.exit(1)

Просмотреть файл

@ -66,3 +66,4 @@ class A01Config(configparser.ConfigParser): # pylint: disable=too-many-ancestor
@property
def endpoint_uri(self) -> str:
return f'https://{self.endpoint}/api'
# return 'http://127.0.0.1:5000/api'

Просмотреть файл

@ -1,6 +1,7 @@
import json
import datetime
import base64
import urllib
from typing import List, Tuple, Generator
import colorama
@ -15,10 +16,11 @@ from a01.common import get_logger, A01Config, NAMESPACE
class Run(object):
logger = get_logger('Run')
def __init__(self, name: str, settings: dict, details: dict):
def __init__(self, name: str, settings: dict, details: dict, owner: str = None):
self.name = name
self.settings = settings
self.details = details
self.owner = owner
self.id = None # pylint: disable=invalid-name
self.creation = None
@ -27,7 +29,8 @@ class Run(object):
result = {
'name': self.name,
'settings': self.settings,
'details': self.details
'details': self.details,
'owner': self.owner
}
return result
@ -71,6 +74,7 @@ class Run(object):
result = Run(name=data['name'], settings=data['settings'], details=data['details'])
result.id = data['id']
result.creation = datetime.datetime.strptime(data['creation'], '%Y-%m-%dT%H:%M:%SZ')
result.owner = data.get('owner', None)
return result
@ -91,7 +95,7 @@ class RunCollection(object):
for run in self.runs:
time = (run.creation - datetime.timedelta(hours=8)).strftime('%Y-%m-%d %H:%M PST')
remark = run.details.get('remark', None) or run.settings.get('a01.reserved.remark', '')
owner = run.details.get('creator', None) or run.details.get('a01.reserved.creator', '')
owner = run.owner or run.details.get('creator', None) or run.details.get('a01.reserved.creator', '')
row = [run.id, run.name, time, remark, owner]
if remark and remark.lower() == 'official':
@ -105,13 +109,22 @@ class RunCollection(object):
return 'Id', 'Name', 'Creation', 'Remark', 'Owner'
@classmethod
def get(cls) -> 'RunCollection':
def get(cls, **kwargs) -> 'RunCollection':
try:
resp = session.get(f'{cls.endpoint_uri()}/runs')
url = f'{cls.endpoint_uri()}/runs'
query = {}
for key, value in kwargs.items():
if value is not None:
query[key] = value
if query:
url = f'{url}?{urllib.parse.urlencode(query)}'
resp = session.get(url)
resp.raise_for_status()
runs = [Run.from_dict(each) for each in resp.json()]
runs = sorted(runs, key=lambda r: r.id)
runs = sorted(runs, key=lambda r: r.id, reverse=True)
return RunCollection(runs)
except HTTPError:

Просмотреть файл

@ -20,11 +20,12 @@ from kubernetes.client.models.v1_env_var import V1EnvVar
from kubernetes.client.models.v1_env_var_source import V1EnvVarSource
from kubernetes.client.models.v1_secret_key_selector import V1SecretKeySelector
import a01
import a01.models
from a01.common import get_logger, A01Config, COMMON_IMAGE_PULL_SECRET
from a01.cli import cmd, arg
from a01.communication import session
from a01.auth import get_user_id, get_service_principal_id
from a01.auth import AuthSettings, AuthenticationError
from a01.output import output_in_table
logger = get_logger(__name__) # pylint: disable=invalid-name
@ -33,13 +34,27 @@ logger = get_logger(__name__) # pylint: disable=invalid-name
@cmd('get runs', desc='Retrieve the runs.')
def get_runs() -> None:
@arg('owner', help='Query runs by owner.')
@arg('me', help='Query runs created by me.')
@arg('last', help='Returns the last NUMBER of records. Default: 20.')
@arg('skip', help='Returns the records after skipping given number of records at the bottom. Default: 0.')
def get_runs(me: bool = False, last: int = 20, skip: int = 0, owner: str = None) -> None: # pylint: disable=invalid-name
try:
runs = a01.models.RunCollection.get()
if me and owner:
raise ValueError('--me and --user are mutually exclusive.')
elif me:
owner = AuthSettings().get_user_name()
runs = a01.models.RunCollection.get(owner=owner, last=last, skip=skip)
output_in_table(runs.get_table_view(), headers=runs.get_table_header())
except ValueError as err:
logger.error(err)
sys.exit(1)
except AuthenticationError as err:
logger.error(err)
print('You need to login. Usage: a01 login.', file=sys.stderr)
sys.exit(1)
@cmd('get run', desc='Retrieve a run')
@ -106,8 +121,9 @@ def get_run(run_id: str, log: bool = False, recording: bool = False, recording_a
def create_run(image: str, from_failures: str = None, live: bool = False, parallelism: int = 3, query: str = None,
remark: str = '', email: bool = False, secret: str = None, mode: str = None,
reset_run: str = None) -> None:
auth = AuthSettings()
remark = remark or ''
creator = get_user_id() if email else get_service_principal_id()
creator = auth.get_user_name()
try:
if not reset_run:
@ -119,7 +135,7 @@ def create_run(image: str, from_failures: str = None, live: bool = False, parall
'a01.reserved.storageshare': 'k8slog',
'a01.reserved.testquery': query,
'a01.reserved.remark': remark,
'a01.reserved.useremail': get_user_id() if email else '',
'a01.reserved.useremail': auth.user_id if email else '',
'a01.reserved.initparallelism': parallelism,
'a01.reserved.livemode': str(live),
'a01.reserved.testmode': mode,
@ -127,8 +143,9 @@ def create_run(image: str, from_failures: str = None, live: bool = False, parall
},
details={
'a01.reserved.creator': creator,
'a01.reserved.client': 'A01 CLI'
})
'a01.reserved.client': f'CLI {a01.__version__}'
},
owner=creator)
# prune
to_delete = [k for k, v in run_model.settings.items() if not v]