зеркало из https://github.com/mozilla/gecko-dev.git
Backed out 4 changesets (bug 1508381) for multiple Windows build bustages CLOSED TREE
Backed out changeset f01cec6f712e (bug 1508381) Backed out changeset ba69e59924de (bug 1508381) Backed out changeset 97fe4e5a665e (bug 1508381) Backed out changeset 0c3065c12bef (bug 1508381)
This commit is contained in:
Родитель
0e57cd7404
Коммит
c152ccec1d
|
@ -10,8 +10,6 @@ import os
|
||||||
import re
|
import re
|
||||||
import requests
|
import requests
|
||||||
from taskcluster.notify import Notify
|
from taskcluster.notify import Notify
|
||||||
from taskcluster import optionsFromEnvironment
|
|
||||||
from taskgraph.util.taskcluster import get_root_url
|
|
||||||
from operator import itemgetter
|
from operator import itemgetter
|
||||||
|
|
||||||
from mozilla_version.gecko import GeckoVersion
|
from mozilla_version.gecko import GeckoVersion
|
||||||
|
@ -218,9 +216,11 @@ Task group: [{task_group_id}](https://tools.taskcluster.net/groups/{task_group_i
|
||||||
|
|
||||||
subject = '{} Build of {} {} build {}'.format(subject_prefix, product, version, build_number)
|
subject = '{} Build of {} {} build {}'.format(subject_prefix, product, version, build_number)
|
||||||
|
|
||||||
notify_options = optionsFromEnvironment({'rootUrl': get_root_url()})
|
notify_options = {}
|
||||||
if 'TASKCLUSTER_PROXY_URL' in os.environ:
|
if 'TASKCLUSTER_PROXY_URL' in os.environ:
|
||||||
notify_options['rootUrl'] = os.environ['TASKCLUSTER_PROXY_URL']
|
# Until bug 1460015 is finished, use the old baseUrl style of proxy URL
|
||||||
|
base_url = os.environ['TASKCLUSTER_PROXY_URL'].rstrip('/')
|
||||||
|
notify_options['baseUrl'] = '{}/notify/v1'.format(base_url)
|
||||||
notify = Notify(notify_options)
|
notify = Notify(notify_options)
|
||||||
for address in addresses:
|
for address in addresses:
|
||||||
notify.email({
|
notify.email({
|
||||||
|
|
|
@ -33,7 +33,8 @@ then
|
||||||
test "${AWS_BUCKET_NAME}"
|
test "${AWS_BUCKET_NAME}"
|
||||||
|
|
||||||
set +x # Don't echo these.
|
set +x # Don't echo these.
|
||||||
secret_url="${TASKCLUSTER_PROXY_URL}/api/auth/v1/aws/s3/read-write/${AWS_BUCKET_NAME}/${S3_PATH}"
|
# Until bug 1460015 is finished, use baseUrl-style proxy URLs
|
||||||
|
secret_url="${TASKCLUSTER_PROXY_URL}/auth/v1/aws/s3/read-write/${AWS_BUCKET_NAME}/${S3_PATH}"
|
||||||
AUTH=$(curl "${secret_url}")
|
AUTH=$(curl "${secret_url}")
|
||||||
AWS_ACCESS_KEY_ID=$(echo "${AUTH}" | jq -r '.credentials.accessKeyId')
|
AWS_ACCESS_KEY_ID=$(echo "${AUTH}" | jq -r '.credentials.accessKeyId')
|
||||||
AWS_SECRET_ACCESS_KEY=$(echo "${AUTH}" | jq -r '.credentials.secretAccessKey')
|
AWS_SECRET_ACCESS_KEY=$(echo "${AUTH}" | jq -r '.credentials.secretAccessKey')
|
||||||
|
|
|
@ -68,7 +68,8 @@ fi
|
||||||
if [ -n "${ARC_SECRET}" ] && getent hosts taskcluster
|
if [ -n "${ARC_SECRET}" ] && getent hosts taskcluster
|
||||||
then
|
then
|
||||||
set +x # Don't echo these
|
set +x # Don't echo these
|
||||||
secrets_url="${TASKCLUSTER_PROXY_URL}/api/secrets/v1/secret/${ARC_SECRET}"
|
# Until bug 1460015 is finished, use baseUrl-style proxy URLs
|
||||||
|
secrets_url="${TASKCLUSTER_PROXY_URL}/secrets/v1/secret/${ARC_SECRET}"
|
||||||
SECRET=$(curl "${secrets_url}")
|
SECRET=$(curl "${secrets_url}")
|
||||||
TOKEN=$(echo "${SECRET}" | jq -r '.secret.token')
|
TOKEN=$(echo "${SECRET}" | jq -r '.secret.token')
|
||||||
elif [ -n "${ARC_TOKEN}" ] # Allow for local testing.
|
elif [ -n "${ARC_TOKEN}" ] # Allow for local testing.
|
||||||
|
|
|
@ -36,7 +36,8 @@ fi
|
||||||
if [ -n "${ARC_SECRET}" ] && getent hosts taskcluster
|
if [ -n "${ARC_SECRET}" ] && getent hosts taskcluster
|
||||||
then
|
then
|
||||||
set +x # Don't echo these
|
set +x # Don't echo these
|
||||||
secrets_url="${TASKCLUSTER_PROXY_URL}/api/secrets/v1/secret/${ARC_SECRET}"
|
# Until bug 1460015 is finished, use the old, baseUrl-style proxy URLs
|
||||||
|
secrets_url="${TASKCLUSTER_PROXY_URL}/secrets/v1/secret/${ARC_SECRET}"
|
||||||
SECRET=$(curl "${secrets_url}")
|
SECRET=$(curl "${secrets_url}")
|
||||||
TOKEN=$(echo "${SECRET}" | jq -r '.secret.token')
|
TOKEN=$(echo "${SECRET}" | jq -r '.secret.token')
|
||||||
elif [ -n "${ARC_TOKEN}" ] # Allow for local testing.
|
elif [ -n "${ARC_TOKEN}" ] # Allow for local testing.
|
||||||
|
|
|
@ -396,7 +396,8 @@ def command_task_artifacts(args):
|
||||||
task=fetch['task'], artifact=fetch['artifact'])
|
task=fetch['task'], artifact=fetch['artifact'])
|
||||||
url = api(root_url, 'queue', 'v1', path)
|
url = api(root_url, 'queue', 'v1', path)
|
||||||
else:
|
else:
|
||||||
url = ('{proxy_url}/api/queue/v1/task/{task}/artifacts/{artifact}').format(
|
# Until bug 1460015 is finished, use the old baseUrl style proxy URLs
|
||||||
|
url = ('{proxy_url}/queue/v1/task/{task}/artifacts/{artifact}').format(
|
||||||
proxy_url=os.environ['TASKCLUSTER_PROXY_URL'],
|
proxy_url=os.environ['TASKCLUSTER_PROXY_URL'],
|
||||||
task=fetch['task'],
|
task=fetch['task'],
|
||||||
artifact=fetch['artifact'])
|
artifact=fetch['artifact'])
|
||||||
|
|
|
@ -21,6 +21,7 @@ from taskgraph.util.attributes import TRUNK_PROJECTS
|
||||||
from taskgraph.util.hash import hash_path
|
from taskgraph.util.hash import hash_path
|
||||||
from taskgraph.util.treeherder import split_symbol
|
from taskgraph.util.treeherder import split_symbol
|
||||||
from taskgraph.transforms.base import TransformSequence
|
from taskgraph.transforms.base import TransformSequence
|
||||||
|
from taskgraph.util.taskcluster import get_root_url
|
||||||
from taskgraph.util.schema import (
|
from taskgraph.util.schema import (
|
||||||
validate_schema,
|
validate_schema,
|
||||||
Schema,
|
Schema,
|
||||||
|
@ -492,6 +493,11 @@ def build_docker_worker_payload(config, task, task_def):
|
||||||
else:
|
else:
|
||||||
raise Exception("unknown docker image type")
|
raise Exception("unknown docker image type")
|
||||||
|
|
||||||
|
# propagate our TASKCLUSTER_ROOT_URL to the task; note that this will soon
|
||||||
|
# be provided directly by the worker, making this redundant:
|
||||||
|
# https://bugzilla.mozilla.org/show_bug.cgi?id=1460015
|
||||||
|
worker['env']['TASKCLUSTER_ROOT_URL'] = get_root_url()
|
||||||
|
|
||||||
features = {}
|
features = {}
|
||||||
|
|
||||||
if worker.get('relengapi-proxy'):
|
if worker.get('relengapi-proxy'):
|
||||||
|
@ -499,6 +505,7 @@ def build_docker_worker_payload(config, task, task_def):
|
||||||
|
|
||||||
if worker.get('taskcluster-proxy'):
|
if worker.get('taskcluster-proxy'):
|
||||||
features['taskclusterProxy'] = True
|
features['taskclusterProxy'] = True
|
||||||
|
worker['env']['TASKCLUSTER_PROXY_URL'] = 'http://taskcluster/'
|
||||||
|
|
||||||
if worker.get('allow-ptrace'):
|
if worker.get('allow-ptrace'):
|
||||||
features['allowPtrace'] = True
|
features['allowPtrace'] = True
|
||||||
|
@ -523,6 +530,11 @@ def build_docker_worker_payload(config, task, task_def):
|
||||||
else:
|
else:
|
||||||
worker['env']['SCCACHE_DISABLE'] = '1'
|
worker['env']['SCCACHE_DISABLE'] = '1'
|
||||||
|
|
||||||
|
# this will soon be provided directly by the worker:
|
||||||
|
# https://bugzilla.mozilla.org/show_bug.cgi?id=1460015
|
||||||
|
if features.get('taskclusterProxy'):
|
||||||
|
worker['env']['TASKCLUSTER_PROXY_URL'] = 'http://taskcluster'
|
||||||
|
|
||||||
capabilities = {}
|
capabilities = {}
|
||||||
|
|
||||||
for lo in 'audio', 'video':
|
for lo in 'audio', 'video':
|
||||||
|
@ -755,6 +767,11 @@ def build_generic_worker_payload(config, task, task_def):
|
||||||
|
|
||||||
env = worker.get('env', {})
|
env = worker.get('env', {})
|
||||||
|
|
||||||
|
# propagate our TASKCLUSTER_ROOT_URL to the task; note that this will soon
|
||||||
|
# be provided directly by the worker, making this redundant:
|
||||||
|
# https://bugzilla.mozilla.org/show_bug.cgi?id=1460015
|
||||||
|
env['TASKCLUSTER_ROOT_URL'] = get_root_url()
|
||||||
|
|
||||||
if task.get('needs-sccache'):
|
if task.get('needs-sccache'):
|
||||||
env['USE_SCCACHE'] = '1'
|
env['USE_SCCACHE'] = '1'
|
||||||
# Disable sccache idle shutdown.
|
# Disable sccache idle shutdown.
|
||||||
|
@ -809,6 +826,9 @@ def build_generic_worker_payload(config, task, task_def):
|
||||||
|
|
||||||
if worker.get('taskcluster-proxy'):
|
if worker.get('taskcluster-proxy'):
|
||||||
features['taskclusterProxy'] = True
|
features['taskclusterProxy'] = True
|
||||||
|
# this will soon be provided directly by the worker:
|
||||||
|
# https://bugzilla.mozilla.org/show_bug.cgi?id=1460015
|
||||||
|
worker['env']['TASKCLUSTER_PROXY_URL'] = 'http://taskcluster'
|
||||||
|
|
||||||
if worker.get('run-as-administrator', False):
|
if worker.get('run-as-administrator', False):
|
||||||
features['runAsAdministrator'] = True
|
features['runAsAdministrator'] = True
|
||||||
|
@ -1307,6 +1327,10 @@ def build_always_optimized_payload(config, task, task_def):
|
||||||
def build_macosx_engine_payload(config, task, task_def):
|
def build_macosx_engine_payload(config, task, task_def):
|
||||||
worker = task['worker']
|
worker = task['worker']
|
||||||
|
|
||||||
|
# propagate our TASKCLUSTER_ROOT_URL to the task; note that this will soon
|
||||||
|
# be provided directly by the worker, making this redundant
|
||||||
|
worker.setdefault('env', {})['TASKCLUSTER_ROOT_URL'] = get_root_url()
|
||||||
|
|
||||||
artifacts = map(lambda artifact: {
|
artifacts = map(lambda artifact: {
|
||||||
'name': artifact['name'],
|
'name': artifact['name'],
|
||||||
'path': artifact['path'],
|
'path': artifact['path'],
|
||||||
|
|
|
@ -31,23 +31,10 @@ CONCURRENCY = 50
|
||||||
|
|
||||||
|
|
||||||
@memoize
|
@memoize
|
||||||
def get_root_url(use_proxy=False):
|
def get_root_url():
|
||||||
"""Get the current TASKCLUSTER_ROOT_URL. When running in a task, this must
|
"""Get the current TASKCLUSTER_ROOT_URL. When running in a task, this must
|
||||||
come from $TASKCLUSTER_ROOT_URL; when run on the command line, we apply a
|
come from $TASKCLUSTER_ROOT_URL; when run on the command line, we apply a
|
||||||
defualt that points to the production deployment of Taskcluster. If use_proxy
|
defualt that points to the production deployment of Taskcluster."""
|
||||||
is set, this attempts to get TASKCLUSTER_PROXY_URL instead, failing if it
|
|
||||||
is not set."""
|
|
||||||
if use_proxy:
|
|
||||||
try:
|
|
||||||
return os.environ['TASKCLUSTER_PROXY_URL']
|
|
||||||
except KeyError:
|
|
||||||
if 'TASK_ID' not in os.environ:
|
|
||||||
raise RuntimeError(
|
|
||||||
'taskcluster-proxy is not available when not executing in a task')
|
|
||||||
else:
|
|
||||||
raise RuntimeError(
|
|
||||||
'taskcluster-proxy is not enabled for this task')
|
|
||||||
|
|
||||||
if 'TASKCLUSTER_ROOT_URL' not in os.environ:
|
if 'TASKCLUSTER_ROOT_URL' not in os.environ:
|
||||||
if 'TASK_ID' in os.environ:
|
if 'TASK_ID' in os.environ:
|
||||||
raise RuntimeError('$TASKCLUSTER_ROOT_URL must be set when running in a task')
|
raise RuntimeError('$TASKCLUSTER_ROOT_URL must be set when running in a task')
|
||||||
|
@ -154,7 +141,11 @@ def get_artifact_path(task, path):
|
||||||
|
|
||||||
|
|
||||||
def get_index_url(index_path, use_proxy=False, multiple=False):
|
def get_index_url(index_path, use_proxy=False, multiple=False):
|
||||||
index_tmpl = liburls.api(get_root_url(use_proxy), 'index', 'v1', 'task{}/{}')
|
if use_proxy:
|
||||||
|
# Until bug 1460015 is finished, use the old baseUrl style of proxy URL
|
||||||
|
index_tmpl = os.environ['TASKCLUSTER_PROXY_URL'] + '/index/v1/task{}/{}'
|
||||||
|
else:
|
||||||
|
index_tmpl = liburls.api(get_root_url(), 'index', 'v1', 'task{}/{}')
|
||||||
return index_tmpl.format('s' if multiple else '', index_path)
|
return index_tmpl.format('s' if multiple else '', index_path)
|
||||||
|
|
||||||
|
|
||||||
|
@ -204,7 +195,11 @@ def parse_time(timestamp):
|
||||||
|
|
||||||
|
|
||||||
def get_task_url(task_id, use_proxy=False):
|
def get_task_url(task_id, use_proxy=False):
|
||||||
task_tmpl = liburls.api(get_root_url(use_proxy), 'queue', 'v1', 'task/{}')
|
if use_proxy:
|
||||||
|
# Until bug 1460015 is finished, use the old baseUrl style of proxy URL
|
||||||
|
task_tmpl = os.environ['TASKCLUSTER_PROXY_URL'] + '/queue/v1/task/{}'
|
||||||
|
else:
|
||||||
|
task_tmpl = liburls.api(get_root_url(), 'queue', 'v1', 'task/{}')
|
||||||
return task_tmpl.format(task_id)
|
return task_tmpl.format(task_id)
|
||||||
|
|
||||||
|
|
||||||
|
@ -245,13 +240,17 @@ def rerun_task(task_id):
|
||||||
def get_current_scopes():
|
def get_current_scopes():
|
||||||
"""Get the current scopes. This only makes sense in a task with the Taskcluster
|
"""Get the current scopes. This only makes sense in a task with the Taskcluster
|
||||||
proxy enabled, where it returns the actual scopes accorded to the task."""
|
proxy enabled, where it returns the actual scopes accorded to the task."""
|
||||||
auth_url = liburls.api(get_root_url(use_proxy=True), 'auth', 'v1', 'scopes/current')
|
# Until bug 1460015 is finished, use the old baseUrl style of proxy URL
|
||||||
resp = _do_request(auth_url)
|
resp = _do_request(os.environ['TASKCLUSTER_PROXY_URL'] + '/auth/v1/scopes/current')
|
||||||
return resp.json().get("scopes", [])
|
return resp.json().get("scopes", [])
|
||||||
|
|
||||||
|
|
||||||
def get_purge_cache_url(provisioner_id, worker_type, use_proxy=False):
|
def get_purge_cache_url(provisioner_id, worker_type, use_proxy=False):
|
||||||
url_tmpl = liburls.api(get_root_url(use_proxy), 'purge-cache', 'v1', 'purge-cache/{}/{}')
|
if use_proxy:
|
||||||
|
# Until bug 1460015 is finished, use the old baseUrl style of proxy URL
|
||||||
|
url_tmpl = os.environ['TASKCLUSTER_PROXY_URL'] + '/purge-cache/v1/purge-cache/{}/{}'
|
||||||
|
else:
|
||||||
|
url_tmpl = liburls.api(get_root_url(), 'purge-cache', 'v1', 'purge-cache/{}/{}')
|
||||||
return url_tmpl.format(provisioner_id, worker_type)
|
return url_tmpl.format(provisioner_id, worker_type)
|
||||||
|
|
||||||
|
|
||||||
|
@ -268,7 +267,11 @@ def purge_cache(provisioner_id, worker_type, cache_name, use_proxy=False):
|
||||||
def send_email(address, subject, content, link, use_proxy=False):
|
def send_email(address, subject, content, link, use_proxy=False):
|
||||||
"""Sends an email using the notify service"""
|
"""Sends an email using the notify service"""
|
||||||
logger.info('Sending email to {}.'.format(address))
|
logger.info('Sending email to {}.'.format(address))
|
||||||
url = liburls.api(get_root_url(use_proxy), 'notify', 'v1', 'email')
|
if use_proxy:
|
||||||
|
# Until bug 1460015 is finished, use the old baseUrl style of proxy URL
|
||||||
|
url = os.environ['TASKCLUSTER_PROXY_URL'] + '/notify/v1/email'
|
||||||
|
else:
|
||||||
|
url = liburls.api(get_root_url(), 'notify', 'v1', 'email')
|
||||||
_do_request(url, json={
|
_do_request(url, json={
|
||||||
'address': address,
|
'address': address,
|
||||||
'subject': subject,
|
'subject': subject,
|
||||||
|
|
|
@ -12,7 +12,7 @@ python-hglib==2.4
|
||||||
redo==2.0.2
|
redo==2.0.2
|
||||||
requests==2.9.1
|
requests==2.9.1
|
||||||
six==1.10.0
|
six==1.10.0
|
||||||
taskcluster==6.0.0
|
taskcluster==4.0.1
|
||||||
taskcluster-urls==11.0.0
|
taskcluster-urls==11.0.0
|
||||||
virtualenv==15.2.0
|
virtualenv==15.2.0
|
||||||
voluptuous==0.11.5
|
voluptuous==0.11.5
|
||||||
|
|
|
@ -104,10 +104,10 @@ taskcluster-urls==11.0.0 \
|
||||||
--hash=sha256:18dcaa9c2412d34ff6c78faca33f0dd8f2384e3f00a98d5832c62d6d664741f0 \
|
--hash=sha256:18dcaa9c2412d34ff6c78faca33f0dd8f2384e3f00a98d5832c62d6d664741f0 \
|
||||||
--hash=sha256:2aceab7cf5b1948bc197f2e5e50c371aa48181ccd490b8bada00f1e3baf0c5cc \
|
--hash=sha256:2aceab7cf5b1948bc197f2e5e50c371aa48181ccd490b8bada00f1e3baf0c5cc \
|
||||||
--hash=sha256:74bd2110b5daaebcec5e1d287bf137b61cb8cf6b2d8f5f2b74183e32bc4e7c87
|
--hash=sha256:74bd2110b5daaebcec5e1d287bf137b61cb8cf6b2d8f5f2b74183e32bc4e7c87
|
||||||
taskcluster==6.0.0 \
|
taskcluster==4.0.1 \
|
||||||
--hash=sha256:48ecd4898c7928deddfb34cb1cfe2b2505c68416e6c503f8a7f3dd0572425e96 \
|
--hash=sha256:27256511044346ac71a495d3c636f2add95c102b9b09f90d6fb1ea3e9949d311 \
|
||||||
--hash=sha256:6d5cf7bdbc09dc48b2d376b418b95c1c157a2d359c4b6b231c1fb14a323c0cc5 \
|
--hash=sha256:99dd90bc1c566968868c8b07ede32f8e031cbccd52c7195a61e802679d461447 \
|
||||||
--hash=sha256:e409fce7a72808e4f87dc7baca7a79d8b64d5c5045264b9e197c120cc40e219b
|
--hash=sha256:d0360063c1a3fcaaa514bb31c03954ba573d2b671df40a2ecfdfd9339cc8e93e
|
||||||
virtualenv-clone==0.3.0 \
|
virtualenv-clone==0.3.0 \
|
||||||
--hash=sha256:4507071d81013fd03ea9930ec26bc8648b997927a11fa80e8ee81198b57e0ac7 \
|
--hash=sha256:4507071d81013fd03ea9930ec26bc8648b997927a11fa80e8ee81198b57e0ac7 \
|
||||||
--hash=sha256:b5cfe535d14dc68dfc1d1bb4ac1209ea28235b91156e2bba8e250d291c3fb4f8 \
|
--hash=sha256:b5cfe535d14dc68dfc1d1bb4ac1209ea28235b91156e2bba8e250d291c3fb4f8 \
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
Metadata-Version: 1.1
|
Metadata-Version: 1.1
|
||||||
Name: taskcluster
|
Name: taskcluster
|
||||||
Version: 6.0.0
|
Version: 4.0.1
|
||||||
Summary: Python client for Taskcluster
|
Summary: Python client for Taskcluster
|
||||||
Home-page: https://github.com/taskcluster/taskcluster-client.py
|
Home-page: https://github.com/taskcluster/taskcluster-client.py
|
||||||
Author: John Ford
|
Author: John Ford
|
||||||
|
|
|
@ -70,7 +70,7 @@ python2-compatible and operate synchronously.
|
||||||
|
|
||||||
|
|
||||||
The objects under `taskcluster.aio` (e.g., `taskcluster.aio.Queue`) require
|
The objects under `taskcluster.aio` (e.g., `taskcluster.aio.Queue`) require
|
||||||
`python>=3.6`. The async objects use asyncio coroutines for concurrency; this
|
`python>=3.5`. The async objects use asyncio coroutines for concurrency; this
|
||||||
allows us to put I/O operations in the background, so operations that require
|
allows us to put I/O operations in the background, so operations that require
|
||||||
the cpu can happen sooner. Given dozens of operations that can run concurrently
|
the cpu can happen sooner. Given dozens of operations that can run concurrently
|
||||||
(e.g., cancelling a medium-to-large task graph), this can result in significant
|
(e.g., cancelling a medium-to-large task graph), this can result in significant
|
||||||
|
@ -101,10 +101,7 @@ Here's a slide deck for an [introduction to async python](https://gitpitch.com/e
|
||||||
|
|
||||||
```python
|
```python
|
||||||
import taskcluster
|
import taskcluster
|
||||||
index = taskcluster.Index({
|
index = taskcluster.Index({'credentials': {'clientId': 'id', 'accessToken': 'accessToken'}})
|
||||||
'rootUrl': 'https://tc.example.com',
|
|
||||||
'credentials': {'clientId': 'id', 'accessToken': 'accessToken'},
|
|
||||||
})
|
|
||||||
index.ping()
|
index.ping()
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -122,34 +119,18 @@ Here's a slide deck for an [introduction to async python](https://gitpitch.com/e
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from taskcluster import client
|
from taskcluster import client
|
||||||
qEvt = client.QueueEvents({rootUrl: 'https://tc.example.com'})
|
qEvt = client.QueueEvents()
|
||||||
# The following calls are equivalent
|
# The following calls are equivalent
|
||||||
qEvt.taskCompleted({'taskId': 'atask'})
|
qEvt.taskCompleted({'taskId': 'atask'})
|
||||||
qEvt.taskCompleted(taskId='atask')
|
qEvt.taskCompleted(taskId='atask')
|
||||||
```
|
```
|
||||||
|
|
||||||
## Root URL
|
|
||||||
|
|
||||||
This client requires a `rootUrl` argument to identify the Taskcluster
|
|
||||||
deployment to talk to. As of this writing, the production cluster has rootUrl
|
|
||||||
`https://taskcluster.net`.
|
|
||||||
|
|
||||||
## Environment Variables
|
|
||||||
|
|
||||||
As of version 6.0.0, the client does not read the standard `TASKCLUSTER_…`
|
|
||||||
environment variables automatically. To fetch their values explicitly, use
|
|
||||||
`taskcluster.optionsFromEnvironment()`:
|
|
||||||
|
|
||||||
```python
|
|
||||||
auth = taskcluster.Auth(taskcluster.optionsFromEnvironment())
|
|
||||||
```
|
|
||||||
|
|
||||||
## Pagination
|
## Pagination
|
||||||
There are two ways to accomplish pagination easily with the python client. The first is
|
There are two ways to accomplish pagination easily with the python client. The first is
|
||||||
to implement pagination in your code:
|
to implement pagination in your code:
|
||||||
```python
|
```python
|
||||||
import taskcluster
|
import taskcluster
|
||||||
queue = taskcluster.Queue({'rootUrl': 'https://tc.example.com'})
|
queue = taskcluster.Queue()
|
||||||
i = 0
|
i = 0
|
||||||
tasks = 0
|
tasks = 0
|
||||||
outcome = queue.listTaskGroup('JzTGxwxhQ76_Tt1dxkaG5g')
|
outcome = queue.listTaskGroup('JzTGxwxhQ76_Tt1dxkaG5g')
|
||||||
|
@ -172,7 +153,7 @@ built and then counted:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
import taskcluster
|
import taskcluster
|
||||||
queue = taskcluster.Queue({'rootUrl': 'https://tc.example.com'})
|
queue = taskcluster.Queue()
|
||||||
|
|
||||||
responses = []
|
responses = []
|
||||||
|
|
||||||
|
@ -1030,9 +1011,9 @@ await asyncAuth.testAuthenticateGet() # -> result
|
||||||
import taskcluster
|
import taskcluster
|
||||||
authEvents = taskcluster.AuthEvents(options)
|
authEvents = taskcluster.AuthEvents(options)
|
||||||
```
|
```
|
||||||
The auth service is responsible for storing credentials, managing
|
The auth service, typically available at `auth.taskcluster.net`
|
||||||
assignment of scopes, and validation of request signatures from other
|
is responsible for storing credentials, managing assignment of scopes,
|
||||||
services.
|
and validation of request signatures from other services.
|
||||||
|
|
||||||
These exchanges provides notifications when credentials or roles are
|
These exchanges provides notifications when credentials or roles are
|
||||||
updated. This is mostly so that multiple instances of the auth service
|
updated. This is mostly so that multiple instances of the auth service
|
||||||
|
@ -1507,23 +1488,11 @@ session = taskcluster.aio.createSession(loop=loop)
|
||||||
asyncEC2Manager = taskcluster.aio.EC2Manager(options, session=session)
|
asyncEC2Manager = taskcluster.aio.EC2Manager(options, session=session)
|
||||||
```
|
```
|
||||||
A taskcluster service which manages EC2 instances. This service does not understand any taskcluster concepts intrinsicaly other than using the name `workerType` to refer to a group of associated instances. Unless you are working on building a provisioner for AWS, you almost certainly do not want to use this service
|
A taskcluster service which manages EC2 instances. This service does not understand any taskcluster concepts intrinsicaly other than using the name `workerType` to refer to a group of associated instances. Unless you are working on building a provisioner for AWS, you almost certainly do not want to use this service
|
||||||
#### Ping Server
|
|
||||||
Respond without doing anything.
|
|
||||||
This endpoint is used to check that the service is up.
|
|
||||||
|
|
||||||
|
|
||||||
```python
|
|
||||||
# Sync calls
|
|
||||||
eC2Manager.ping() # -> None`
|
|
||||||
# Async call
|
|
||||||
await asyncEC2Manager.ping() # -> None
|
|
||||||
```
|
|
||||||
|
|
||||||
#### See the list of worker types which are known to be managed
|
#### See the list of worker types which are known to be managed
|
||||||
This method is only for debugging the ec2-manager
|
This method is only for debugging the ec2-manager
|
||||||
|
|
||||||
|
|
||||||
Required [output schema](v1/list-worker-types.json#)
|
Required [output schema](http://schemas.taskcluster.net/ec2-manager/v1/list-worker-types.json#)
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# Sync calls
|
# Sync calls
|
||||||
|
@ -1541,7 +1510,7 @@ Takes the following arguments:
|
||||||
|
|
||||||
* `workerType`
|
* `workerType`
|
||||||
|
|
||||||
Required [input schema](v1/run-instance-request.json#)
|
Required [input schema](http://schemas.taskcluster.net/ec2-manager/v1/run-instance-request.json#)
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# Sync calls
|
# Sync calls
|
||||||
|
@ -1579,7 +1548,7 @@ Takes the following arguments:
|
||||||
|
|
||||||
* `workerType`
|
* `workerType`
|
||||||
|
|
||||||
Required [output schema](v1/worker-type-resources.json#)
|
Required [output schema](http://schemas.taskcluster.net/ec2-manager/v1/worker-type-resources.json#)
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# Sync calls
|
# Sync calls
|
||||||
|
@ -1599,7 +1568,7 @@ Takes the following arguments:
|
||||||
|
|
||||||
* `workerType`
|
* `workerType`
|
||||||
|
|
||||||
Required [output schema](v1/health.json#)
|
Required [output schema](http://schemas.taskcluster.net/ec2-manager/v1/health.json#)
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# Sync calls
|
# Sync calls
|
||||||
|
@ -1619,7 +1588,7 @@ Takes the following arguments:
|
||||||
|
|
||||||
* `workerType`
|
* `workerType`
|
||||||
|
|
||||||
Required [output schema](v1/errors.json#)
|
Required [output schema](http://schemas.taskcluster.net/ec2-manager/v1/errors.json#)
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# Sync calls
|
# Sync calls
|
||||||
|
@ -1639,7 +1608,7 @@ Takes the following arguments:
|
||||||
|
|
||||||
* `workerType`
|
* `workerType`
|
||||||
|
|
||||||
Required [output schema](v1/worker-type-state.json#)
|
Required [output schema](http://schemas.taskcluster.net/ec2-manager/v1/worker-type-state.json#)
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# Sync calls
|
# Sync calls
|
||||||
|
@ -1659,7 +1628,7 @@ Takes the following arguments:
|
||||||
|
|
||||||
* `name`
|
* `name`
|
||||||
|
|
||||||
Required [input schema](v1/create-key-pair.json#)
|
Required [input schema](http://schemas.taskcluster.net/ec2-manager/v1/create-key-pair.json#)
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# Sync calls
|
# Sync calls
|
||||||
|
@ -1711,7 +1680,7 @@ await asyncEC2Manager.terminateInstance(region='value', instanceId='value') # ->
|
||||||
Return a list of possible prices for EC2
|
Return a list of possible prices for EC2
|
||||||
|
|
||||||
|
|
||||||
Required [output schema](v1/prices.json#)
|
Required [output schema](http://schemas.taskcluster.net/ec2-manager/v1/prices.json#)
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# Sync calls
|
# Sync calls
|
||||||
|
@ -1724,9 +1693,9 @@ await asyncEC2Manager.getPrices() # -> result
|
||||||
Return a list of possible prices for EC2
|
Return a list of possible prices for EC2
|
||||||
|
|
||||||
|
|
||||||
Required [input schema](v1/prices-request.json#)
|
Required [input schema](http://schemas.taskcluster.net/ec2-manager/v1/prices-request.json#)
|
||||||
|
|
||||||
Required [output schema](v1/prices.json#)
|
Required [output schema](http://schemas.taskcluster.net/ec2-manager/v1/prices.json#)
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# Sync calls
|
# Sync calls
|
||||||
|
@ -1739,7 +1708,7 @@ await asyncEC2Manager.getSpecificPrices(payload) # -> result
|
||||||
Give some basic stats on the health of our EC2 account
|
Give some basic stats on the health of our EC2 account
|
||||||
|
|
||||||
|
|
||||||
Required [output schema](v1/health.json#)
|
Required [output schema](http://schemas.taskcluster.net/ec2-manager/v1/health.json#)
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# Sync calls
|
# Sync calls
|
||||||
|
@ -1752,7 +1721,7 @@ await asyncEC2Manager.getHealth() # -> result
|
||||||
Return a list of recent errors encountered
|
Return a list of recent errors encountered
|
||||||
|
|
||||||
|
|
||||||
Required [output schema](v1/errors.json#)
|
Required [output schema](http://schemas.taskcluster.net/ec2-manager/v1/errors.json#)
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# Sync calls
|
# Sync calls
|
||||||
|
@ -1852,6 +1821,29 @@ eC2Manager.purgeQueues() # -> None`
|
||||||
await asyncEC2Manager.purgeQueues() # -> None
|
await asyncEC2Manager.purgeQueues() # -> None
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### API Reference
|
||||||
|
Generate an API reference for this service
|
||||||
|
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Sync calls
|
||||||
|
eC2Manager.apiReference() # -> None`
|
||||||
|
# Async call
|
||||||
|
await asyncEC2Manager.apiReference() # -> None
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Ping Server
|
||||||
|
Respond without doing anything.
|
||||||
|
This endpoint is used to check that the service is up.
|
||||||
|
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Sync calls
|
||||||
|
eC2Manager.ping() # -> None`
|
||||||
|
# Async call
|
||||||
|
await asyncEC2Manager.ping() # -> None
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -1868,8 +1860,9 @@ loop = asyncio.get_event_loop()
|
||||||
session = taskcluster.aio.createSession(loop=loop)
|
session = taskcluster.aio.createSession(loop=loop)
|
||||||
asyncGithub = taskcluster.aio.Github(options, session=session)
|
asyncGithub = taskcluster.aio.Github(options, session=session)
|
||||||
```
|
```
|
||||||
The github service is responsible for creating tasks in reposnse
|
The github service, typically available at
|
||||||
to GitHub events, and posting results to the GitHub UI.
|
`github.taskcluster.net`, is responsible for publishing pulse
|
||||||
|
messages in response to GitHub events.
|
||||||
|
|
||||||
This document describes the API end-point for consuming GitHub
|
This document describes the API end-point for consuming GitHub
|
||||||
web hooks, as well as some useful consumer APIs.
|
web hooks, as well as some useful consumer APIs.
|
||||||
|
@ -2063,12 +2056,6 @@ github service
|
||||||
* `organization` is required Description: The GitHub `organization` which had an event. All periods have been replaced by % - such that foo.bar becomes foo%bar - and all other special characters aside from - and _ have been stripped.
|
* `organization` is required Description: The GitHub `organization` which had an event. All periods have been replaced by % - such that foo.bar becomes foo%bar - and all other special characters aside from - and _ have been stripped.
|
||||||
* `repository` is required Description: The GitHub `repository` which had an event.All periods have been replaced by % - such that foo.bar becomes foo%bar - and all other special characters aside from - and _ have been stripped.
|
* `repository` is required Description: The GitHub `repository` which had an event.All periods have been replaced by % - such that foo.bar becomes foo%bar - and all other special characters aside from - and _ have been stripped.
|
||||||
|
|
||||||
#### GitHub release Event
|
|
||||||
* `githubEvents.taskGroupDefined(routingKeyPattern) -> routingKey`
|
|
||||||
* `routingKeyKind` is constant of `primary` is required Description: Identifier for the routing-key kind. This is always `"primary"` for the formalized routing key.
|
|
||||||
* `organization` is required Description: The GitHub `organization` which had an event. All periods have been replaced by % - such that foo.bar becomes foo%bar - and all other special characters aside from - and _ have been stripped.
|
|
||||||
* `repository` is required Description: The GitHub `repository` which had an event.All periods have been replaced by % - such that foo.bar becomes foo%bar - and all other special characters aside from - and _ have been stripped.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -2102,7 +2089,7 @@ https://www.npmjs.com/package/cron-parser. For example:
|
||||||
* `['0 0 9,21 * * 1-5', '0 0 12 * * 0,6']` -- weekdays at 9:00 and 21:00 UTC, weekends at noon
|
* `['0 0 9,21 * * 1-5', '0 0 12 * * 0,6']` -- weekdays at 9:00 and 21:00 UTC, weekends at noon
|
||||||
|
|
||||||
The task definition is used as a JSON-e template, with a context depending on how it is fired. See
|
The task definition is used as a JSON-e template, with a context depending on how it is fired. See
|
||||||
[/docs/reference/core/taskcluster-hooks/docs/firing-hooks](firing-hooks)
|
https://docs.taskcluster.net/reference/core/taskcluster-hooks/docs/firing-hooks
|
||||||
for more information.
|
for more information.
|
||||||
#### Ping Server
|
#### Ping Server
|
||||||
Respond without doing anything.
|
Respond without doing anything.
|
||||||
|
@ -2636,18 +2623,6 @@ asyncLogin = taskcluster.aio.Login(options, session=session)
|
||||||
```
|
```
|
||||||
The Login service serves as the interface between external authentication
|
The Login service serves as the interface between external authentication
|
||||||
systems and Taskcluster credentials.
|
systems and Taskcluster credentials.
|
||||||
#### Ping Server
|
|
||||||
Respond without doing anything.
|
|
||||||
This endpoint is used to check that the service is up.
|
|
||||||
|
|
||||||
|
|
||||||
```python
|
|
||||||
# Sync calls
|
|
||||||
login.ping() # -> None`
|
|
||||||
# Async call
|
|
||||||
await asyncLogin.ping() # -> None
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Get Taskcluster credentials given a suitable `access_token`
|
#### Get Taskcluster credentials given a suitable `access_token`
|
||||||
Given an OIDC `access_token` from a trusted OpenID provider, return a
|
Given an OIDC `access_token` from a trusted OpenID provider, return a
|
||||||
set of Taskcluster credentials for use on behalf of the identified
|
set of Taskcluster credentials for use on behalf of the identified
|
||||||
|
@ -2661,7 +2636,7 @@ Authorization: Bearer abc.xyz
|
||||||
```
|
```
|
||||||
|
|
||||||
The `access_token` is first verified against the named
|
The `access_token` is first verified against the named
|
||||||
:provider, then passed to the provider's APIBuilder to retrieve a user
|
:provider, then passed to the provider's API to retrieve a user
|
||||||
profile. That profile is then used to generate Taskcluster credentials
|
profile. That profile is then used to generate Taskcluster credentials
|
||||||
appropriate to the user. Note that the resulting credentials may or may
|
appropriate to the user. Note that the resulting credentials may or may
|
||||||
not include a `certificate` property. Callers should be prepared for either
|
not include a `certificate` property. Callers should be prepared for either
|
||||||
|
@ -2677,7 +2652,7 @@ Takes the following arguments:
|
||||||
|
|
||||||
* `provider`
|
* `provider`
|
||||||
|
|
||||||
Required [output schema](v1/oidc-credentials-response.json#)
|
Required [output schema](http://schemas.taskcluster.net/login/v1/oidc-credentials-response.json)
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# Sync calls
|
# Sync calls
|
||||||
|
@ -2688,6 +2663,18 @@ await asyncLogin.oidcCredentials(provider) # -> result
|
||||||
await asyncLogin.oidcCredentials(provider='value') # -> result
|
await asyncLogin.oidcCredentials(provider='value') # -> result
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Ping Server
|
||||||
|
Respond without doing anything.
|
||||||
|
This endpoint is used to check that the service is up.
|
||||||
|
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Sync calls
|
||||||
|
login.ping() # -> None`
|
||||||
|
# Async call
|
||||||
|
await asyncLogin.ping() # -> None
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -2774,111 +2761,6 @@ await asyncNotify.irc(payload) # -> None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Methods in `taskcluster.Pulse`
|
|
||||||
```python
|
|
||||||
import asyncio # Only for async
|
|
||||||
// Create Pulse client instance
|
|
||||||
import taskcluster
|
|
||||||
import taskcluster.aio
|
|
||||||
|
|
||||||
pulse = taskcluster.Pulse(options)
|
|
||||||
# Below only for async instances, assume already in coroutine
|
|
||||||
loop = asyncio.get_event_loop()
|
|
||||||
session = taskcluster.aio.createSession(loop=loop)
|
|
||||||
asyncPulse = taskcluster.aio.Pulse(options, session=session)
|
|
||||||
```
|
|
||||||
The taskcluster-pulse service, typically available at `pulse.taskcluster.net`
|
|
||||||
manages pulse credentials for taskcluster users.
|
|
||||||
|
|
||||||
A service to manage Pulse credentials for anything using
|
|
||||||
Taskcluster credentials. This allows for self-service pulse
|
|
||||||
access and greater control within the Taskcluster project.
|
|
||||||
#### Ping Server
|
|
||||||
Respond without doing anything.
|
|
||||||
This endpoint is used to check that the service is up.
|
|
||||||
|
|
||||||
|
|
||||||
```python
|
|
||||||
# Sync calls
|
|
||||||
pulse.ping() # -> None`
|
|
||||||
# Async call
|
|
||||||
await asyncPulse.ping() # -> None
|
|
||||||
```
|
|
||||||
|
|
||||||
#### List Namespaces
|
|
||||||
List the namespaces managed by this service.
|
|
||||||
|
|
||||||
This will list up to 1000 namespaces. If more namespaces are present a
|
|
||||||
`continuationToken` will be returned, which can be given in the next
|
|
||||||
request. For the initial request, do not provide continuation token.
|
|
||||||
|
|
||||||
|
|
||||||
Required [output schema](v1/list-namespaces-response.json#)
|
|
||||||
|
|
||||||
```python
|
|
||||||
# Sync calls
|
|
||||||
pulse.listNamespaces() # -> result`
|
|
||||||
# Async call
|
|
||||||
await asyncPulse.listNamespaces() # -> result
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Get a namespace
|
|
||||||
Get public information about a single namespace. This is the same information
|
|
||||||
as returned by `listNamespaces`.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Takes the following arguments:
|
|
||||||
|
|
||||||
* `namespace`
|
|
||||||
|
|
||||||
Required [output schema](v1/namespace.json#)
|
|
||||||
|
|
||||||
```python
|
|
||||||
# Sync calls
|
|
||||||
pulse.namespace(namespace) # -> result`
|
|
||||||
pulse.namespace(namespace='value') # -> result
|
|
||||||
# Async call
|
|
||||||
await asyncPulse.namespace(namespace) # -> result
|
|
||||||
await asyncPulse.namespace(namespace='value') # -> result
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Claim a namespace
|
|
||||||
Claim a namespace, returning a connection string with access to that namespace
|
|
||||||
good for use until the `reclaimAt` time in the response body. The connection
|
|
||||||
string can be used as many times as desired during this period, but must not
|
|
||||||
be used after `reclaimAt`.
|
|
||||||
|
|
||||||
Connections made with this connection string may persist beyond `reclaimAt`,
|
|
||||||
although it should not persist forever. 24 hours is a good maximum, and this
|
|
||||||
service will terminate connections after 72 hours (although this value is
|
|
||||||
configurable).
|
|
||||||
|
|
||||||
The specified `expires` time updates any existing expiration times. Connections
|
|
||||||
for expired namespaces will be terminated.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Takes the following arguments:
|
|
||||||
|
|
||||||
* `namespace`
|
|
||||||
|
|
||||||
Required [input schema](v1/namespace-request.json#)
|
|
||||||
|
|
||||||
Required [output schema](v1/namespace-response.json#)
|
|
||||||
|
|
||||||
```python
|
|
||||||
# Sync calls
|
|
||||||
pulse.claimNamespace(namespace, payload) # -> result`
|
|
||||||
pulse.claimNamespace(payload, namespace='value') # -> result
|
|
||||||
# Async call
|
|
||||||
await asyncPulse.claimNamespace(namespace, payload) # -> result
|
|
||||||
await asyncPulse.claimNamespace(payload, namespace='value') # -> result
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Methods in `taskcluster.PurgeCache`
|
### Methods in `taskcluster.PurgeCache`
|
||||||
```python
|
```python
|
||||||
import asyncio # Only for async
|
import asyncio # Only for async
|
||||||
|
@ -2892,7 +2774,8 @@ loop = asyncio.get_event_loop()
|
||||||
session = taskcluster.aio.createSession(loop=loop)
|
session = taskcluster.aio.createSession(loop=loop)
|
||||||
asyncPurgeCache = taskcluster.aio.PurgeCache(options, session=session)
|
asyncPurgeCache = taskcluster.aio.PurgeCache(options, session=session)
|
||||||
```
|
```
|
||||||
The purge-cache service is responsible for publishing a pulse
|
The purge-cache service, typically available at
|
||||||
|
`purge-cache.taskcluster.net`, is responsible for publishing a pulse
|
||||||
message for workers, so they can purge cache upon request.
|
message for workers, so they can purge cache upon request.
|
||||||
|
|
||||||
This document describes the API end-point for publishing the pulse
|
This document describes the API end-point for publishing the pulse
|
||||||
|
|
|
@ -7,7 +7,7 @@ import sys
|
||||||
# The VERSION variable is automagically changed
|
# The VERSION variable is automagically changed
|
||||||
# by release.sh. Make sure you understand how
|
# by release.sh. Make sure you understand how
|
||||||
# that script works if you want to change this
|
# that script works if you want to change this
|
||||||
VERSION = '6.0.0'
|
VERSION = '4.0.1'
|
||||||
|
|
||||||
tests_require = [
|
tests_require = [
|
||||||
'nose==1.3.7',
|
'nose==1.3.7',
|
||||||
|
@ -30,7 +30,6 @@ install_requires = [
|
||||||
'requests>=2.4.3,<3',
|
'requests>=2.4.3,<3',
|
||||||
'mohawk>=0.3.4,<0.4',
|
'mohawk>=0.3.4,<0.4',
|
||||||
'slugid>=1.0.7,<2',
|
'slugid>=1.0.7,<2',
|
||||||
'taskcluster-urls>=10.1.0,<12',
|
|
||||||
'six>=1.10.0,<2',
|
'six>=1.10.0,<2',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,6 @@ from .hooks import Hooks # NOQA
|
||||||
from .index import Index # NOQA
|
from .index import Index # NOQA
|
||||||
from .login import Login # NOQA
|
from .login import Login # NOQA
|
||||||
from .notify import Notify # NOQA
|
from .notify import Notify # NOQA
|
||||||
from .pulse import Pulse # NOQA
|
|
||||||
from .purgecache import PurgeCache # NOQA
|
from .purgecache import PurgeCache # NOQA
|
||||||
from .purgecacheevents import PurgeCacheEvents # NOQA
|
from .purgecacheevents import PurgeCacheEvents # NOQA
|
||||||
from .queue import Queue # NOQA
|
from .queue import Queue # NOQA
|
||||||
|
|
|
@ -9,7 +9,6 @@ from .hooks import Hooks # NOQA
|
||||||
from .index import Index # NOQA
|
from .index import Index # NOQA
|
||||||
from .login import Login # NOQA
|
from .login import Login # NOQA
|
||||||
from .notify import Notify # NOQA
|
from .notify import Notify # NOQA
|
||||||
from .pulse import Pulse # NOQA
|
|
||||||
from .purgecache import PurgeCache # NOQA
|
from .purgecache import PurgeCache # NOQA
|
||||||
from .purgecacheevents import PurgeCacheEvents # NOQA
|
from .purgecacheevents import PurgeCacheEvents # NOQA
|
||||||
from .queue import Queue # NOQA
|
from .queue import Queue # NOQA
|
||||||
|
|
|
@ -114,7 +114,7 @@ class AsyncBaseClient(BaseClient):
|
||||||
the logic about doing failure retry and passes off the actual work
|
the logic about doing failure retry and passes off the actual work
|
||||||
of doing an HTTP request to another method."""
|
of doing an HTTP request to another method."""
|
||||||
|
|
||||||
url = self._constructUrl(route)
|
url = self._joinBaseUrlAndRoute(route)
|
||||||
log.debug('Full URL used is: %s', url)
|
log.debug('Full URL used is: %s', url)
|
||||||
|
|
||||||
hawkExt = self.makeHawkExt()
|
hawkExt = self.makeHawkExt()
|
||||||
|
@ -221,7 +221,7 @@ class AsyncBaseClient(BaseClient):
|
||||||
try:
|
try:
|
||||||
await response.release()
|
await response.release()
|
||||||
return await response.json()
|
return await response.json()
|
||||||
except (ValueError, aiohttp.client_exceptions.ContentTypeError):
|
except ValueError:
|
||||||
return {"response": response}
|
return {"response": response}
|
||||||
|
|
||||||
# This code-path should be unreachable
|
# This code-path should be unreachable
|
||||||
|
@ -239,8 +239,6 @@ class AsyncBaseClient(BaseClient):
|
||||||
|
|
||||||
|
|
||||||
def createApiClient(name, api):
|
def createApiClient(name, api):
|
||||||
api = api['reference']
|
|
||||||
|
|
||||||
attributes = dict(
|
attributes = dict(
|
||||||
name=name,
|
name=name,
|
||||||
__doc__=api.get('description'),
|
__doc__=api.get('description'),
|
||||||
|
@ -248,22 +246,12 @@ def createApiClient(name, api):
|
||||||
funcinfo={},
|
funcinfo={},
|
||||||
)
|
)
|
||||||
|
|
||||||
# apply a default for apiVersion; this can be removed when all services
|
copiedOptions = ('baseUrl', 'exchangePrefix')
|
||||||
# have apiVersion
|
|
||||||
if 'apiVersion' not in api:
|
|
||||||
api['apiVersion'] = 'v1'
|
|
||||||
|
|
||||||
copiedOptions = ('exchangePrefix',)
|
|
||||||
for opt in copiedOptions:
|
for opt in copiedOptions:
|
||||||
if opt in api:
|
if opt in api['reference']:
|
||||||
attributes['classOptions'][opt] = api[opt]
|
attributes['classOptions'][opt] = api['reference'][opt]
|
||||||
|
|
||||||
copiedProperties = ('serviceName', 'apiVersion')
|
for entry in api['reference']['entries']:
|
||||||
for opt in copiedProperties:
|
|
||||||
if opt in api:
|
|
||||||
attributes[opt] = api[opt]
|
|
||||||
|
|
||||||
for entry in api['entries']:
|
|
||||||
if entry['type'] == 'function':
|
if entry['type'] == 'function':
|
||||||
def addApiCall(e):
|
def addApiCall(e):
|
||||||
async def apiCall(self, *args, **kwargs):
|
async def apiCall(self, *args, **kwargs):
|
||||||
|
|
|
@ -111,6 +111,6 @@ async def putFile(filename, url, contentType, session=None):
|
||||||
with open(filename, 'rb') as f:
|
with open(filename, 'rb') as f:
|
||||||
contentLength = os.fstat(f.fileno()).st_size
|
contentLength = os.fstat(f.fileno()).st_size
|
||||||
return await makeHttpRequest('put', url, f, headers={
|
return await makeHttpRequest('put', url, f, headers={
|
||||||
'Content-Length': str(contentLength),
|
'Content-Length': contentLength,
|
||||||
'Content-Type': contentType,
|
'Content-Type': contentType,
|
||||||
}, session=session)
|
}, session=session)
|
||||||
|
|
|
@ -55,9 +55,8 @@ class Auth(AsyncBaseClient):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
classOptions = {
|
classOptions = {
|
||||||
|
"baseUrl": "https://auth.taskcluster.net/v1/"
|
||||||
}
|
}
|
||||||
serviceName = 'auth'
|
|
||||||
apiVersion = 'v1'
|
|
||||||
|
|
||||||
async def ping(self, *args, **kwargs):
|
async def ping(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -13,9 +13,9 @@ _defaultConfig = config
|
||||||
|
|
||||||
class AuthEvents(AsyncBaseClient):
|
class AuthEvents(AsyncBaseClient):
|
||||||
"""
|
"""
|
||||||
The auth service is responsible for storing credentials, managing
|
The auth service, typically available at `auth.taskcluster.net`
|
||||||
assignment of scopes, and validation of request signatures from other
|
is responsible for storing credentials, managing assignment of scopes,
|
||||||
services.
|
and validation of request signatures from other services.
|
||||||
|
|
||||||
These exchanges provides notifications when credentials or roles are
|
These exchanges provides notifications when credentials or roles are
|
||||||
updated. This is mostly so that multiple instances of the auth service
|
updated. This is mostly so that multiple instances of the auth service
|
||||||
|
@ -24,10 +24,8 @@ class AuthEvents(AsyncBaseClient):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
classOptions = {
|
classOptions = {
|
||||||
"exchangePrefix": "exchange/taskcluster-auth/v1/",
|
"exchangePrefix": "exchange/taskcluster-auth/v1/"
|
||||||
}
|
}
|
||||||
serviceName = 'auth'
|
|
||||||
apiVersion = 'v1'
|
|
||||||
|
|
||||||
def clientCreated(self, *args, **kwargs):
|
def clientCreated(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -44,9 +44,8 @@ class AwsProvisioner(AsyncBaseClient):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
classOptions = {
|
classOptions = {
|
||||||
|
"baseUrl": "https://aws-provisioner.taskcluster.net/v1"
|
||||||
}
|
}
|
||||||
serviceName = 'aws-provisioner'
|
|
||||||
apiVersion = 'v1'
|
|
||||||
|
|
||||||
async def listWorkerTypeSummaries(self, *args, **kwargs):
|
async def listWorkerTypeSummaries(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -17,9 +17,8 @@ class AwsProvisionerEvents(AsyncBaseClient):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
classOptions = {
|
classOptions = {
|
||||||
"exchangePrefix": "exchange/taskcluster-aws-provisioner/v1/",
|
"exchangePrefix": "exchange/taskcluster-aws-provisioner/v1/"
|
||||||
}
|
}
|
||||||
apiVersion = 'v1'
|
|
||||||
|
|
||||||
def workerTypeCreated(self, *args, **kwargs):
|
def workerTypeCreated(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -17,21 +17,8 @@ class EC2Manager(AsyncBaseClient):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
classOptions = {
|
classOptions = {
|
||||||
|
"baseUrl": "https://ec2-manager.taskcluster.net/v1"
|
||||||
}
|
}
|
||||||
serviceName = 'ec2-manager'
|
|
||||||
apiVersion = 'v1'
|
|
||||||
|
|
||||||
async def ping(self, *args, **kwargs):
|
|
||||||
"""
|
|
||||||
Ping Server
|
|
||||||
|
|
||||||
Respond without doing anything.
|
|
||||||
This endpoint is used to check that the service is up.
|
|
||||||
|
|
||||||
This method is ``stable``
|
|
||||||
"""
|
|
||||||
|
|
||||||
return await self._makeApiCall(self.funcinfo["ping"], *args, **kwargs)
|
|
||||||
|
|
||||||
async def listWorkerTypes(self, *args, **kwargs):
|
async def listWorkerTypes(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
@ -39,7 +26,7 @@ class EC2Manager(AsyncBaseClient):
|
||||||
|
|
||||||
This method is only for debugging the ec2-manager
|
This method is only for debugging the ec2-manager
|
||||||
|
|
||||||
This method gives output: ``v1/list-worker-types.json#``
|
This method gives output: ``http://schemas.taskcluster.net/ec2-manager/v1/list-worker-types.json#``
|
||||||
|
|
||||||
This method is ``experimental``
|
This method is ``experimental``
|
||||||
"""
|
"""
|
||||||
|
@ -52,7 +39,7 @@ class EC2Manager(AsyncBaseClient):
|
||||||
|
|
||||||
Request an instance of a worker type
|
Request an instance of a worker type
|
||||||
|
|
||||||
This method takes input: ``v1/run-instance-request.json#``
|
This method takes input: ``http://schemas.taskcluster.net/ec2-manager/v1/run-instance-request.json#``
|
||||||
|
|
||||||
This method is ``experimental``
|
This method is ``experimental``
|
||||||
"""
|
"""
|
||||||
|
@ -76,7 +63,7 @@ class EC2Manager(AsyncBaseClient):
|
||||||
|
|
||||||
Return an object which has a generic state description. This only contains counts of instances
|
Return an object which has a generic state description. This only contains counts of instances
|
||||||
|
|
||||||
This method gives output: ``v1/worker-type-resources.json#``
|
This method gives output: ``http://schemas.taskcluster.net/ec2-manager/v1/worker-type-resources.json#``
|
||||||
|
|
||||||
This method is ``experimental``
|
This method is ``experimental``
|
||||||
"""
|
"""
|
||||||
|
@ -89,7 +76,7 @@ class EC2Manager(AsyncBaseClient):
|
||||||
|
|
||||||
Return a view of the health of a given worker type
|
Return a view of the health of a given worker type
|
||||||
|
|
||||||
This method gives output: ``v1/health.json#``
|
This method gives output: ``http://schemas.taskcluster.net/ec2-manager/v1/health.json#``
|
||||||
|
|
||||||
This method is ``experimental``
|
This method is ``experimental``
|
||||||
"""
|
"""
|
||||||
|
@ -102,7 +89,7 @@ class EC2Manager(AsyncBaseClient):
|
||||||
|
|
||||||
Return a list of the most recent errors encountered by a worker type
|
Return a list of the most recent errors encountered by a worker type
|
||||||
|
|
||||||
This method gives output: ``v1/errors.json#``
|
This method gives output: ``http://schemas.taskcluster.net/ec2-manager/v1/errors.json#``
|
||||||
|
|
||||||
This method is ``experimental``
|
This method is ``experimental``
|
||||||
"""
|
"""
|
||||||
|
@ -115,7 +102,7 @@ class EC2Manager(AsyncBaseClient):
|
||||||
|
|
||||||
Return state information for a given worker type
|
Return state information for a given worker type
|
||||||
|
|
||||||
This method gives output: ``v1/worker-type-state.json#``
|
This method gives output: ``http://schemas.taskcluster.net/ec2-manager/v1/worker-type-state.json#``
|
||||||
|
|
||||||
This method is ``experimental``
|
This method is ``experimental``
|
||||||
"""
|
"""
|
||||||
|
@ -128,7 +115,7 @@ class EC2Manager(AsyncBaseClient):
|
||||||
|
|
||||||
Idempotently ensure that a keypair of a given name exists
|
Idempotently ensure that a keypair of a given name exists
|
||||||
|
|
||||||
This method takes input: ``v1/create-key-pair.json#``
|
This method takes input: ``http://schemas.taskcluster.net/ec2-manager/v1/create-key-pair.json#``
|
||||||
|
|
||||||
This method is ``experimental``
|
This method is ``experimental``
|
||||||
"""
|
"""
|
||||||
|
@ -163,7 +150,7 @@ class EC2Manager(AsyncBaseClient):
|
||||||
|
|
||||||
Return a list of possible prices for EC2
|
Return a list of possible prices for EC2
|
||||||
|
|
||||||
This method gives output: ``v1/prices.json#``
|
This method gives output: ``http://schemas.taskcluster.net/ec2-manager/v1/prices.json#``
|
||||||
|
|
||||||
This method is ``experimental``
|
This method is ``experimental``
|
||||||
"""
|
"""
|
||||||
|
@ -176,9 +163,9 @@ class EC2Manager(AsyncBaseClient):
|
||||||
|
|
||||||
Return a list of possible prices for EC2
|
Return a list of possible prices for EC2
|
||||||
|
|
||||||
This method takes input: ``v1/prices-request.json#``
|
This method takes input: ``http://schemas.taskcluster.net/ec2-manager/v1/prices-request.json#``
|
||||||
|
|
||||||
This method gives output: ``v1/prices.json#``
|
This method gives output: ``http://schemas.taskcluster.net/ec2-manager/v1/prices.json#``
|
||||||
|
|
||||||
This method is ``experimental``
|
This method is ``experimental``
|
||||||
"""
|
"""
|
||||||
|
@ -191,7 +178,7 @@ class EC2Manager(AsyncBaseClient):
|
||||||
|
|
||||||
Give some basic stats on the health of our EC2 account
|
Give some basic stats on the health of our EC2 account
|
||||||
|
|
||||||
This method gives output: ``v1/health.json#``
|
This method gives output: ``http://schemas.taskcluster.net/ec2-manager/v1/health.json#``
|
||||||
|
|
||||||
This method is ``experimental``
|
This method is ``experimental``
|
||||||
"""
|
"""
|
||||||
|
@ -204,7 +191,7 @@ class EC2Manager(AsyncBaseClient):
|
||||||
|
|
||||||
Return a list of recent errors encountered
|
Return a list of recent errors encountered
|
||||||
|
|
||||||
This method gives output: ``v1/errors.json#``
|
This method gives output: ``http://schemas.taskcluster.net/ec2-manager/v1/errors.json#``
|
||||||
|
|
||||||
This method is ``experimental``
|
This method is ``experimental``
|
||||||
"""
|
"""
|
||||||
|
@ -302,6 +289,29 @@ class EC2Manager(AsyncBaseClient):
|
||||||
|
|
||||||
return await self._makeApiCall(self.funcinfo["purgeQueues"], *args, **kwargs)
|
return await self._makeApiCall(self.funcinfo["purgeQueues"], *args, **kwargs)
|
||||||
|
|
||||||
|
async def apiReference(self, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
API Reference
|
||||||
|
|
||||||
|
Generate an API reference for this service
|
||||||
|
|
||||||
|
This method is ``experimental``
|
||||||
|
"""
|
||||||
|
|
||||||
|
return await self._makeApiCall(self.funcinfo["apiReference"], *args, **kwargs)
|
||||||
|
|
||||||
|
async def ping(self, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Ping Server
|
||||||
|
|
||||||
|
Respond without doing anything.
|
||||||
|
This endpoint is used to check that the service is up.
|
||||||
|
|
||||||
|
This method is ``stable``
|
||||||
|
"""
|
||||||
|
|
||||||
|
return await self._makeApiCall(self.funcinfo["ping"], *args, **kwargs)
|
||||||
|
|
||||||
funcinfo = {
|
funcinfo = {
|
||||||
"allState": {
|
"allState": {
|
||||||
'args': [],
|
'args': [],
|
||||||
|
@ -317,6 +327,13 @@ class EC2Manager(AsyncBaseClient):
|
||||||
'route': '/internal/ami-usage',
|
'route': '/internal/ami-usage',
|
||||||
'stability': 'experimental',
|
'stability': 'experimental',
|
||||||
},
|
},
|
||||||
|
"apiReference": {
|
||||||
|
'args': [],
|
||||||
|
'method': 'get',
|
||||||
|
'name': 'apiReference',
|
||||||
|
'route': '/internal/api-reference',
|
||||||
|
'stability': 'experimental',
|
||||||
|
},
|
||||||
"dbpoolStats": {
|
"dbpoolStats": {
|
||||||
'args': [],
|
'args': [],
|
||||||
'method': 'get',
|
'method': 'get',
|
||||||
|
@ -333,7 +350,7 @@ class EC2Manager(AsyncBaseClient):
|
||||||
},
|
},
|
||||||
"ensureKeyPair": {
|
"ensureKeyPair": {
|
||||||
'args': ['name'],
|
'args': ['name'],
|
||||||
'input': 'v1/create-key-pair.json#',
|
'input': 'http://schemas.taskcluster.net/ec2-manager/v1/create-key-pair.json#',
|
||||||
'method': 'get',
|
'method': 'get',
|
||||||
'name': 'ensureKeyPair',
|
'name': 'ensureKeyPair',
|
||||||
'route': '/key-pairs/<name>',
|
'route': '/key-pairs/<name>',
|
||||||
|
@ -343,7 +360,7 @@ class EC2Manager(AsyncBaseClient):
|
||||||
'args': [],
|
'args': [],
|
||||||
'method': 'get',
|
'method': 'get',
|
||||||
'name': 'getHealth',
|
'name': 'getHealth',
|
||||||
'output': 'v1/health.json#',
|
'output': 'http://schemas.taskcluster.net/ec2-manager/v1/health.json#',
|
||||||
'route': '/health',
|
'route': '/health',
|
||||||
'stability': 'experimental',
|
'stability': 'experimental',
|
||||||
},
|
},
|
||||||
|
@ -351,7 +368,7 @@ class EC2Manager(AsyncBaseClient):
|
||||||
'args': [],
|
'args': [],
|
||||||
'method': 'get',
|
'method': 'get',
|
||||||
'name': 'getPrices',
|
'name': 'getPrices',
|
||||||
'output': 'v1/prices.json#',
|
'output': 'http://schemas.taskcluster.net/ec2-manager/v1/prices.json#',
|
||||||
'route': '/prices',
|
'route': '/prices',
|
||||||
'stability': 'experimental',
|
'stability': 'experimental',
|
||||||
},
|
},
|
||||||
|
@ -359,16 +376,16 @@ class EC2Manager(AsyncBaseClient):
|
||||||
'args': [],
|
'args': [],
|
||||||
'method': 'get',
|
'method': 'get',
|
||||||
'name': 'getRecentErrors',
|
'name': 'getRecentErrors',
|
||||||
'output': 'v1/errors.json#',
|
'output': 'http://schemas.taskcluster.net/ec2-manager/v1/errors.json#',
|
||||||
'route': '/errors',
|
'route': '/errors',
|
||||||
'stability': 'experimental',
|
'stability': 'experimental',
|
||||||
},
|
},
|
||||||
"getSpecificPrices": {
|
"getSpecificPrices": {
|
||||||
'args': [],
|
'args': [],
|
||||||
'input': 'v1/prices-request.json#',
|
'input': 'http://schemas.taskcluster.net/ec2-manager/v1/prices-request.json#',
|
||||||
'method': 'post',
|
'method': 'post',
|
||||||
'name': 'getSpecificPrices',
|
'name': 'getSpecificPrices',
|
||||||
'output': 'v1/prices.json#',
|
'output': 'http://schemas.taskcluster.net/ec2-manager/v1/prices.json#',
|
||||||
'route': '/prices',
|
'route': '/prices',
|
||||||
'stability': 'experimental',
|
'stability': 'experimental',
|
||||||
},
|
},
|
||||||
|
@ -376,7 +393,7 @@ class EC2Manager(AsyncBaseClient):
|
||||||
'args': [],
|
'args': [],
|
||||||
'method': 'get',
|
'method': 'get',
|
||||||
'name': 'listWorkerTypes',
|
'name': 'listWorkerTypes',
|
||||||
'output': 'v1/list-worker-types.json#',
|
'output': 'http://schemas.taskcluster.net/ec2-manager/v1/list-worker-types.json#',
|
||||||
'route': '/worker-types',
|
'route': '/worker-types',
|
||||||
'stability': 'experimental',
|
'stability': 'experimental',
|
||||||
},
|
},
|
||||||
|
@ -410,7 +427,7 @@ class EC2Manager(AsyncBaseClient):
|
||||||
},
|
},
|
||||||
"runInstance": {
|
"runInstance": {
|
||||||
'args': ['workerType'],
|
'args': ['workerType'],
|
||||||
'input': 'v1/run-instance-request.json#',
|
'input': 'http://schemas.taskcluster.net/ec2-manager/v1/run-instance-request.json#',
|
||||||
'method': 'put',
|
'method': 'put',
|
||||||
'name': 'runInstance',
|
'name': 'runInstance',
|
||||||
'route': '/worker-types/<workerType>/instance',
|
'route': '/worker-types/<workerType>/instance',
|
||||||
|
@ -441,7 +458,7 @@ class EC2Manager(AsyncBaseClient):
|
||||||
'args': ['workerType'],
|
'args': ['workerType'],
|
||||||
'method': 'get',
|
'method': 'get',
|
||||||
'name': 'workerTypeErrors',
|
'name': 'workerTypeErrors',
|
||||||
'output': 'v1/errors.json#',
|
'output': 'http://schemas.taskcluster.net/ec2-manager/v1/errors.json#',
|
||||||
'route': '/worker-types/<workerType>/errors',
|
'route': '/worker-types/<workerType>/errors',
|
||||||
'stability': 'experimental',
|
'stability': 'experimental',
|
||||||
},
|
},
|
||||||
|
@ -449,7 +466,7 @@ class EC2Manager(AsyncBaseClient):
|
||||||
'args': ['workerType'],
|
'args': ['workerType'],
|
||||||
'method': 'get',
|
'method': 'get',
|
||||||
'name': 'workerTypeHealth',
|
'name': 'workerTypeHealth',
|
||||||
'output': 'v1/health.json#',
|
'output': 'http://schemas.taskcluster.net/ec2-manager/v1/health.json#',
|
||||||
'route': '/worker-types/<workerType>/health',
|
'route': '/worker-types/<workerType>/health',
|
||||||
'stability': 'experimental',
|
'stability': 'experimental',
|
||||||
},
|
},
|
||||||
|
@ -457,7 +474,7 @@ class EC2Manager(AsyncBaseClient):
|
||||||
'args': ['workerType'],
|
'args': ['workerType'],
|
||||||
'method': 'get',
|
'method': 'get',
|
||||||
'name': 'workerTypeState',
|
'name': 'workerTypeState',
|
||||||
'output': 'v1/worker-type-state.json#',
|
'output': 'http://schemas.taskcluster.net/ec2-manager/v1/worker-type-state.json#',
|
||||||
'route': '/worker-types/<workerType>/state',
|
'route': '/worker-types/<workerType>/state',
|
||||||
'stability': 'experimental',
|
'stability': 'experimental',
|
||||||
},
|
},
|
||||||
|
@ -465,7 +482,7 @@ class EC2Manager(AsyncBaseClient):
|
||||||
'args': ['workerType'],
|
'args': ['workerType'],
|
||||||
'method': 'get',
|
'method': 'get',
|
||||||
'name': 'workerTypeStats',
|
'name': 'workerTypeStats',
|
||||||
'output': 'v1/worker-type-resources.json#',
|
'output': 'http://schemas.taskcluster.net/ec2-manager/v1/worker-type-resources.json#',
|
||||||
'route': '/worker-types/<workerType>/stats',
|
'route': '/worker-types/<workerType>/stats',
|
||||||
'stability': 'experimental',
|
'stability': 'experimental',
|
||||||
},
|
},
|
||||||
|
|
|
@ -13,8 +13,9 @@ _defaultConfig = config
|
||||||
|
|
||||||
class Github(AsyncBaseClient):
|
class Github(AsyncBaseClient):
|
||||||
"""
|
"""
|
||||||
The github service is responsible for creating tasks in reposnse
|
The github service, typically available at
|
||||||
to GitHub events, and posting results to the GitHub UI.
|
`github.taskcluster.net`, is responsible for publishing pulse
|
||||||
|
messages in response to GitHub events.
|
||||||
|
|
||||||
This document describes the API end-point for consuming GitHub
|
This document describes the API end-point for consuming GitHub
|
||||||
web hooks, as well as some useful consumer APIs.
|
web hooks, as well as some useful consumer APIs.
|
||||||
|
@ -24,9 +25,8 @@ class Github(AsyncBaseClient):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
classOptions = {
|
classOptions = {
|
||||||
|
"baseUrl": "https://github.taskcluster.net/v1/"
|
||||||
}
|
}
|
||||||
serviceName = 'github'
|
|
||||||
apiVersion = 'v1'
|
|
||||||
|
|
||||||
async def ping(self, *args, **kwargs):
|
async def ping(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -22,10 +22,8 @@ class GithubEvents(AsyncBaseClient):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
classOptions = {
|
classOptions = {
|
||||||
"exchangePrefix": "exchange/taskcluster-github/v1/",
|
"exchangePrefix": "exchange/taskcluster-github/v1/"
|
||||||
}
|
}
|
||||||
serviceName = 'github'
|
|
||||||
apiVersion = 'v1'
|
|
||||||
|
|
||||||
def pullRequest(self, *args, **kwargs):
|
def pullRequest(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
@ -150,43 +148,6 @@ class GithubEvents(AsyncBaseClient):
|
||||||
}
|
}
|
||||||
return self._makeTopicExchange(ref, *args, **kwargs)
|
return self._makeTopicExchange(ref, *args, **kwargs)
|
||||||
|
|
||||||
def taskGroupDefined(self, *args, **kwargs):
|
|
||||||
"""
|
|
||||||
GitHub release Event
|
|
||||||
|
|
||||||
used for creating status indicators in GitHub UI using Statuses API
|
|
||||||
|
|
||||||
This exchange outputs: ``v1/task-group-defined-message.json#``This exchange takes the following keys:
|
|
||||||
|
|
||||||
* routingKeyKind: Identifier for the routing-key kind. This is always `"primary"` for the formalized routing key. (required)
|
|
||||||
|
|
||||||
* organization: The GitHub `organization` which had an event. All periods have been replaced by % - such that foo.bar becomes foo%bar - and all other special characters aside from - and _ have been stripped. (required)
|
|
||||||
|
|
||||||
* repository: The GitHub `repository` which had an event.All periods have been replaced by % - such that foo.bar becomes foo%bar - and all other special characters aside from - and _ have been stripped. (required)
|
|
||||||
"""
|
|
||||||
|
|
||||||
ref = {
|
|
||||||
'exchange': 'task-group-defined',
|
|
||||||
'name': 'taskGroupDefined',
|
|
||||||
'routingKey': [
|
|
||||||
{
|
|
||||||
'constant': 'primary',
|
|
||||||
'multipleWords': False,
|
|
||||||
'name': 'routingKeyKind',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'multipleWords': False,
|
|
||||||
'name': 'organization',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'multipleWords': False,
|
|
||||||
'name': 'repository',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'schema': 'v1/task-group-defined-message.json#',
|
|
||||||
}
|
|
||||||
return self._makeTopicExchange(ref, *args, **kwargs)
|
|
||||||
|
|
||||||
funcinfo = {
|
funcinfo = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,14 +30,13 @@ class Hooks(AsyncBaseClient):
|
||||||
* `['0 0 9,21 * * 1-5', '0 0 12 * * 0,6']` -- weekdays at 9:00 and 21:00 UTC, weekends at noon
|
* `['0 0 9,21 * * 1-5', '0 0 12 * * 0,6']` -- weekdays at 9:00 and 21:00 UTC, weekends at noon
|
||||||
|
|
||||||
The task definition is used as a JSON-e template, with a context depending on how it is fired. See
|
The task definition is used as a JSON-e template, with a context depending on how it is fired. See
|
||||||
[/docs/reference/core/taskcluster-hooks/docs/firing-hooks](firing-hooks)
|
https://docs.taskcluster.net/reference/core/taskcluster-hooks/docs/firing-hooks
|
||||||
for more information.
|
for more information.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
classOptions = {
|
classOptions = {
|
||||||
|
"baseUrl": "https://hooks.taskcluster.net/v1/"
|
||||||
}
|
}
|
||||||
serviceName = 'hooks'
|
|
||||||
apiVersion = 'v1'
|
|
||||||
|
|
||||||
async def ping(self, *args, **kwargs):
|
async def ping(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -108,9 +108,8 @@ class Index(AsyncBaseClient):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
classOptions = {
|
classOptions = {
|
||||||
|
"baseUrl": "https://index.taskcluster.net/v1/"
|
||||||
}
|
}
|
||||||
serviceName = 'index'
|
|
||||||
apiVersion = 'v1'
|
|
||||||
|
|
||||||
async def ping(self, *args, **kwargs):
|
async def ping(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -18,21 +18,8 @@ class Login(AsyncBaseClient):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
classOptions = {
|
classOptions = {
|
||||||
|
"baseUrl": "https://login.taskcluster.net/v1"
|
||||||
}
|
}
|
||||||
serviceName = 'login'
|
|
||||||
apiVersion = 'v1'
|
|
||||||
|
|
||||||
async def ping(self, *args, **kwargs):
|
|
||||||
"""
|
|
||||||
Ping Server
|
|
||||||
|
|
||||||
Respond without doing anything.
|
|
||||||
This endpoint is used to check that the service is up.
|
|
||||||
|
|
||||||
This method is ``stable``
|
|
||||||
"""
|
|
||||||
|
|
||||||
return await self._makeApiCall(self.funcinfo["ping"], *args, **kwargs)
|
|
||||||
|
|
||||||
async def oidcCredentials(self, *args, **kwargs):
|
async def oidcCredentials(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
@ -50,7 +37,7 @@ class Login(AsyncBaseClient):
|
||||||
```
|
```
|
||||||
|
|
||||||
The `access_token` is first verified against the named
|
The `access_token` is first verified against the named
|
||||||
:provider, then passed to the provider's APIBuilder to retrieve a user
|
:provider, then passed to the provider's API to retrieve a user
|
||||||
profile. That profile is then used to generate Taskcluster credentials
|
profile. That profile is then used to generate Taskcluster credentials
|
||||||
appropriate to the user. Note that the resulting credentials may or may
|
appropriate to the user. Note that the resulting credentials may or may
|
||||||
not include a `certificate` property. Callers should be prepared for either
|
not include a `certificate` property. Callers should be prepared for either
|
||||||
|
@ -60,19 +47,31 @@ class Login(AsyncBaseClient):
|
||||||
monitor this expiration and refresh the credentials if necessary, by calling
|
monitor this expiration and refresh the credentials if necessary, by calling
|
||||||
this endpoint again, if they have expired.
|
this endpoint again, if they have expired.
|
||||||
|
|
||||||
This method gives output: ``v1/oidc-credentials-response.json#``
|
This method gives output: ``http://schemas.taskcluster.net/login/v1/oidc-credentials-response.json``
|
||||||
|
|
||||||
This method is ``experimental``
|
This method is ``experimental``
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return await self._makeApiCall(self.funcinfo["oidcCredentials"], *args, **kwargs)
|
return await self._makeApiCall(self.funcinfo["oidcCredentials"], *args, **kwargs)
|
||||||
|
|
||||||
|
async def ping(self, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Ping Server
|
||||||
|
|
||||||
|
Respond without doing anything.
|
||||||
|
This endpoint is used to check that the service is up.
|
||||||
|
|
||||||
|
This method is ``stable``
|
||||||
|
"""
|
||||||
|
|
||||||
|
return await self._makeApiCall(self.funcinfo["ping"], *args, **kwargs)
|
||||||
|
|
||||||
funcinfo = {
|
funcinfo = {
|
||||||
"oidcCredentials": {
|
"oidcCredentials": {
|
||||||
'args': ['provider'],
|
'args': ['provider'],
|
||||||
'method': 'get',
|
'method': 'get',
|
||||||
'name': 'oidcCredentials',
|
'name': 'oidcCredentials',
|
||||||
'output': 'v1/oidc-credentials-response.json#',
|
'output': 'http://schemas.taskcluster.net/login/v1/oidc-credentials-response.json',
|
||||||
'route': '/oidc-credentials/<provider>',
|
'route': '/oidc-credentials/<provider>',
|
||||||
'stability': 'experimental',
|
'stability': 'experimental',
|
||||||
},
|
},
|
||||||
|
|
|
@ -19,9 +19,8 @@ class Notify(AsyncBaseClient):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
classOptions = {
|
classOptions = {
|
||||||
|
"baseUrl": "https://notify.taskcluster.net/v1/"
|
||||||
}
|
}
|
||||||
serviceName = 'notify'
|
|
||||||
apiVersion = 'v1'
|
|
||||||
|
|
||||||
async def ping(self, *args, **kwargs):
|
async def ping(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -22,21 +22,21 @@ class Pulse(AsyncBaseClient):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
classOptions = {
|
classOptions = {
|
||||||
|
"baseUrl": "https://pulse.taskcluster.net/v1"
|
||||||
}
|
}
|
||||||
serviceName = 'pulse'
|
|
||||||
apiVersion = 'v1'
|
|
||||||
|
|
||||||
async def ping(self, *args, **kwargs):
|
async def overview(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Ping Server
|
Rabbit Overview
|
||||||
|
|
||||||
Respond without doing anything.
|
Get an overview of the Rabbit cluster.
|
||||||
This endpoint is used to check that the service is up.
|
|
||||||
|
|
||||||
This method is ``stable``
|
This method gives output: ``http://schemas.taskcluster.net/pulse/v1/rabbit-overview.json``
|
||||||
|
|
||||||
|
This method is ``experimental``
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return await self._makeApiCall(self.funcinfo["ping"], *args, **kwargs)
|
return await self._makeApiCall(self.funcinfo["overview"], *args, **kwargs)
|
||||||
|
|
||||||
async def listNamespaces(self, *args, **kwargs):
|
async def listNamespaces(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
@ -46,9 +46,9 @@ class Pulse(AsyncBaseClient):
|
||||||
|
|
||||||
This will list up to 1000 namespaces. If more namespaces are present a
|
This will list up to 1000 namespaces. If more namespaces are present a
|
||||||
`continuationToken` will be returned, which can be given in the next
|
`continuationToken` will be returned, which can be given in the next
|
||||||
request. For the initial request, do not provide continuation token.
|
request. For the initial request, do not provide continuation.
|
||||||
|
|
||||||
This method gives output: ``v1/list-namespaces-response.json#``
|
This method gives output: ``http://schemas.taskcluster.net/pulse/v1/list-namespaces-response.json``
|
||||||
|
|
||||||
This method is ``experimental``
|
This method is ``experimental``
|
||||||
"""
|
"""
|
||||||
|
@ -62,7 +62,7 @@ class Pulse(AsyncBaseClient):
|
||||||
Get public information about a single namespace. This is the same information
|
Get public information about a single namespace. This is the same information
|
||||||
as returned by `listNamespaces`.
|
as returned by `listNamespaces`.
|
||||||
|
|
||||||
This method gives output: ``v1/namespace.json#``
|
This method gives output: ``http://schemas.taskcluster.net/pulse/v1/namespace.json``
|
||||||
|
|
||||||
This method is ``experimental``
|
This method is ``experimental``
|
||||||
"""
|
"""
|
||||||
|
@ -73,35 +73,43 @@ class Pulse(AsyncBaseClient):
|
||||||
"""
|
"""
|
||||||
Claim a namespace
|
Claim a namespace
|
||||||
|
|
||||||
Claim a namespace, returning a connection string with access to that namespace
|
Claim a namespace, returning a username and password with access to that
|
||||||
good for use until the `reclaimAt` time in the response body. The connection
|
namespace good for a short time. Clients should call this endpoint again
|
||||||
string can be used as many times as desired during this period, but must not
|
at the re-claim time given in the response, as the password will be rotated
|
||||||
be used after `reclaimAt`.
|
soon after that time. The namespace will expire, and any associated queues
|
||||||
|
and exchanges will be deleted, at the given expiration time.
|
||||||
|
|
||||||
Connections made with this connection string may persist beyond `reclaimAt`,
|
The `expires` and `contact` properties can be updated at any time in a reclaim
|
||||||
although it should not persist forever. 24 hours is a good maximum, and this
|
operation.
|
||||||
service will terminate connections after 72 hours (although this value is
|
|
||||||
configurable).
|
|
||||||
|
|
||||||
The specified `expires` time updates any existing expiration times. Connections
|
This method takes input: ``http://schemas.taskcluster.net/pulse/v1/namespace-request.json``
|
||||||
for expired namespaces will be terminated.
|
|
||||||
|
|
||||||
This method takes input: ``v1/namespace-request.json#``
|
This method gives output: ``http://schemas.taskcluster.net/pulse/v1/namespace-response.json``
|
||||||
|
|
||||||
This method gives output: ``v1/namespace-response.json#``
|
|
||||||
|
|
||||||
This method is ``experimental``
|
This method is ``experimental``
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return await self._makeApiCall(self.funcinfo["claimNamespace"], *args, **kwargs)
|
return await self._makeApiCall(self.funcinfo["claimNamespace"], *args, **kwargs)
|
||||||
|
|
||||||
|
async def ping(self, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Ping Server
|
||||||
|
|
||||||
|
Respond without doing anything.
|
||||||
|
This endpoint is used to check that the service is up.
|
||||||
|
|
||||||
|
This method is ``stable``
|
||||||
|
"""
|
||||||
|
|
||||||
|
return await self._makeApiCall(self.funcinfo["ping"], *args, **kwargs)
|
||||||
|
|
||||||
funcinfo = {
|
funcinfo = {
|
||||||
"claimNamespace": {
|
"claimNamespace": {
|
||||||
'args': ['namespace'],
|
'args': ['namespace'],
|
||||||
'input': 'v1/namespace-request.json#',
|
'input': 'http://schemas.taskcluster.net/pulse/v1/namespace-request.json',
|
||||||
'method': 'post',
|
'method': 'post',
|
||||||
'name': 'claimNamespace',
|
'name': 'claimNamespace',
|
||||||
'output': 'v1/namespace-response.json#',
|
'output': 'http://schemas.taskcluster.net/pulse/v1/namespace-response.json',
|
||||||
'route': '/namespace/<namespace>',
|
'route': '/namespace/<namespace>',
|
||||||
'stability': 'experimental',
|
'stability': 'experimental',
|
||||||
},
|
},
|
||||||
|
@ -109,8 +117,8 @@ class Pulse(AsyncBaseClient):
|
||||||
'args': [],
|
'args': [],
|
||||||
'method': 'get',
|
'method': 'get',
|
||||||
'name': 'listNamespaces',
|
'name': 'listNamespaces',
|
||||||
'output': 'v1/list-namespaces-response.json#',
|
'output': 'http://schemas.taskcluster.net/pulse/v1/list-namespaces-response.json',
|
||||||
'query': ['limit', 'continuationToken'],
|
'query': ['limit', 'continuation'],
|
||||||
'route': '/namespaces',
|
'route': '/namespaces',
|
||||||
'stability': 'experimental',
|
'stability': 'experimental',
|
||||||
},
|
},
|
||||||
|
@ -118,10 +126,18 @@ class Pulse(AsyncBaseClient):
|
||||||
'args': ['namespace'],
|
'args': ['namespace'],
|
||||||
'method': 'get',
|
'method': 'get',
|
||||||
'name': 'namespace',
|
'name': 'namespace',
|
||||||
'output': 'v1/namespace.json#',
|
'output': 'http://schemas.taskcluster.net/pulse/v1/namespace.json',
|
||||||
'route': '/namespace/<namespace>',
|
'route': '/namespace/<namespace>',
|
||||||
'stability': 'experimental',
|
'stability': 'experimental',
|
||||||
},
|
},
|
||||||
|
"overview": {
|
||||||
|
'args': [],
|
||||||
|
'method': 'get',
|
||||||
|
'name': 'overview',
|
||||||
|
'output': 'http://schemas.taskcluster.net/pulse/v1/rabbit-overview.json',
|
||||||
|
'route': '/overview',
|
||||||
|
'stability': 'experimental',
|
||||||
|
},
|
||||||
"ping": {
|
"ping": {
|
||||||
'args': [],
|
'args': [],
|
||||||
'method': 'get',
|
'method': 'get',
|
||||||
|
|
|
@ -13,7 +13,8 @@ _defaultConfig = config
|
||||||
|
|
||||||
class PurgeCache(AsyncBaseClient):
|
class PurgeCache(AsyncBaseClient):
|
||||||
"""
|
"""
|
||||||
The purge-cache service is responsible for publishing a pulse
|
The purge-cache service, typically available at
|
||||||
|
`purge-cache.taskcluster.net`, is responsible for publishing a pulse
|
||||||
message for workers, so they can purge cache upon request.
|
message for workers, so they can purge cache upon request.
|
||||||
|
|
||||||
This document describes the API end-point for publishing the pulse
|
This document describes the API end-point for publishing the pulse
|
||||||
|
@ -21,9 +22,8 @@ class PurgeCache(AsyncBaseClient):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
classOptions = {
|
classOptions = {
|
||||||
|
"baseUrl": "https://purge-cache.taskcluster.net/v1/"
|
||||||
}
|
}
|
||||||
serviceName = 'purge-cache'
|
|
||||||
apiVersion = 'v1'
|
|
||||||
|
|
||||||
async def ping(self, *args, **kwargs):
|
async def ping(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -22,10 +22,8 @@ class PurgeCacheEvents(AsyncBaseClient):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
classOptions = {
|
classOptions = {
|
||||||
"exchangePrefix": "exchange/taskcluster-purge-cache/v1/",
|
"exchangePrefix": "exchange/taskcluster-purge-cache/v1/"
|
||||||
}
|
}
|
||||||
serviceName = 'purge-cache'
|
|
||||||
apiVersion = 'v1'
|
|
||||||
|
|
||||||
def purgeCache(self, *args, **kwargs):
|
def purgeCache(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -25,9 +25,8 @@ class Queue(AsyncBaseClient):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
classOptions = {
|
classOptions = {
|
||||||
|
"baseUrl": "https://queue.taskcluster.net/v1/"
|
||||||
}
|
}
|
||||||
serviceName = 'queue'
|
|
||||||
apiVersion = 'v1'
|
|
||||||
|
|
||||||
async def ping(self, *args, **kwargs):
|
async def ping(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -63,10 +63,8 @@ class QueueEvents(AsyncBaseClient):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
classOptions = {
|
classOptions = {
|
||||||
"exchangePrefix": "exchange/taskcluster-queue/v1/",
|
"exchangePrefix": "exchange/taskcluster-queue/v1/"
|
||||||
}
|
}
|
||||||
serviceName = 'queue'
|
|
||||||
apiVersion = 'v1'
|
|
||||||
|
|
||||||
def taskDefined(self, *args, **kwargs):
|
def taskDefined(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -23,9 +23,8 @@ class Secrets(AsyncBaseClient):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
classOptions = {
|
classOptions = {
|
||||||
|
"baseUrl": "https://secrets.taskcluster.net/v1/"
|
||||||
}
|
}
|
||||||
serviceName = 'secrets'
|
|
||||||
apiVersion = 'v1'
|
|
||||||
|
|
||||||
async def ping(self, *args, **kwargs):
|
async def ping(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -23,10 +23,8 @@ class TreeherderEvents(AsyncBaseClient):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
classOptions = {
|
classOptions = {
|
||||||
"exchangePrefix": "exchange/taskcluster-treeherder/v1/",
|
"exchangePrefix": "exchange/taskcluster-treeherder/v1/"
|
||||||
}
|
}
|
||||||
serviceName = 'treeherder'
|
|
||||||
apiVersion = 'v1'
|
|
||||||
|
|
||||||
def jobs(self, *args, **kwargs):
|
def jobs(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
@ -35,7 +33,7 @@ class TreeherderEvents(AsyncBaseClient):
|
||||||
When a task run is scheduled or resolved, a message is posted to
|
When a task run is scheduled or resolved, a message is posted to
|
||||||
this exchange in a Treeherder consumable format.
|
this exchange in a Treeherder consumable format.
|
||||||
|
|
||||||
This exchange outputs: ``v1/pulse-job.json#``This exchange takes the following keys:
|
This exchange outputs: ``http://schemas.taskcluster.net/taskcluster-treeherder/v1/pulse-job.json#``This exchange takes the following keys:
|
||||||
|
|
||||||
* destination: destination (required)
|
* destination: destination (required)
|
||||||
|
|
||||||
|
@ -61,7 +59,7 @@ class TreeherderEvents(AsyncBaseClient):
|
||||||
'name': 'reserved',
|
'name': 'reserved',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
'schema': 'v1/pulse-job.json#',
|
'schema': 'http://schemas.taskcluster.net/taskcluster-treeherder/v1/pulse-job.json#',
|
||||||
}
|
}
|
||||||
return self._makeTopicExchange(ref, *args, **kwargs)
|
return self._makeTopicExchange(ref, *args, **kwargs)
|
||||||
|
|
||||||
|
|
|
@ -55,9 +55,8 @@ class Auth(BaseClient):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
classOptions = {
|
classOptions = {
|
||||||
|
"baseUrl": "https://auth.taskcluster.net/v1/"
|
||||||
}
|
}
|
||||||
serviceName = 'auth'
|
|
||||||
apiVersion = 'v1'
|
|
||||||
|
|
||||||
def ping(self, *args, **kwargs):
|
def ping(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -13,9 +13,9 @@ _defaultConfig = config
|
||||||
|
|
||||||
class AuthEvents(BaseClient):
|
class AuthEvents(BaseClient):
|
||||||
"""
|
"""
|
||||||
The auth service is responsible for storing credentials, managing
|
The auth service, typically available at `auth.taskcluster.net`
|
||||||
assignment of scopes, and validation of request signatures from other
|
is responsible for storing credentials, managing assignment of scopes,
|
||||||
services.
|
and validation of request signatures from other services.
|
||||||
|
|
||||||
These exchanges provides notifications when credentials or roles are
|
These exchanges provides notifications when credentials or roles are
|
||||||
updated. This is mostly so that multiple instances of the auth service
|
updated. This is mostly so that multiple instances of the auth service
|
||||||
|
@ -24,10 +24,8 @@ class AuthEvents(BaseClient):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
classOptions = {
|
classOptions = {
|
||||||
"exchangePrefix": "exchange/taskcluster-auth/v1/",
|
"exchangePrefix": "exchange/taskcluster-auth/v1/"
|
||||||
}
|
}
|
||||||
serviceName = 'auth'
|
|
||||||
apiVersion = 'v1'
|
|
||||||
|
|
||||||
def clientCreated(self, *args, **kwargs):
|
def clientCreated(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -44,9 +44,8 @@ class AwsProvisioner(BaseClient):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
classOptions = {
|
classOptions = {
|
||||||
|
"baseUrl": "https://aws-provisioner.taskcluster.net/v1"
|
||||||
}
|
}
|
||||||
serviceName = 'aws-provisioner'
|
|
||||||
apiVersion = 'v1'
|
|
||||||
|
|
||||||
def listWorkerTypeSummaries(self, *args, **kwargs):
|
def listWorkerTypeSummaries(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -17,9 +17,8 @@ class AwsProvisionerEvents(BaseClient):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
classOptions = {
|
classOptions = {
|
||||||
"exchangePrefix": "exchange/taskcluster-aws-provisioner/v1/",
|
"exchangePrefix": "exchange/taskcluster-aws-provisioner/v1/"
|
||||||
}
|
}
|
||||||
apiVersion = 'v1'
|
|
||||||
|
|
||||||
def workerTypeCreated(self, *args, **kwargs):
|
def workerTypeCreated(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
from __future__ import absolute_import, division, print_function
|
from __future__ import absolute_import, division, print_function
|
||||||
|
|
||||||
|
import os
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import copy
|
import copy
|
||||||
|
@ -20,7 +21,6 @@ import mohawk.bewit
|
||||||
|
|
||||||
import taskcluster.exceptions as exceptions
|
import taskcluster.exceptions as exceptions
|
||||||
import taskcluster.utils as utils
|
import taskcluster.utils as utils
|
||||||
import taskcluster_urls as liburls
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -28,11 +28,10 @@ log = logging.getLogger(__name__)
|
||||||
# Default configuration
|
# Default configuration
|
||||||
_defaultConfig = config = {
|
_defaultConfig = config = {
|
||||||
'credentials': {
|
'credentials': {
|
||||||
'clientId': None,
|
'clientId': os.environ.get('TASKCLUSTER_CLIENT_ID'),
|
||||||
'accessToken': None,
|
'accessToken': os.environ.get('TASKCLUSTER_ACCESS_TOKEN'),
|
||||||
'certificate': None,
|
'certificate': os.environ.get('TASKCLUSTER_CERTIFICATE'),
|
||||||
},
|
},
|
||||||
'rootUrl': None,
|
|
||||||
'maxRetries': 5,
|
'maxRetries': 5,
|
||||||
'signedUrlExpiration': 15 * 60,
|
'signedUrlExpiration': 15 * 60,
|
||||||
}
|
}
|
||||||
|
@ -53,14 +52,10 @@ class BaseClient(object):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, options=None, session=None):
|
def __init__(self, options=None, session=None):
|
||||||
if options and options.get('baseUrl'):
|
|
||||||
raise exceptions.TaskclusterFailure('baseUrl option is no longer allowed')
|
|
||||||
o = copy.deepcopy(self.classOptions)
|
o = copy.deepcopy(self.classOptions)
|
||||||
o.update(_defaultConfig)
|
o.update(_defaultConfig)
|
||||||
if options:
|
if options:
|
||||||
o.update(options)
|
o.update(options)
|
||||||
if not o.get('rootUrl'):
|
|
||||||
raise exceptions.TaskclusterFailure('rootUrl option is required')
|
|
||||||
|
|
||||||
credentials = o.get('credentials')
|
credentials = o.get('credentials')
|
||||||
if credentials:
|
if credentials:
|
||||||
|
@ -72,7 +67,6 @@ class BaseClient(object):
|
||||||
except:
|
except:
|
||||||
s = '%s (%s) must be unicode encodable' % (x, credentials[x])
|
s = '%s (%s) must be unicode encodable' % (x, credentials[x])
|
||||||
raise exceptions.TaskclusterAuthFailure(s)
|
raise exceptions.TaskclusterAuthFailure(s)
|
||||||
|
|
||||||
self.options = o
|
self.options = o
|
||||||
if 'credentials' in o:
|
if 'credentials' in o:
|
||||||
log.debug('credentials key scrubbed from logging output')
|
log.debug('credentials key scrubbed from logging output')
|
||||||
|
@ -174,7 +168,7 @@ class BaseClient(object):
|
||||||
route = self._subArgsInRoute(entry, routeParams)
|
route = self._subArgsInRoute(entry, routeParams)
|
||||||
if query:
|
if query:
|
||||||
route += '?' + urllib.parse.urlencode(query)
|
route += '?' + urllib.parse.urlencode(query)
|
||||||
return liburls.api(self.options['rootUrl'], self.serviceName, self.apiVersion, route)
|
return self._joinBaseUrlAndRoute(route)
|
||||||
|
|
||||||
def buildSignedUrl(self, methodName, *args, **kwargs):
|
def buildSignedUrl(self, methodName, *args, **kwargs):
|
||||||
""" Build a signed URL. This URL contains the credentials needed to access
|
""" Build a signed URL. This URL contains the credentials needed to access
|
||||||
|
@ -243,14 +237,11 @@ class BaseClient(object):
|
||||||
u.fragment,
|
u.fragment,
|
||||||
))
|
))
|
||||||
|
|
||||||
def _constructUrl(self, route):
|
def _joinBaseUrlAndRoute(self, route):
|
||||||
"""Construct a URL for the given route on this service, based on the
|
return urllib.parse.urljoin(
|
||||||
rootUrl"""
|
'{}/'.format(self.options['baseUrl'].rstrip('/')),
|
||||||
return liburls.api(
|
route.lstrip('/')
|
||||||
self.options['rootUrl'],
|
)
|
||||||
self.serviceName,
|
|
||||||
self.apiVersion,
|
|
||||||
route.rstrip('/'))
|
|
||||||
|
|
||||||
def _makeApiCall(self, entry, *args, **kwargs):
|
def _makeApiCall(self, entry, *args, **kwargs):
|
||||||
""" This function is used to dispatch calls to other functions
|
""" This function is used to dispatch calls to other functions
|
||||||
|
@ -446,7 +437,7 @@ class BaseClient(object):
|
||||||
the logic about doing failure retry and passes off the actual work
|
the logic about doing failure retry and passes off the actual work
|
||||||
of doing an HTTP request to another method."""
|
of doing an HTTP request to another method."""
|
||||||
|
|
||||||
url = self._constructUrl(route)
|
url = self._joinBaseUrlAndRoute(route)
|
||||||
log.debug('Full URL used is: %s', url)
|
log.debug('Full URL used is: %s', url)
|
||||||
|
|
||||||
hawkExt = self.makeHawkExt()
|
hawkExt = self.makeHawkExt()
|
||||||
|
@ -554,8 +545,6 @@ class BaseClient(object):
|
||||||
|
|
||||||
|
|
||||||
def createApiClient(name, api):
|
def createApiClient(name, api):
|
||||||
api = api['reference']
|
|
||||||
|
|
||||||
attributes = dict(
|
attributes = dict(
|
||||||
name=name,
|
name=name,
|
||||||
__doc__=api.get('description'),
|
__doc__=api.get('description'),
|
||||||
|
@ -563,22 +552,12 @@ def createApiClient(name, api):
|
||||||
funcinfo={},
|
funcinfo={},
|
||||||
)
|
)
|
||||||
|
|
||||||
# apply a default for apiVersion; this can be removed when all services
|
copiedOptions = ('baseUrl', 'exchangePrefix')
|
||||||
# have apiVersion
|
|
||||||
if 'apiVersion' not in api:
|
|
||||||
api['apiVersion'] = 'v1'
|
|
||||||
|
|
||||||
copiedOptions = ('exchangePrefix',)
|
|
||||||
for opt in copiedOptions:
|
for opt in copiedOptions:
|
||||||
if opt in api:
|
if opt in api['reference']:
|
||||||
attributes['classOptions'][opt] = api[opt]
|
attributes['classOptions'][opt] = api['reference'][opt]
|
||||||
|
|
||||||
copiedProperties = ('serviceName', 'apiVersion')
|
for entry in api['reference']['entries']:
|
||||||
for opt in copiedProperties:
|
|
||||||
if opt in api:
|
|
||||||
attributes[opt] = api[opt]
|
|
||||||
|
|
||||||
for entry in api['entries']:
|
|
||||||
if entry['type'] == 'function':
|
if entry['type'] == 'function':
|
||||||
def addApiCall(e):
|
def addApiCall(e):
|
||||||
def apiCall(self, *args, **kwargs):
|
def apiCall(self, *args, **kwargs):
|
||||||
|
|
|
@ -17,21 +17,8 @@ class EC2Manager(BaseClient):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
classOptions = {
|
classOptions = {
|
||||||
|
"baseUrl": "https://ec2-manager.taskcluster.net/v1"
|
||||||
}
|
}
|
||||||
serviceName = 'ec2-manager'
|
|
||||||
apiVersion = 'v1'
|
|
||||||
|
|
||||||
def ping(self, *args, **kwargs):
|
|
||||||
"""
|
|
||||||
Ping Server
|
|
||||||
|
|
||||||
Respond without doing anything.
|
|
||||||
This endpoint is used to check that the service is up.
|
|
||||||
|
|
||||||
This method is ``stable``
|
|
||||||
"""
|
|
||||||
|
|
||||||
return self._makeApiCall(self.funcinfo["ping"], *args, **kwargs)
|
|
||||||
|
|
||||||
def listWorkerTypes(self, *args, **kwargs):
|
def listWorkerTypes(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
@ -39,7 +26,7 @@ class EC2Manager(BaseClient):
|
||||||
|
|
||||||
This method is only for debugging the ec2-manager
|
This method is only for debugging the ec2-manager
|
||||||
|
|
||||||
This method gives output: ``v1/list-worker-types.json#``
|
This method gives output: ``http://schemas.taskcluster.net/ec2-manager/v1/list-worker-types.json#``
|
||||||
|
|
||||||
This method is ``experimental``
|
This method is ``experimental``
|
||||||
"""
|
"""
|
||||||
|
@ -52,7 +39,7 @@ class EC2Manager(BaseClient):
|
||||||
|
|
||||||
Request an instance of a worker type
|
Request an instance of a worker type
|
||||||
|
|
||||||
This method takes input: ``v1/run-instance-request.json#``
|
This method takes input: ``http://schemas.taskcluster.net/ec2-manager/v1/run-instance-request.json#``
|
||||||
|
|
||||||
This method is ``experimental``
|
This method is ``experimental``
|
||||||
"""
|
"""
|
||||||
|
@ -76,7 +63,7 @@ class EC2Manager(BaseClient):
|
||||||
|
|
||||||
Return an object which has a generic state description. This only contains counts of instances
|
Return an object which has a generic state description. This only contains counts of instances
|
||||||
|
|
||||||
This method gives output: ``v1/worker-type-resources.json#``
|
This method gives output: ``http://schemas.taskcluster.net/ec2-manager/v1/worker-type-resources.json#``
|
||||||
|
|
||||||
This method is ``experimental``
|
This method is ``experimental``
|
||||||
"""
|
"""
|
||||||
|
@ -89,7 +76,7 @@ class EC2Manager(BaseClient):
|
||||||
|
|
||||||
Return a view of the health of a given worker type
|
Return a view of the health of a given worker type
|
||||||
|
|
||||||
This method gives output: ``v1/health.json#``
|
This method gives output: ``http://schemas.taskcluster.net/ec2-manager/v1/health.json#``
|
||||||
|
|
||||||
This method is ``experimental``
|
This method is ``experimental``
|
||||||
"""
|
"""
|
||||||
|
@ -102,7 +89,7 @@ class EC2Manager(BaseClient):
|
||||||
|
|
||||||
Return a list of the most recent errors encountered by a worker type
|
Return a list of the most recent errors encountered by a worker type
|
||||||
|
|
||||||
This method gives output: ``v1/errors.json#``
|
This method gives output: ``http://schemas.taskcluster.net/ec2-manager/v1/errors.json#``
|
||||||
|
|
||||||
This method is ``experimental``
|
This method is ``experimental``
|
||||||
"""
|
"""
|
||||||
|
@ -115,7 +102,7 @@ class EC2Manager(BaseClient):
|
||||||
|
|
||||||
Return state information for a given worker type
|
Return state information for a given worker type
|
||||||
|
|
||||||
This method gives output: ``v1/worker-type-state.json#``
|
This method gives output: ``http://schemas.taskcluster.net/ec2-manager/v1/worker-type-state.json#``
|
||||||
|
|
||||||
This method is ``experimental``
|
This method is ``experimental``
|
||||||
"""
|
"""
|
||||||
|
@ -128,7 +115,7 @@ class EC2Manager(BaseClient):
|
||||||
|
|
||||||
Idempotently ensure that a keypair of a given name exists
|
Idempotently ensure that a keypair of a given name exists
|
||||||
|
|
||||||
This method takes input: ``v1/create-key-pair.json#``
|
This method takes input: ``http://schemas.taskcluster.net/ec2-manager/v1/create-key-pair.json#``
|
||||||
|
|
||||||
This method is ``experimental``
|
This method is ``experimental``
|
||||||
"""
|
"""
|
||||||
|
@ -163,7 +150,7 @@ class EC2Manager(BaseClient):
|
||||||
|
|
||||||
Return a list of possible prices for EC2
|
Return a list of possible prices for EC2
|
||||||
|
|
||||||
This method gives output: ``v1/prices.json#``
|
This method gives output: ``http://schemas.taskcluster.net/ec2-manager/v1/prices.json#``
|
||||||
|
|
||||||
This method is ``experimental``
|
This method is ``experimental``
|
||||||
"""
|
"""
|
||||||
|
@ -176,9 +163,9 @@ class EC2Manager(BaseClient):
|
||||||
|
|
||||||
Return a list of possible prices for EC2
|
Return a list of possible prices for EC2
|
||||||
|
|
||||||
This method takes input: ``v1/prices-request.json#``
|
This method takes input: ``http://schemas.taskcluster.net/ec2-manager/v1/prices-request.json#``
|
||||||
|
|
||||||
This method gives output: ``v1/prices.json#``
|
This method gives output: ``http://schemas.taskcluster.net/ec2-manager/v1/prices.json#``
|
||||||
|
|
||||||
This method is ``experimental``
|
This method is ``experimental``
|
||||||
"""
|
"""
|
||||||
|
@ -191,7 +178,7 @@ class EC2Manager(BaseClient):
|
||||||
|
|
||||||
Give some basic stats on the health of our EC2 account
|
Give some basic stats on the health of our EC2 account
|
||||||
|
|
||||||
This method gives output: ``v1/health.json#``
|
This method gives output: ``http://schemas.taskcluster.net/ec2-manager/v1/health.json#``
|
||||||
|
|
||||||
This method is ``experimental``
|
This method is ``experimental``
|
||||||
"""
|
"""
|
||||||
|
@ -204,7 +191,7 @@ class EC2Manager(BaseClient):
|
||||||
|
|
||||||
Return a list of recent errors encountered
|
Return a list of recent errors encountered
|
||||||
|
|
||||||
This method gives output: ``v1/errors.json#``
|
This method gives output: ``http://schemas.taskcluster.net/ec2-manager/v1/errors.json#``
|
||||||
|
|
||||||
This method is ``experimental``
|
This method is ``experimental``
|
||||||
"""
|
"""
|
||||||
|
@ -302,6 +289,29 @@ class EC2Manager(BaseClient):
|
||||||
|
|
||||||
return self._makeApiCall(self.funcinfo["purgeQueues"], *args, **kwargs)
|
return self._makeApiCall(self.funcinfo["purgeQueues"], *args, **kwargs)
|
||||||
|
|
||||||
|
def apiReference(self, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
API Reference
|
||||||
|
|
||||||
|
Generate an API reference for this service
|
||||||
|
|
||||||
|
This method is ``experimental``
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self._makeApiCall(self.funcinfo["apiReference"], *args, **kwargs)
|
||||||
|
|
||||||
|
def ping(self, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Ping Server
|
||||||
|
|
||||||
|
Respond without doing anything.
|
||||||
|
This endpoint is used to check that the service is up.
|
||||||
|
|
||||||
|
This method is ``stable``
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self._makeApiCall(self.funcinfo["ping"], *args, **kwargs)
|
||||||
|
|
||||||
funcinfo = {
|
funcinfo = {
|
||||||
"allState": {
|
"allState": {
|
||||||
'args': [],
|
'args': [],
|
||||||
|
@ -317,6 +327,13 @@ class EC2Manager(BaseClient):
|
||||||
'route': '/internal/ami-usage',
|
'route': '/internal/ami-usage',
|
||||||
'stability': 'experimental',
|
'stability': 'experimental',
|
||||||
},
|
},
|
||||||
|
"apiReference": {
|
||||||
|
'args': [],
|
||||||
|
'method': 'get',
|
||||||
|
'name': 'apiReference',
|
||||||
|
'route': '/internal/api-reference',
|
||||||
|
'stability': 'experimental',
|
||||||
|
},
|
||||||
"dbpoolStats": {
|
"dbpoolStats": {
|
||||||
'args': [],
|
'args': [],
|
||||||
'method': 'get',
|
'method': 'get',
|
||||||
|
@ -333,7 +350,7 @@ class EC2Manager(BaseClient):
|
||||||
},
|
},
|
||||||
"ensureKeyPair": {
|
"ensureKeyPair": {
|
||||||
'args': ['name'],
|
'args': ['name'],
|
||||||
'input': 'v1/create-key-pair.json#',
|
'input': 'http://schemas.taskcluster.net/ec2-manager/v1/create-key-pair.json#',
|
||||||
'method': 'get',
|
'method': 'get',
|
||||||
'name': 'ensureKeyPair',
|
'name': 'ensureKeyPair',
|
||||||
'route': '/key-pairs/<name>',
|
'route': '/key-pairs/<name>',
|
||||||
|
@ -343,7 +360,7 @@ class EC2Manager(BaseClient):
|
||||||
'args': [],
|
'args': [],
|
||||||
'method': 'get',
|
'method': 'get',
|
||||||
'name': 'getHealth',
|
'name': 'getHealth',
|
||||||
'output': 'v1/health.json#',
|
'output': 'http://schemas.taskcluster.net/ec2-manager/v1/health.json#',
|
||||||
'route': '/health',
|
'route': '/health',
|
||||||
'stability': 'experimental',
|
'stability': 'experimental',
|
||||||
},
|
},
|
||||||
|
@ -351,7 +368,7 @@ class EC2Manager(BaseClient):
|
||||||
'args': [],
|
'args': [],
|
||||||
'method': 'get',
|
'method': 'get',
|
||||||
'name': 'getPrices',
|
'name': 'getPrices',
|
||||||
'output': 'v1/prices.json#',
|
'output': 'http://schemas.taskcluster.net/ec2-manager/v1/prices.json#',
|
||||||
'route': '/prices',
|
'route': '/prices',
|
||||||
'stability': 'experimental',
|
'stability': 'experimental',
|
||||||
},
|
},
|
||||||
|
@ -359,16 +376,16 @@ class EC2Manager(BaseClient):
|
||||||
'args': [],
|
'args': [],
|
||||||
'method': 'get',
|
'method': 'get',
|
||||||
'name': 'getRecentErrors',
|
'name': 'getRecentErrors',
|
||||||
'output': 'v1/errors.json#',
|
'output': 'http://schemas.taskcluster.net/ec2-manager/v1/errors.json#',
|
||||||
'route': '/errors',
|
'route': '/errors',
|
||||||
'stability': 'experimental',
|
'stability': 'experimental',
|
||||||
},
|
},
|
||||||
"getSpecificPrices": {
|
"getSpecificPrices": {
|
||||||
'args': [],
|
'args': [],
|
||||||
'input': 'v1/prices-request.json#',
|
'input': 'http://schemas.taskcluster.net/ec2-manager/v1/prices-request.json#',
|
||||||
'method': 'post',
|
'method': 'post',
|
||||||
'name': 'getSpecificPrices',
|
'name': 'getSpecificPrices',
|
||||||
'output': 'v1/prices.json#',
|
'output': 'http://schemas.taskcluster.net/ec2-manager/v1/prices.json#',
|
||||||
'route': '/prices',
|
'route': '/prices',
|
||||||
'stability': 'experimental',
|
'stability': 'experimental',
|
||||||
},
|
},
|
||||||
|
@ -376,7 +393,7 @@ class EC2Manager(BaseClient):
|
||||||
'args': [],
|
'args': [],
|
||||||
'method': 'get',
|
'method': 'get',
|
||||||
'name': 'listWorkerTypes',
|
'name': 'listWorkerTypes',
|
||||||
'output': 'v1/list-worker-types.json#',
|
'output': 'http://schemas.taskcluster.net/ec2-manager/v1/list-worker-types.json#',
|
||||||
'route': '/worker-types',
|
'route': '/worker-types',
|
||||||
'stability': 'experimental',
|
'stability': 'experimental',
|
||||||
},
|
},
|
||||||
|
@ -410,7 +427,7 @@ class EC2Manager(BaseClient):
|
||||||
},
|
},
|
||||||
"runInstance": {
|
"runInstance": {
|
||||||
'args': ['workerType'],
|
'args': ['workerType'],
|
||||||
'input': 'v1/run-instance-request.json#',
|
'input': 'http://schemas.taskcluster.net/ec2-manager/v1/run-instance-request.json#',
|
||||||
'method': 'put',
|
'method': 'put',
|
||||||
'name': 'runInstance',
|
'name': 'runInstance',
|
||||||
'route': '/worker-types/<workerType>/instance',
|
'route': '/worker-types/<workerType>/instance',
|
||||||
|
@ -441,7 +458,7 @@ class EC2Manager(BaseClient):
|
||||||
'args': ['workerType'],
|
'args': ['workerType'],
|
||||||
'method': 'get',
|
'method': 'get',
|
||||||
'name': 'workerTypeErrors',
|
'name': 'workerTypeErrors',
|
||||||
'output': 'v1/errors.json#',
|
'output': 'http://schemas.taskcluster.net/ec2-manager/v1/errors.json#',
|
||||||
'route': '/worker-types/<workerType>/errors',
|
'route': '/worker-types/<workerType>/errors',
|
||||||
'stability': 'experimental',
|
'stability': 'experimental',
|
||||||
},
|
},
|
||||||
|
@ -449,7 +466,7 @@ class EC2Manager(BaseClient):
|
||||||
'args': ['workerType'],
|
'args': ['workerType'],
|
||||||
'method': 'get',
|
'method': 'get',
|
||||||
'name': 'workerTypeHealth',
|
'name': 'workerTypeHealth',
|
||||||
'output': 'v1/health.json#',
|
'output': 'http://schemas.taskcluster.net/ec2-manager/v1/health.json#',
|
||||||
'route': '/worker-types/<workerType>/health',
|
'route': '/worker-types/<workerType>/health',
|
||||||
'stability': 'experimental',
|
'stability': 'experimental',
|
||||||
},
|
},
|
||||||
|
@ -457,7 +474,7 @@ class EC2Manager(BaseClient):
|
||||||
'args': ['workerType'],
|
'args': ['workerType'],
|
||||||
'method': 'get',
|
'method': 'get',
|
||||||
'name': 'workerTypeState',
|
'name': 'workerTypeState',
|
||||||
'output': 'v1/worker-type-state.json#',
|
'output': 'http://schemas.taskcluster.net/ec2-manager/v1/worker-type-state.json#',
|
||||||
'route': '/worker-types/<workerType>/state',
|
'route': '/worker-types/<workerType>/state',
|
||||||
'stability': 'experimental',
|
'stability': 'experimental',
|
||||||
},
|
},
|
||||||
|
@ -465,7 +482,7 @@ class EC2Manager(BaseClient):
|
||||||
'args': ['workerType'],
|
'args': ['workerType'],
|
||||||
'method': 'get',
|
'method': 'get',
|
||||||
'name': 'workerTypeStats',
|
'name': 'workerTypeStats',
|
||||||
'output': 'v1/worker-type-resources.json#',
|
'output': 'http://schemas.taskcluster.net/ec2-manager/v1/worker-type-resources.json#',
|
||||||
'route': '/worker-types/<workerType>/stats',
|
'route': '/worker-types/<workerType>/stats',
|
||||||
'stability': 'experimental',
|
'stability': 'experimental',
|
||||||
},
|
},
|
||||||
|
|
|
@ -13,8 +13,9 @@ _defaultConfig = config
|
||||||
|
|
||||||
class Github(BaseClient):
|
class Github(BaseClient):
|
||||||
"""
|
"""
|
||||||
The github service is responsible for creating tasks in reposnse
|
The github service, typically available at
|
||||||
to GitHub events, and posting results to the GitHub UI.
|
`github.taskcluster.net`, is responsible for publishing pulse
|
||||||
|
messages in response to GitHub events.
|
||||||
|
|
||||||
This document describes the API end-point for consuming GitHub
|
This document describes the API end-point for consuming GitHub
|
||||||
web hooks, as well as some useful consumer APIs.
|
web hooks, as well as some useful consumer APIs.
|
||||||
|
@ -24,9 +25,8 @@ class Github(BaseClient):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
classOptions = {
|
classOptions = {
|
||||||
|
"baseUrl": "https://github.taskcluster.net/v1/"
|
||||||
}
|
}
|
||||||
serviceName = 'github'
|
|
||||||
apiVersion = 'v1'
|
|
||||||
|
|
||||||
def ping(self, *args, **kwargs):
|
def ping(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -22,10 +22,8 @@ class GithubEvents(BaseClient):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
classOptions = {
|
classOptions = {
|
||||||
"exchangePrefix": "exchange/taskcluster-github/v1/",
|
"exchangePrefix": "exchange/taskcluster-github/v1/"
|
||||||
}
|
}
|
||||||
serviceName = 'github'
|
|
||||||
apiVersion = 'v1'
|
|
||||||
|
|
||||||
def pullRequest(self, *args, **kwargs):
|
def pullRequest(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
@ -150,43 +148,6 @@ class GithubEvents(BaseClient):
|
||||||
}
|
}
|
||||||
return self._makeTopicExchange(ref, *args, **kwargs)
|
return self._makeTopicExchange(ref, *args, **kwargs)
|
||||||
|
|
||||||
def taskGroupDefined(self, *args, **kwargs):
|
|
||||||
"""
|
|
||||||
GitHub release Event
|
|
||||||
|
|
||||||
used for creating status indicators in GitHub UI using Statuses API
|
|
||||||
|
|
||||||
This exchange outputs: ``v1/task-group-defined-message.json#``This exchange takes the following keys:
|
|
||||||
|
|
||||||
* routingKeyKind: Identifier for the routing-key kind. This is always `"primary"` for the formalized routing key. (required)
|
|
||||||
|
|
||||||
* organization: The GitHub `organization` which had an event. All periods have been replaced by % - such that foo.bar becomes foo%bar - and all other special characters aside from - and _ have been stripped. (required)
|
|
||||||
|
|
||||||
* repository: The GitHub `repository` which had an event.All periods have been replaced by % - such that foo.bar becomes foo%bar - and all other special characters aside from - and _ have been stripped. (required)
|
|
||||||
"""
|
|
||||||
|
|
||||||
ref = {
|
|
||||||
'exchange': 'task-group-defined',
|
|
||||||
'name': 'taskGroupDefined',
|
|
||||||
'routingKey': [
|
|
||||||
{
|
|
||||||
'constant': 'primary',
|
|
||||||
'multipleWords': False,
|
|
||||||
'name': 'routingKeyKind',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'multipleWords': False,
|
|
||||||
'name': 'organization',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'multipleWords': False,
|
|
||||||
'name': 'repository',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'schema': 'v1/task-group-defined-message.json#',
|
|
||||||
}
|
|
||||||
return self._makeTopicExchange(ref, *args, **kwargs)
|
|
||||||
|
|
||||||
funcinfo = {
|
funcinfo = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,14 +30,13 @@ class Hooks(BaseClient):
|
||||||
* `['0 0 9,21 * * 1-5', '0 0 12 * * 0,6']` -- weekdays at 9:00 and 21:00 UTC, weekends at noon
|
* `['0 0 9,21 * * 1-5', '0 0 12 * * 0,6']` -- weekdays at 9:00 and 21:00 UTC, weekends at noon
|
||||||
|
|
||||||
The task definition is used as a JSON-e template, with a context depending on how it is fired. See
|
The task definition is used as a JSON-e template, with a context depending on how it is fired. See
|
||||||
[/docs/reference/core/taskcluster-hooks/docs/firing-hooks](firing-hooks)
|
https://docs.taskcluster.net/reference/core/taskcluster-hooks/docs/firing-hooks
|
||||||
for more information.
|
for more information.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
classOptions = {
|
classOptions = {
|
||||||
|
"baseUrl": "https://hooks.taskcluster.net/v1/"
|
||||||
}
|
}
|
||||||
serviceName = 'hooks'
|
|
||||||
apiVersion = 'v1'
|
|
||||||
|
|
||||||
def ping(self, *args, **kwargs):
|
def ping(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -108,9 +108,8 @@ class Index(BaseClient):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
classOptions = {
|
classOptions = {
|
||||||
|
"baseUrl": "https://index.taskcluster.net/v1/"
|
||||||
}
|
}
|
||||||
serviceName = 'index'
|
|
||||||
apiVersion = 'v1'
|
|
||||||
|
|
||||||
def ping(self, *args, **kwargs):
|
def ping(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -18,21 +18,8 @@ class Login(BaseClient):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
classOptions = {
|
classOptions = {
|
||||||
|
"baseUrl": "https://login.taskcluster.net/v1"
|
||||||
}
|
}
|
||||||
serviceName = 'login'
|
|
||||||
apiVersion = 'v1'
|
|
||||||
|
|
||||||
def ping(self, *args, **kwargs):
|
|
||||||
"""
|
|
||||||
Ping Server
|
|
||||||
|
|
||||||
Respond without doing anything.
|
|
||||||
This endpoint is used to check that the service is up.
|
|
||||||
|
|
||||||
This method is ``stable``
|
|
||||||
"""
|
|
||||||
|
|
||||||
return self._makeApiCall(self.funcinfo["ping"], *args, **kwargs)
|
|
||||||
|
|
||||||
def oidcCredentials(self, *args, **kwargs):
|
def oidcCredentials(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
@ -50,7 +37,7 @@ class Login(BaseClient):
|
||||||
```
|
```
|
||||||
|
|
||||||
The `access_token` is first verified against the named
|
The `access_token` is first verified against the named
|
||||||
:provider, then passed to the provider's APIBuilder to retrieve a user
|
:provider, then passed to the provider's API to retrieve a user
|
||||||
profile. That profile is then used to generate Taskcluster credentials
|
profile. That profile is then used to generate Taskcluster credentials
|
||||||
appropriate to the user. Note that the resulting credentials may or may
|
appropriate to the user. Note that the resulting credentials may or may
|
||||||
not include a `certificate` property. Callers should be prepared for either
|
not include a `certificate` property. Callers should be prepared for either
|
||||||
|
@ -60,19 +47,31 @@ class Login(BaseClient):
|
||||||
monitor this expiration and refresh the credentials if necessary, by calling
|
monitor this expiration and refresh the credentials if necessary, by calling
|
||||||
this endpoint again, if they have expired.
|
this endpoint again, if they have expired.
|
||||||
|
|
||||||
This method gives output: ``v1/oidc-credentials-response.json#``
|
This method gives output: ``http://schemas.taskcluster.net/login/v1/oidc-credentials-response.json``
|
||||||
|
|
||||||
This method is ``experimental``
|
This method is ``experimental``
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return self._makeApiCall(self.funcinfo["oidcCredentials"], *args, **kwargs)
|
return self._makeApiCall(self.funcinfo["oidcCredentials"], *args, **kwargs)
|
||||||
|
|
||||||
|
def ping(self, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Ping Server
|
||||||
|
|
||||||
|
Respond without doing anything.
|
||||||
|
This endpoint is used to check that the service is up.
|
||||||
|
|
||||||
|
This method is ``stable``
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self._makeApiCall(self.funcinfo["ping"], *args, **kwargs)
|
||||||
|
|
||||||
funcinfo = {
|
funcinfo = {
|
||||||
"oidcCredentials": {
|
"oidcCredentials": {
|
||||||
'args': ['provider'],
|
'args': ['provider'],
|
||||||
'method': 'get',
|
'method': 'get',
|
||||||
'name': 'oidcCredentials',
|
'name': 'oidcCredentials',
|
||||||
'output': 'v1/oidc-credentials-response.json#',
|
'output': 'http://schemas.taskcluster.net/login/v1/oidc-credentials-response.json',
|
||||||
'route': '/oidc-credentials/<provider>',
|
'route': '/oidc-credentials/<provider>',
|
||||||
'stability': 'experimental',
|
'stability': 'experimental',
|
||||||
},
|
},
|
||||||
|
|
|
@ -19,9 +19,8 @@ class Notify(BaseClient):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
classOptions = {
|
classOptions = {
|
||||||
|
"baseUrl": "https://notify.taskcluster.net/v1/"
|
||||||
}
|
}
|
||||||
serviceName = 'notify'
|
|
||||||
apiVersion = 'v1'
|
|
||||||
|
|
||||||
def ping(self, *args, **kwargs):
|
def ping(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -22,21 +22,21 @@ class Pulse(BaseClient):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
classOptions = {
|
classOptions = {
|
||||||
|
"baseUrl": "https://pulse.taskcluster.net/v1"
|
||||||
}
|
}
|
||||||
serviceName = 'pulse'
|
|
||||||
apiVersion = 'v1'
|
|
||||||
|
|
||||||
def ping(self, *args, **kwargs):
|
def overview(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Ping Server
|
Rabbit Overview
|
||||||
|
|
||||||
Respond without doing anything.
|
Get an overview of the Rabbit cluster.
|
||||||
This endpoint is used to check that the service is up.
|
|
||||||
|
|
||||||
This method is ``stable``
|
This method gives output: ``http://schemas.taskcluster.net/pulse/v1/rabbit-overview.json``
|
||||||
|
|
||||||
|
This method is ``experimental``
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return self._makeApiCall(self.funcinfo["ping"], *args, **kwargs)
|
return self._makeApiCall(self.funcinfo["overview"], *args, **kwargs)
|
||||||
|
|
||||||
def listNamespaces(self, *args, **kwargs):
|
def listNamespaces(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
@ -46,9 +46,9 @@ class Pulse(BaseClient):
|
||||||
|
|
||||||
This will list up to 1000 namespaces. If more namespaces are present a
|
This will list up to 1000 namespaces. If more namespaces are present a
|
||||||
`continuationToken` will be returned, which can be given in the next
|
`continuationToken` will be returned, which can be given in the next
|
||||||
request. For the initial request, do not provide continuation token.
|
request. For the initial request, do not provide continuation.
|
||||||
|
|
||||||
This method gives output: ``v1/list-namespaces-response.json#``
|
This method gives output: ``http://schemas.taskcluster.net/pulse/v1/list-namespaces-response.json``
|
||||||
|
|
||||||
This method is ``experimental``
|
This method is ``experimental``
|
||||||
"""
|
"""
|
||||||
|
@ -62,7 +62,7 @@ class Pulse(BaseClient):
|
||||||
Get public information about a single namespace. This is the same information
|
Get public information about a single namespace. This is the same information
|
||||||
as returned by `listNamespaces`.
|
as returned by `listNamespaces`.
|
||||||
|
|
||||||
This method gives output: ``v1/namespace.json#``
|
This method gives output: ``http://schemas.taskcluster.net/pulse/v1/namespace.json``
|
||||||
|
|
||||||
This method is ``experimental``
|
This method is ``experimental``
|
||||||
"""
|
"""
|
||||||
|
@ -73,35 +73,43 @@ class Pulse(BaseClient):
|
||||||
"""
|
"""
|
||||||
Claim a namespace
|
Claim a namespace
|
||||||
|
|
||||||
Claim a namespace, returning a connection string with access to that namespace
|
Claim a namespace, returning a username and password with access to that
|
||||||
good for use until the `reclaimAt` time in the response body. The connection
|
namespace good for a short time. Clients should call this endpoint again
|
||||||
string can be used as many times as desired during this period, but must not
|
at the re-claim time given in the response, as the password will be rotated
|
||||||
be used after `reclaimAt`.
|
soon after that time. The namespace will expire, and any associated queues
|
||||||
|
and exchanges will be deleted, at the given expiration time.
|
||||||
|
|
||||||
Connections made with this connection string may persist beyond `reclaimAt`,
|
The `expires` and `contact` properties can be updated at any time in a reclaim
|
||||||
although it should not persist forever. 24 hours is a good maximum, and this
|
operation.
|
||||||
service will terminate connections after 72 hours (although this value is
|
|
||||||
configurable).
|
|
||||||
|
|
||||||
The specified `expires` time updates any existing expiration times. Connections
|
This method takes input: ``http://schemas.taskcluster.net/pulse/v1/namespace-request.json``
|
||||||
for expired namespaces will be terminated.
|
|
||||||
|
|
||||||
This method takes input: ``v1/namespace-request.json#``
|
This method gives output: ``http://schemas.taskcluster.net/pulse/v1/namespace-response.json``
|
||||||
|
|
||||||
This method gives output: ``v1/namespace-response.json#``
|
|
||||||
|
|
||||||
This method is ``experimental``
|
This method is ``experimental``
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return self._makeApiCall(self.funcinfo["claimNamespace"], *args, **kwargs)
|
return self._makeApiCall(self.funcinfo["claimNamespace"], *args, **kwargs)
|
||||||
|
|
||||||
|
def ping(self, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Ping Server
|
||||||
|
|
||||||
|
Respond without doing anything.
|
||||||
|
This endpoint is used to check that the service is up.
|
||||||
|
|
||||||
|
This method is ``stable``
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self._makeApiCall(self.funcinfo["ping"], *args, **kwargs)
|
||||||
|
|
||||||
funcinfo = {
|
funcinfo = {
|
||||||
"claimNamespace": {
|
"claimNamespace": {
|
||||||
'args': ['namespace'],
|
'args': ['namespace'],
|
||||||
'input': 'v1/namespace-request.json#',
|
'input': 'http://schemas.taskcluster.net/pulse/v1/namespace-request.json',
|
||||||
'method': 'post',
|
'method': 'post',
|
||||||
'name': 'claimNamespace',
|
'name': 'claimNamespace',
|
||||||
'output': 'v1/namespace-response.json#',
|
'output': 'http://schemas.taskcluster.net/pulse/v1/namespace-response.json',
|
||||||
'route': '/namespace/<namespace>',
|
'route': '/namespace/<namespace>',
|
||||||
'stability': 'experimental',
|
'stability': 'experimental',
|
||||||
},
|
},
|
||||||
|
@ -109,8 +117,8 @@ class Pulse(BaseClient):
|
||||||
'args': [],
|
'args': [],
|
||||||
'method': 'get',
|
'method': 'get',
|
||||||
'name': 'listNamespaces',
|
'name': 'listNamespaces',
|
||||||
'output': 'v1/list-namespaces-response.json#',
|
'output': 'http://schemas.taskcluster.net/pulse/v1/list-namespaces-response.json',
|
||||||
'query': ['limit', 'continuationToken'],
|
'query': ['limit', 'continuation'],
|
||||||
'route': '/namespaces',
|
'route': '/namespaces',
|
||||||
'stability': 'experimental',
|
'stability': 'experimental',
|
||||||
},
|
},
|
||||||
|
@ -118,10 +126,18 @@ class Pulse(BaseClient):
|
||||||
'args': ['namespace'],
|
'args': ['namespace'],
|
||||||
'method': 'get',
|
'method': 'get',
|
||||||
'name': 'namespace',
|
'name': 'namespace',
|
||||||
'output': 'v1/namespace.json#',
|
'output': 'http://schemas.taskcluster.net/pulse/v1/namespace.json',
|
||||||
'route': '/namespace/<namespace>',
|
'route': '/namespace/<namespace>',
|
||||||
'stability': 'experimental',
|
'stability': 'experimental',
|
||||||
},
|
},
|
||||||
|
"overview": {
|
||||||
|
'args': [],
|
||||||
|
'method': 'get',
|
||||||
|
'name': 'overview',
|
||||||
|
'output': 'http://schemas.taskcluster.net/pulse/v1/rabbit-overview.json',
|
||||||
|
'route': '/overview',
|
||||||
|
'stability': 'experimental',
|
||||||
|
},
|
||||||
"ping": {
|
"ping": {
|
||||||
'args': [],
|
'args': [],
|
||||||
'method': 'get',
|
'method': 'get',
|
||||||
|
|
|
@ -13,7 +13,8 @@ _defaultConfig = config
|
||||||
|
|
||||||
class PurgeCache(BaseClient):
|
class PurgeCache(BaseClient):
|
||||||
"""
|
"""
|
||||||
The purge-cache service is responsible for publishing a pulse
|
The purge-cache service, typically available at
|
||||||
|
`purge-cache.taskcluster.net`, is responsible for publishing a pulse
|
||||||
message for workers, so they can purge cache upon request.
|
message for workers, so they can purge cache upon request.
|
||||||
|
|
||||||
This document describes the API end-point for publishing the pulse
|
This document describes the API end-point for publishing the pulse
|
||||||
|
@ -21,9 +22,8 @@ class PurgeCache(BaseClient):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
classOptions = {
|
classOptions = {
|
||||||
|
"baseUrl": "https://purge-cache.taskcluster.net/v1/"
|
||||||
}
|
}
|
||||||
serviceName = 'purge-cache'
|
|
||||||
apiVersion = 'v1'
|
|
||||||
|
|
||||||
def ping(self, *args, **kwargs):
|
def ping(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -22,10 +22,8 @@ class PurgeCacheEvents(BaseClient):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
classOptions = {
|
classOptions = {
|
||||||
"exchangePrefix": "exchange/taskcluster-purge-cache/v1/",
|
"exchangePrefix": "exchange/taskcluster-purge-cache/v1/"
|
||||||
}
|
}
|
||||||
serviceName = 'purge-cache'
|
|
||||||
apiVersion = 'v1'
|
|
||||||
|
|
||||||
def purgeCache(self, *args, **kwargs):
|
def purgeCache(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -25,9 +25,8 @@ class Queue(BaseClient):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
classOptions = {
|
classOptions = {
|
||||||
|
"baseUrl": "https://queue.taskcluster.net/v1/"
|
||||||
}
|
}
|
||||||
serviceName = 'queue'
|
|
||||||
apiVersion = 'v1'
|
|
||||||
|
|
||||||
def ping(self, *args, **kwargs):
|
def ping(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -63,10 +63,8 @@ class QueueEvents(BaseClient):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
classOptions = {
|
classOptions = {
|
||||||
"exchangePrefix": "exchange/taskcluster-queue/v1/",
|
"exchangePrefix": "exchange/taskcluster-queue/v1/"
|
||||||
}
|
}
|
||||||
serviceName = 'queue'
|
|
||||||
apiVersion = 'v1'
|
|
||||||
|
|
||||||
def taskDefined(self, *args, **kwargs):
|
def taskDefined(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -23,9 +23,8 @@ class Secrets(BaseClient):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
classOptions = {
|
classOptions = {
|
||||||
|
"baseUrl": "https://secrets.taskcluster.net/v1/"
|
||||||
}
|
}
|
||||||
serviceName = 'secrets'
|
|
||||||
apiVersion = 'v1'
|
|
||||||
|
|
||||||
def ping(self, *args, **kwargs):
|
def ping(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -23,10 +23,8 @@ class TreeherderEvents(BaseClient):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
classOptions = {
|
classOptions = {
|
||||||
"exchangePrefix": "exchange/taskcluster-treeherder/v1/",
|
"exchangePrefix": "exchange/taskcluster-treeherder/v1/"
|
||||||
}
|
}
|
||||||
serviceName = 'treeherder'
|
|
||||||
apiVersion = 'v1'
|
|
||||||
|
|
||||||
def jobs(self, *args, **kwargs):
|
def jobs(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
@ -35,7 +33,7 @@ class TreeherderEvents(BaseClient):
|
||||||
When a task run is scheduled or resolved, a message is posted to
|
When a task run is scheduled or resolved, a message is posted to
|
||||||
this exchange in a Treeherder consumable format.
|
this exchange in a Treeherder consumable format.
|
||||||
|
|
||||||
This exchange outputs: ``v1/pulse-job.json#``This exchange takes the following keys:
|
This exchange outputs: ``http://schemas.taskcluster.net/taskcluster-treeherder/v1/pulse-job.json#``This exchange takes the following keys:
|
||||||
|
|
||||||
* destination: destination (required)
|
* destination: destination (required)
|
||||||
|
|
||||||
|
@ -61,7 +59,7 @@ class TreeherderEvents(BaseClient):
|
||||||
'name': 'reserved',
|
'name': 'reserved',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
'schema': 'v1/pulse-job.json#',
|
'schema': 'http://schemas.taskcluster.net/taskcluster-treeherder/v1/pulse-job.json#',
|
||||||
}
|
}
|
||||||
return self._makeTopicExchange(ref, *args, **kwargs)
|
return self._makeTopicExchange(ref, *args, **kwargs)
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
# -*- coding: UTF-8 -*-
|
|
||||||
from __future__ import absolute_import, division, print_function
|
from __future__ import absolute_import, division, print_function
|
||||||
import re
|
import re
|
||||||
import json
|
import json
|
||||||
|
@ -11,6 +10,7 @@ import requests.exceptions
|
||||||
import slugid
|
import slugid
|
||||||
import time
|
import time
|
||||||
import six
|
import six
|
||||||
|
import sys
|
||||||
import random
|
import random
|
||||||
|
|
||||||
from . import exceptions
|
from . import exceptions
|
||||||
|
@ -298,7 +298,7 @@ def putFile(filename, url, contentType):
|
||||||
with open(filename, 'rb') as f:
|
with open(filename, 'rb') as f:
|
||||||
contentLength = os.fstat(f.fileno()).st_size
|
contentLength = os.fstat(f.fileno()).st_size
|
||||||
return makeHttpRequest('put', url, f, headers={
|
return makeHttpRequest('put', url, f, headers={
|
||||||
'Content-Length': str(contentLength),
|
'Content-Length': contentLength,
|
||||||
'Content-Type': contentType,
|
'Content-Type': contentType,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -319,30 +319,89 @@ def isExpired(certificate):
|
||||||
return expiry < int(time.time() * 1000) + 20 * 60
|
return expiry < int(time.time() * 1000) + 20 * 60
|
||||||
|
|
||||||
|
|
||||||
def optionsFromEnvironment(defaults=None):
|
def authenticate(description=None):
|
||||||
"""Fetch root URL and credentials from the standard TASKCLUSTER_…
|
"""
|
||||||
environment variables and return them in a format suitable for passing to a
|
Open a web-browser to login.taskcluster.net and listen on localhost for
|
||||||
client constructor."""
|
a callback with credentials in query-string.
|
||||||
options = defaults or {}
|
|
||||||
credentials = options.get('credentials', {})
|
|
||||||
|
|
||||||
rootUrl = os.environ.get('TASKCLUSTER_ROOT_URL')
|
The description will be shown on login.taskcluster.net, if not provided
|
||||||
if rootUrl:
|
a default message with script path will be displayed.
|
||||||
options['rootUrl'] = rootUrl
|
"""
|
||||||
|
# Importing here to avoid loading these 'obscure' module before it's needed.
|
||||||
|
# Most clients won't use this feature, so we don't want issues with these
|
||||||
|
# modules to affect the library. Maybe they don't work in some environments
|
||||||
|
import webbrowser
|
||||||
|
from six.moves import urllib
|
||||||
|
from six.moves.urllib.parse import quote
|
||||||
|
import BaseHTTPServer
|
||||||
|
|
||||||
clientId = os.environ.get('TASKCLUSTER_CLIENT_ID')
|
if not description:
|
||||||
if clientId:
|
script = '[interpreter/unknown]'
|
||||||
credentials['clientId'] = clientId
|
main = sys.modules.get('__main__', None)
|
||||||
|
if main and hasattr(main, '__file__'):
|
||||||
|
script = os.path.abspath(main.__file__)
|
||||||
|
description = (
|
||||||
|
"Python script: `%s`\n\nWould like some temporary credentials."
|
||||||
|
% script
|
||||||
|
)
|
||||||
|
|
||||||
accessToken = os.environ.get('TASKCLUSTER_ACCESS_TOKEN')
|
creds = [None]
|
||||||
if accessToken:
|
|
||||||
credentials['accessToken'] = accessToken
|
|
||||||
|
|
||||||
certificate = os.environ.get('TASKCLUSTER_CERTIFICATE')
|
class AuthCallBackRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
|
||||||
if certificate:
|
def log_message(format, *args):
|
||||||
credentials['certificate'] = certificate
|
pass
|
||||||
|
|
||||||
if credentials:
|
def do_GET(self):
|
||||||
options['credentials'] = credentials
|
url = urllib.parse.urlparse(self.path)
|
||||||
|
query = urllib.parse.parse_qs(url.query)
|
||||||
|
clientId = query.get('clientId', [None])[0]
|
||||||
|
accessToken = query.get('accessToken', [None])[0]
|
||||||
|
certificate = query.get('certificate', [None])[0]
|
||||||
|
hasCreds = clientId and accessToken and certificate
|
||||||
|
if hasCreds:
|
||||||
|
creds[0] = {
|
||||||
|
"clientId": clientId,
|
||||||
|
"accessToken": accessToken,
|
||||||
|
"certificate": certificate
|
||||||
|
}
|
||||||
|
self.send_response(200)
|
||||||
|
self.send_header('Content-type', 'text/html')
|
||||||
|
self.end_headers()
|
||||||
|
if hasCreds:
|
||||||
|
self.wfile.write("""
|
||||||
|
<h1>Credentials transferred successfully</h1>
|
||||||
|
<i>You can close this window now.</i>
|
||||||
|
<script>window.close();</script>
|
||||||
|
""")
|
||||||
|
else:
|
||||||
|
self.wfile.write("""
|
||||||
|
<h1>Transfer of credentials failed!</h1>
|
||||||
|
<p>Something went wrong, you can navigate back and try again...</p>
|
||||||
|
""")
|
||||||
|
return
|
||||||
|
|
||||||
return options
|
# Create server on localhost at random port
|
||||||
|
retries = 5
|
||||||
|
while retries > 0:
|
||||||
|
try:
|
||||||
|
server = BaseHTTPServer.HTTPServer(('', 0), AuthCallBackRequestHandler)
|
||||||
|
except:
|
||||||
|
retries -= 1
|
||||||
|
break
|
||||||
|
port = server.server_address[1]
|
||||||
|
|
||||||
|
query = "?target=" + quote('http://localhost:' + str(port), '')
|
||||||
|
query += "&description=" + quote(description, '')
|
||||||
|
|
||||||
|
webbrowser.open('https://login.taskcluster.net' + query, 1, True)
|
||||||
|
print("")
|
||||||
|
print("-------------------------------------------------------")
|
||||||
|
print(" Opening browser window to login.taskcluster.net")
|
||||||
|
print(" Asking you to grant temporary credentials to:")
|
||||||
|
print(" http://localhost:" + str(port))
|
||||||
|
print("-------------------------------------------------------")
|
||||||
|
print("")
|
||||||
|
|
||||||
|
while not creds[0]:
|
||||||
|
server.handle_request()
|
||||||
|
return creds[0]
|
||||||
|
|
|
@ -21,7 +21,6 @@ class TestAuthenticationAsync(base.TCTest):
|
||||||
async def x():
|
async def x():
|
||||||
async with subjectAsync.createSession(loop=loop) as session:
|
async with subjectAsync.createSession(loop=loop) as session:
|
||||||
client = subjectAsync.Auth({
|
client = subjectAsync.Auth({
|
||||||
'rootUrl': self.real_root_url,
|
|
||||||
'credentials': {
|
'credentials': {
|
||||||
'clientId': 'tester',
|
'clientId': 'tester',
|
||||||
'accessToken': 'no-secret',
|
'accessToken': 'no-secret',
|
||||||
|
@ -50,14 +49,13 @@ class TestAuthenticationAsync(base.TCTest):
|
||||||
['test:xyz'],
|
['test:xyz'],
|
||||||
)
|
)
|
||||||
client = subjectAsync.Auth({
|
client = subjectAsync.Auth({
|
||||||
'rootUrl': self.real_root_url,
|
|
||||||
'credentials': tempCred,
|
'credentials': tempCred,
|
||||||
}, session=session)
|
}, session=session)
|
||||||
|
|
||||||
result = await client.testAuthenticate({
|
result = client.testAuthenticate({
|
||||||
'clientScopes': ['test:*'],
|
'clientScopes': ['test:*'],
|
||||||
'requiredScopes': ['test:xyz'],
|
'requiredScopes': ['test:xyz'],
|
||||||
})
|
})
|
||||||
self.assertEqual(result, {'scopes': ['test:xyz'], 'clientId': 'tester'})
|
self.assertEqual(result, {'scopes': ['test:xyz'], 'clientId': 'tester'})
|
||||||
|
|
||||||
loop.run_until_complete(x())
|
loop.run_until_complete
|
||||||
|
|
|
@ -7,7 +7,6 @@ from six.moves import urllib
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import json
|
import json
|
||||||
import copy
|
|
||||||
|
|
||||||
import mock
|
import mock
|
||||||
import httmock
|
import httmock
|
||||||
|
@ -17,7 +16,6 @@ import base
|
||||||
import taskcluster.auth as subject
|
import taskcluster.auth as subject
|
||||||
import taskcluster.exceptions as exc
|
import taskcluster.exceptions as exc
|
||||||
import taskcluster.utils as utils
|
import taskcluster.utils as utils
|
||||||
import taskcluster_urls as liburls
|
|
||||||
|
|
||||||
|
|
||||||
class ClientTest(base.TCTest):
|
class ClientTest(base.TCTest):
|
||||||
|
@ -47,7 +45,7 @@ class ClientTest(base.TCTest):
|
||||||
]
|
]
|
||||||
self.apiRef = base.createApiRef(entries=entries)
|
self.apiRef = base.createApiRef(entries=entries)
|
||||||
self.clientClass = subject.createApiClient('testApi', self.apiRef)
|
self.clientClass = subject.createApiClient('testApi', self.apiRef)
|
||||||
self.client = self.clientClass({'rootUrl': self.test_root_url})
|
self.client = self.clientClass()
|
||||||
# Patch time.sleep so that we don't delay tests
|
# Patch time.sleep so that we don't delay tests
|
||||||
sleepPatcher = mock.patch('time.sleep')
|
sleepPatcher = mock.patch('time.sleep')
|
||||||
sleepSleep = sleepPatcher.start()
|
sleepSleep = sleepPatcher.start()
|
||||||
|
@ -58,32 +56,6 @@ class ClientTest(base.TCTest):
|
||||||
time.sleep = self.realTimeSleep
|
time.sleep = self.realTimeSleep
|
||||||
|
|
||||||
|
|
||||||
class TestConstructorOptions(ClientTest):
|
|
||||||
|
|
||||||
def test_baseUrl_not_allowed(self):
|
|
||||||
with self.assertRaises(exc.TaskclusterFailure):
|
|
||||||
self.clientClass({'baseUrl': 'https://bogus.net'})
|
|
||||||
|
|
||||||
def test_rootUrl_set_correctly(self):
|
|
||||||
client = self.clientClass({'rootUrl': self.test_root_url})
|
|
||||||
self.assertEqual(client.options['rootUrl'], self.test_root_url)
|
|
||||||
|
|
||||||
def test_apiVersion_set_correctly(self):
|
|
||||||
client = self.clientClass({'rootUrl': self.test_root_url})
|
|
||||||
self.assertEqual(client.apiVersion, 'v1')
|
|
||||||
|
|
||||||
def test_apiVersion_set_correctly_default(self):
|
|
||||||
apiRef = copy.deepcopy(self.apiRef)
|
|
||||||
del apiRef['reference']['apiVersion']
|
|
||||||
clientClass = subject.createApiClient('testApi', apiRef)
|
|
||||||
client = clientClass({'rootUrl': self.test_root_url})
|
|
||||||
self.assertEqual(client.apiVersion, 'v1')
|
|
||||||
|
|
||||||
def test_serviceName_set_correctly(self):
|
|
||||||
client = self.clientClass({'rootUrl': self.test_root_url})
|
|
||||||
self.assertEqual(client.serviceName, 'fake')
|
|
||||||
|
|
||||||
|
|
||||||
class TestSubArgsInRoute(ClientTest):
|
class TestSubArgsInRoute(ClientTest):
|
||||||
|
|
||||||
def test_valid_no_subs(self):
|
def test_valid_no_subs(self):
|
||||||
|
@ -196,10 +168,10 @@ class TestProcessArgs(ClientTest):
|
||||||
|
|
||||||
def test_calling_convention_1_with_payload(self):
|
def test_calling_convention_1_with_payload(self):
|
||||||
params, payload, query, _, _ = self.client._processArgs(
|
params, payload, query, _, _ = self.client._processArgs(
|
||||||
{'args': ['k1', 'k2'], 'name': 'test', 'input': True},
|
{'args': ['k1', 'k2'], 'name': 'test', 'input': True},
|
||||||
1,
|
1,
|
||||||
2,
|
2,
|
||||||
{'A': 123}
|
{'A': 123}
|
||||||
)
|
)
|
||||||
self.assertEqual(params, {'k1': 1, 'k2': 2})
|
self.assertEqual(params, {'k1': 1, 'k2': 2})
|
||||||
self.assertEqual(payload, {'A': 123})
|
self.assertEqual(payload, {'A': 123})
|
||||||
|
@ -213,8 +185,8 @@ class TestProcessArgs(ClientTest):
|
||||||
|
|
||||||
def test_calling_convention_2_with_payload(self):
|
def test_calling_convention_2_with_payload(self):
|
||||||
params, payload, query, _, _ = self.client._processArgs(
|
params, payload, query, _, _ = self.client._processArgs(
|
||||||
{'args': ['k1', 'k2'], 'name': 'test', 'input': True},
|
{'args': ['k1', 'k2'], 'name': 'test', 'input': True},
|
||||||
{'A': 123}, k1=1, k2=2
|
{'A': 123}, k1=1, k2=2
|
||||||
)
|
)
|
||||||
self.assertEqual(params, {'k1': 1, 'k2': 2})
|
self.assertEqual(params, {'k1': 1, 'k2': 2})
|
||||||
self.assertEqual(payload, {'A': 123})
|
self.assertEqual(payload, {'A': 123})
|
||||||
|
@ -222,8 +194,8 @@ class TestProcessArgs(ClientTest):
|
||||||
|
|
||||||
def test_calling_convention_3_without_payload_without_query(self):
|
def test_calling_convention_3_without_payload_without_query(self):
|
||||||
params, payload, query, _, _ = self.client._processArgs(
|
params, payload, query, _, _ = self.client._processArgs(
|
||||||
{'args': ['k1', 'k2'], 'name': 'test'},
|
{'args': ['k1', 'k2'], 'name': 'test'},
|
||||||
params={'k1': 1, 'k2': 2}
|
params={'k1': 1, 'k2': 2}
|
||||||
)
|
)
|
||||||
self.assertEqual(params, {'k1': 1, 'k2': 2})
|
self.assertEqual(params, {'k1': 1, 'k2': 2})
|
||||||
self.assertEqual(payload, None)
|
self.assertEqual(payload, None)
|
||||||
|
@ -231,9 +203,9 @@ class TestProcessArgs(ClientTest):
|
||||||
|
|
||||||
def test_calling_convention_3_with_payload_without_query(self):
|
def test_calling_convention_3_with_payload_without_query(self):
|
||||||
params, payload, query, _, _ = self.client._processArgs(
|
params, payload, query, _, _ = self.client._processArgs(
|
||||||
{'args': ['k1', 'k2'], 'name': 'test'},
|
{'args': ['k1', 'k2'], 'name': 'test'},
|
||||||
params={'k1': 1, 'k2': 2},
|
params={'k1': 1, 'k2': 2},
|
||||||
payload={'A': 123}
|
payload={'A': 123}
|
||||||
)
|
)
|
||||||
self.assertEqual(params, {'k1': 1, 'k2': 2})
|
self.assertEqual(params, {'k1': 1, 'k2': 2})
|
||||||
self.assertEqual(payload, {'A': 123})
|
self.assertEqual(payload, {'A': 123})
|
||||||
|
@ -241,10 +213,10 @@ class TestProcessArgs(ClientTest):
|
||||||
|
|
||||||
def test_calling_convention_3_with_payload_with_query(self):
|
def test_calling_convention_3_with_payload_with_query(self):
|
||||||
params, payload, query, _, _ = self.client._processArgs(
|
params, payload, query, _, _ = self.client._processArgs(
|
||||||
{'args': ['k1', 'k2'], 'name': 'test'},
|
{'args': ['k1', 'k2'], 'name': 'test'},
|
||||||
params={'k1': 1, 'k2': 2},
|
params={'k1': 1, 'k2': 2},
|
||||||
payload={'A': 123},
|
payload={'A': 123},
|
||||||
query={'B': 456}
|
query={'B': 456}
|
||||||
)
|
)
|
||||||
self.assertEqual(params, {'k1': 1, 'k2': 2})
|
self.assertEqual(params, {'k1': 1, 'k2': 2})
|
||||||
self.assertEqual(payload, {'A': 123})
|
self.assertEqual(payload, {'A': 123})
|
||||||
|
@ -252,9 +224,9 @@ class TestProcessArgs(ClientTest):
|
||||||
|
|
||||||
def test_calling_convention_3_without_payload_with_query(self):
|
def test_calling_convention_3_without_payload_with_query(self):
|
||||||
params, payload, query, _, _ = self.client._processArgs(
|
params, payload, query, _, _ = self.client._processArgs(
|
||||||
{'args': ['k1', 'k2'], 'name': 'test'},
|
{'args': ['k1', 'k2'], 'name': 'test'},
|
||||||
params={'k1': 1, 'k2': 2},
|
params={'k1': 1, 'k2': 2},
|
||||||
query={'B': 456}
|
query={'B': 456}
|
||||||
)
|
)
|
||||||
self.assertEqual(params, {'k1': 1, 'k2': 2})
|
self.assertEqual(params, {'k1': 1, 'k2': 2})
|
||||||
self.assertEqual(payload, None)
|
self.assertEqual(payload, None)
|
||||||
|
@ -262,11 +234,11 @@ class TestProcessArgs(ClientTest):
|
||||||
|
|
||||||
def test_calling_convention_3_with_positional_arguments_with_payload_with_query(self):
|
def test_calling_convention_3_with_positional_arguments_with_payload_with_query(self):
|
||||||
params, payload, query, _, _ = self.client._processArgs(
|
params, payload, query, _, _ = self.client._processArgs(
|
||||||
{'args': ['k1', 'k2'], 'name': 'test'},
|
{'args': ['k1', 'k2'], 'name': 'test'},
|
||||||
1,
|
1,
|
||||||
2,
|
2,
|
||||||
query={'B': 456},
|
query={'B': 456},
|
||||||
payload={'A': 123}
|
payload={'A': 123}
|
||||||
)
|
)
|
||||||
self.assertEqual(params, {'k1': 1, 'k2': 2})
|
self.assertEqual(params, {'k1': 1, 'k2': 2})
|
||||||
self.assertEqual(payload, {'A': 123})
|
self.assertEqual(payload, {'A': 123})
|
||||||
|
@ -286,12 +258,12 @@ class TestProcessArgs(ClientTest):
|
||||||
def test_calling_convention_3_with_pos_args_same_as_param_kwarg_dict_vals_with_payload_with_query(self):
|
def test_calling_convention_3_with_pos_args_same_as_param_kwarg_dict_vals_with_payload_with_query(self):
|
||||||
with self.assertRaises(exc.TaskclusterFailure):
|
with self.assertRaises(exc.TaskclusterFailure):
|
||||||
params, payload, query, _, _ = self.client._processArgs(
|
params, payload, query, _, _ = self.client._processArgs(
|
||||||
{'args': ['k1', 'k2'], 'name': 'test'},
|
{'args': ['k1', 'k2'], 'name': 'test'},
|
||||||
1,
|
1,
|
||||||
2,
|
2,
|
||||||
params={'k1': 1, 'k2': 2},
|
params={'k1': 1, 'k2': 2},
|
||||||
query={'B': 456},
|
query={'B': 456},
|
||||||
payload={'A': 123}
|
payload={'A': 123}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -312,8 +284,6 @@ class ObjWithDotJson(object):
|
||||||
|
|
||||||
class TestMakeHttpRequest(ClientTest):
|
class TestMakeHttpRequest(ClientTest):
|
||||||
|
|
||||||
apiPath = liburls.api(ClientTest.test_root_url, 'fake', 'v1', 'test')
|
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
|
||||||
ClientTest.setUp(self)
|
ClientTest.setUp(self)
|
||||||
|
@ -323,8 +293,8 @@ class TestMakeHttpRequest(ClientTest):
|
||||||
expected = {'test': 'works'}
|
expected = {'test': 'works'}
|
||||||
p.return_value = ObjWithDotJson(200, expected)
|
p.return_value = ObjWithDotJson(200, expected)
|
||||||
|
|
||||||
v = self.client._makeHttpRequest('GET', 'test', None)
|
v = self.client._makeHttpRequest('GET', 'http://www.example.com', None)
|
||||||
p.assert_called_once_with('GET', self.apiPath, None, mock.ANY)
|
p.assert_called_once_with('GET', 'http://www.example.com', None, mock.ANY)
|
||||||
self.assertEqual(expected, v)
|
self.assertEqual(expected, v)
|
||||||
|
|
||||||
def test_success_first_try_payload(self):
|
def test_success_first_try_payload(self):
|
||||||
|
@ -332,8 +302,8 @@ class TestMakeHttpRequest(ClientTest):
|
||||||
expected = {'test': 'works'}
|
expected = {'test': 'works'}
|
||||||
p.return_value = ObjWithDotJson(200, expected)
|
p.return_value = ObjWithDotJson(200, expected)
|
||||||
|
|
||||||
v = self.client._makeHttpRequest('GET', 'test', {'payload': 2})
|
v = self.client._makeHttpRequest('GET', 'http://www.example.com', {'payload': 2})
|
||||||
p.assert_called_once_with('GET', self.apiPath,
|
p.assert_called_once_with('GET', 'http://www.example.com',
|
||||||
utils.dumpJson({'payload': 2}), mock.ANY)
|
utils.dumpJson({'payload': 2}), mock.ANY)
|
||||||
self.assertEqual(expected, v)
|
self.assertEqual(expected, v)
|
||||||
|
|
||||||
|
@ -348,10 +318,10 @@ class TestMakeHttpRequest(ClientTest):
|
||||||
ObjWithDotJson(200, expected)
|
ObjWithDotJson(200, expected)
|
||||||
]
|
]
|
||||||
p.side_effect = sideEffect
|
p.side_effect = sideEffect
|
||||||
expectedCalls = [mock.call('GET', self.apiPath, None, mock.ANY)
|
expectedCalls = [mock.call('GET', 'http://www.example.com', None, mock.ANY)
|
||||||
for x in range(self.client.options['maxRetries'])]
|
for x in range(self.client.options['maxRetries'])]
|
||||||
|
|
||||||
v = self.client._makeHttpRequest('GET', 'test', None)
|
v = self.client._makeHttpRequest('GET', 'http://www.example.com', None)
|
||||||
p.assert_has_calls(expectedCalls)
|
p.assert_has_calls(expectedCalls)
|
||||||
self.assertEqual(expected, v)
|
self.assertEqual(expected, v)
|
||||||
|
|
||||||
|
@ -373,12 +343,12 @@ class TestMakeHttpRequest(ClientTest):
|
||||||
ObjWithDotJson(200, {'got this': 'wrong'})
|
ObjWithDotJson(200, {'got this': 'wrong'})
|
||||||
]
|
]
|
||||||
p.side_effect = sideEffect
|
p.side_effect = sideEffect
|
||||||
expectedCalls = [mock.call('GET', self.apiPath, None, mock.ANY)
|
expectedCalls = [mock.call('GET', 'http://www.example.com', None, mock.ANY)
|
||||||
for x in range(self.client.options['maxRetries'] + 1)]
|
for x in range(self.client.options['maxRetries'] + 1)]
|
||||||
|
|
||||||
with self.assertRaises(exc.TaskclusterRestFailure):
|
with self.assertRaises(exc.TaskclusterRestFailure):
|
||||||
try:
|
try:
|
||||||
self.client._makeHttpRequest('GET', 'test', None)
|
self.client._makeHttpRequest('GET', 'http://www.example.com', None)
|
||||||
except exc.TaskclusterRestFailure as err:
|
except exc.TaskclusterRestFailure as err:
|
||||||
self.assertEqual('msg', str(err))
|
self.assertEqual('msg', str(err))
|
||||||
self.assertEqual(500, err.status_code)
|
self.assertEqual(500, err.status_code)
|
||||||
|
@ -397,34 +367,43 @@ class TestMakeHttpRequest(ClientTest):
|
||||||
ObjWithDotJson(200, expected)
|
ObjWithDotJson(200, expected)
|
||||||
]
|
]
|
||||||
p.side_effect = sideEffect
|
p.side_effect = sideEffect
|
||||||
expectedCalls = [mock.call('GET', self.apiPath, None, mock.ANY)
|
expectedCalls = [mock.call('GET', 'http://www.example.com', None, mock.ANY)
|
||||||
for x in range(self.client.options['maxRetries'])]
|
for x in range(self.client.options['maxRetries'])]
|
||||||
|
|
||||||
v = self.client._makeHttpRequest('GET', 'test', None)
|
v = self.client._makeHttpRequest('GET', 'http://www.example.com', None)
|
||||||
p.assert_has_calls(expectedCalls)
|
p.assert_has_calls(expectedCalls)
|
||||||
self.assertEqual(expected, v)
|
self.assertEqual(expected, v)
|
||||||
|
|
||||||
def test_failure_status_code(self):
|
def test_failure_status_code(self):
|
||||||
with mock.patch.object(utils, 'makeSingleHttpRequest') as p:
|
with mock.patch.object(utils, 'makeSingleHttpRequest') as p:
|
||||||
p.return_value = ObjWithDotJson(500, None)
|
p.return_value = ObjWithDotJson(500, None)
|
||||||
expectedCalls = [mock.call('GET', self.apiPath, None, mock.ANY)
|
expectedCalls = [mock.call('GET', 'http://www.example.com', None, mock.ANY)
|
||||||
for x in range(self.client.options['maxRetries'])]
|
for x in range(self.client.options['maxRetries'])]
|
||||||
with self.assertRaises(exc.TaskclusterRestFailure):
|
with self.assertRaises(exc.TaskclusterRestFailure):
|
||||||
self.client._makeHttpRequest('GET', 'test', None)
|
self.client._makeHttpRequest('GET', 'http://www.example.com', None)
|
||||||
p.assert_has_calls(expectedCalls)
|
p.assert_has_calls(expectedCalls)
|
||||||
|
|
||||||
def test_failure_connection_errors(self):
|
def test_failure_connection_errors(self):
|
||||||
with mock.patch.object(utils, 'makeSingleHttpRequest') as p:
|
with mock.patch.object(utils, 'makeSingleHttpRequest') as p:
|
||||||
p.side_effect = requests.exceptions.RequestException
|
p.side_effect = requests.exceptions.RequestException
|
||||||
expectedCalls = [mock.call('GET', self.apiPath, None, mock.ANY)
|
expectedCalls = [mock.call('GET', 'http://www.example.com', None, mock.ANY)
|
||||||
for x in range(self.client.options['maxRetries'])]
|
for x in range(self.client.options['maxRetries'])]
|
||||||
with self.assertRaises(exc.TaskclusterConnectionError):
|
with self.assertRaises(exc.TaskclusterConnectionError):
|
||||||
self.client._makeHttpRequest('GET', 'test', None)
|
self.client._makeHttpRequest('GET', 'http://www.example.com', None)
|
||||||
p.assert_has_calls(expectedCalls)
|
p.assert_has_calls(expectedCalls)
|
||||||
|
|
||||||
|
|
||||||
class TestOptions(ClientTest):
|
class TestOptions(ClientTest):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
ClientTest.setUp(self)
|
||||||
|
self.clientClass2 = subject.createApiClient('testApi', base.createApiRef())
|
||||||
|
self.client2 = self.clientClass2({'baseUrl': 'http://notlocalhost:5888/v2'})
|
||||||
|
|
||||||
|
def test_defaults_should_work(self):
|
||||||
|
self.assertEqual(self.client.options['baseUrl'], 'https://fake.taskcluster.net/v1')
|
||||||
|
self.assertEqual(self.client2.options['baseUrl'], 'http://notlocalhost:5888/v2')
|
||||||
|
|
||||||
def test_change_default_doesnt_change_previous_instances(self):
|
def test_change_default_doesnt_change_previous_instances(self):
|
||||||
prevMaxRetries = subject._defaultConfig['maxRetries']
|
prevMaxRetries = subject._defaultConfig['maxRetries']
|
||||||
with mock.patch.dict(subject._defaultConfig, {'maxRetries': prevMaxRetries + 1}):
|
with mock.patch.dict(subject._defaultConfig, {'maxRetries': prevMaxRetries + 1}):
|
||||||
|
@ -436,10 +415,7 @@ class TestOptions(ClientTest):
|
||||||
'clientId': u"\U0001F4A9",
|
'clientId': u"\U0001F4A9",
|
||||||
}
|
}
|
||||||
with self.assertRaises(exc.TaskclusterAuthFailure):
|
with self.assertRaises(exc.TaskclusterAuthFailure):
|
||||||
subject.Auth({
|
subject.Auth({'credentials': badCredentials})
|
||||||
'rootUrl': self.real_root_url,
|
|
||||||
'credentials': badCredentials,
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
class TestMakeApiCall(ClientTest):
|
class TestMakeApiCall(ClientTest):
|
||||||
|
@ -542,13 +518,13 @@ class TestTopicExchange(ClientTest):
|
||||||
self.assertEqual(expected, actual['routingKeyPattern'])
|
self.assertEqual(expected, actual['routingKeyPattern'])
|
||||||
|
|
||||||
def test_exchange(self):
|
def test_exchange(self):
|
||||||
expected = 'exchange/taskcluster-fake/v1/topicExchange'
|
expected = 'test/v1/topicExchange'
|
||||||
actual = self.client.topicName('')
|
actual = self.client.topicName('')
|
||||||
self.assertEqual(expected, actual['exchange'])
|
self.assertEqual(expected, actual['exchange'])
|
||||||
|
|
||||||
def test_exchange_trailing_slash(self):
|
def test_exchange_trailing_slash(self):
|
||||||
self.client.options['exchangePrefix'] = 'exchange/taskcluster-fake2/v1/'
|
self.client.options['exchangePrefix'] = 'test/v1/'
|
||||||
expected = 'exchange/taskcluster-fake2/v1/topicExchange'
|
expected = 'test/v1/topicExchange'
|
||||||
actual = self.client.topicName('')
|
actual = self.client.topicName('')
|
||||||
self.assertEqual(expected, actual['exchange'])
|
self.assertEqual(expected, actual['exchange'])
|
||||||
|
|
||||||
|
@ -580,17 +556,18 @@ class TestTopicExchange(ClientTest):
|
||||||
|
|
||||||
class TestBuildUrl(ClientTest):
|
class TestBuildUrl(ClientTest):
|
||||||
|
|
||||||
apiPath = liburls.api(ClientTest.test_root_url, 'fake', 'v1', 'two_args_no_input/arg0/arg1')
|
|
||||||
|
|
||||||
def test_build_url_positional(self):
|
def test_build_url_positional(self):
|
||||||
|
expected = 'https://fake.taskcluster.net/v1/two_args_no_input/arg0/arg1'
|
||||||
actual = self.client.buildUrl('two_args_no_input', 'arg0', 'arg1')
|
actual = self.client.buildUrl('two_args_no_input', 'arg0', 'arg1')
|
||||||
self.assertEqual(self.apiPath, actual)
|
self.assertEqual(expected, actual)
|
||||||
|
|
||||||
def test_build_url_keyword(self):
|
def test_build_url_keyword(self):
|
||||||
|
expected = 'https://fake.taskcluster.net/v1/two_args_no_input/arg0/arg1'
|
||||||
actual = self.client.buildUrl('two_args_no_input', arg0='arg0', arg1='arg1')
|
actual = self.client.buildUrl('two_args_no_input', arg0='arg0', arg1='arg1')
|
||||||
self.assertEqual(self.apiPath, actual)
|
self.assertEqual(expected, actual)
|
||||||
|
|
||||||
def test_build_url_query_string(self):
|
def test_build_url_query_string(self):
|
||||||
|
expected = 'https://fake.taskcluster.net/v1/two_args_no_input/arg0/arg1?qs0=1'
|
||||||
actual = self.client.buildUrl(
|
actual = self.client.buildUrl(
|
||||||
'two_args_no_input',
|
'two_args_no_input',
|
||||||
params={
|
params={
|
||||||
|
@ -599,7 +576,7 @@ class TestBuildUrl(ClientTest):
|
||||||
},
|
},
|
||||||
query={'qs0': 1}
|
query={'qs0': 1}
|
||||||
)
|
)
|
||||||
self.assertEqual(self.apiPath + '?qs0=1', actual)
|
self.assertEqual(expected, actual)
|
||||||
|
|
||||||
def test_fails_to_build_url_for_missing_method(self):
|
def test_fails_to_build_url_for_missing_method(self):
|
||||||
with self.assertRaises(exc.TaskclusterFailure):
|
with self.assertRaises(exc.TaskclusterFailure):
|
||||||
|
@ -612,17 +589,17 @@ class TestBuildUrl(ClientTest):
|
||||||
|
|
||||||
class TestBuildSignedUrl(ClientTest):
|
class TestBuildSignedUrl(ClientTest):
|
||||||
|
|
||||||
apiPath = liburls.api(ClientTest.test_root_url, 'fake', 'v1', 'two_args_no_input/arg0/arg1')
|
|
||||||
|
|
||||||
def test_builds_surl_positional(self):
|
def test_builds_surl_positional(self):
|
||||||
|
expected = 'https://fake.taskcluster.net/v1/two_args_no_input/arg0/arg1?bewit=X'
|
||||||
actual = self.client.buildSignedUrl('two_args_no_input', 'arg0', 'arg1')
|
actual = self.client.buildSignedUrl('two_args_no_input', 'arg0', 'arg1')
|
||||||
actual = re.sub('bewit=[^&]*', 'bewit=X', actual)
|
actual = re.sub('bewit=[^&]*', 'bewit=X', actual)
|
||||||
self.assertEqual(self.apiPath + '?bewit=X', actual)
|
self.assertEqual(expected, actual)
|
||||||
|
|
||||||
def test_builds_surl_keyword(self):
|
def test_builds_surl_keyword(self):
|
||||||
|
expected = 'https://fake.taskcluster.net/v1/two_args_no_input/arg0/arg1?bewit=X'
|
||||||
actual = self.client.buildSignedUrl('two_args_no_input', arg0='arg0', arg1='arg1')
|
actual = self.client.buildSignedUrl('two_args_no_input', arg0='arg0', arg1='arg1')
|
||||||
actual = re.sub('bewit=[^&]*', 'bewit=X', actual)
|
actual = re.sub('bewit=[^&]*', 'bewit=X', actual)
|
||||||
self.assertEqual(self.apiPath + '?bewit=X', actual)
|
self.assertEqual(expected, actual)
|
||||||
|
|
||||||
|
|
||||||
class TestMockHttpCalls(ClientTest):
|
class TestMockHttpCalls(ClientTest):
|
||||||
|
@ -645,30 +622,30 @@ class TestMockHttpCalls(ClientTest):
|
||||||
def test_no_args_no_input(self):
|
def test_no_args_no_input(self):
|
||||||
with httmock.HTTMock(self.fakeSite):
|
with httmock.HTTMock(self.fakeSite):
|
||||||
self.client.no_args_no_input()
|
self.client.no_args_no_input()
|
||||||
self.assertEqual(self.gotUrl, 'https://tc-tests.example.com/api/fake/v1/no_args_no_input')
|
self.assertEqual(self.gotUrl, 'https://fake.taskcluster.net/v1/no_args_no_input')
|
||||||
|
|
||||||
def test_two_args_no_input(self):
|
def test_two_args_no_input(self):
|
||||||
with httmock.HTTMock(self.fakeSite):
|
with httmock.HTTMock(self.fakeSite):
|
||||||
self.client.two_args_no_input('1', '2')
|
self.client.two_args_no_input('1', '2')
|
||||||
self.assertEqual(self.gotUrl, 'https://tc-tests.example.com/api/fake/v1/two_args_no_input/1/2')
|
self.assertEqual(self.gotUrl, 'https://fake.taskcluster.net/v1/two_args_no_input/1/2')
|
||||||
|
|
||||||
def test_no_args_with_input(self):
|
def test_no_args_with_input(self):
|
||||||
with httmock.HTTMock(self.fakeSite):
|
with httmock.HTTMock(self.fakeSite):
|
||||||
self.client.no_args_with_input({'x': 1})
|
self.client.no_args_with_input({'x': 1})
|
||||||
self.assertEqual(self.gotUrl, 'https://tc-tests.example.com/api/fake/v1/no_args_with_input')
|
self.assertEqual(self.gotUrl, 'https://fake.taskcluster.net/v1/no_args_with_input')
|
||||||
self.assertEqual(json.loads(self.gotRequest.body), {"x": 1})
|
self.assertEqual(json.loads(self.gotRequest.body), {"x": 1})
|
||||||
|
|
||||||
def test_no_args_with_empty_input(self):
|
def test_no_args_with_empty_input(self):
|
||||||
with httmock.HTTMock(self.fakeSite):
|
with httmock.HTTMock(self.fakeSite):
|
||||||
self.client.no_args_with_input({})
|
self.client.no_args_with_input({})
|
||||||
self.assertEqual(self.gotUrl, 'https://tc-tests.example.com/api/fake/v1/no_args_with_input')
|
self.assertEqual(self.gotUrl, 'https://fake.taskcluster.net/v1/no_args_with_input')
|
||||||
self.assertEqual(json.loads(self.gotRequest.body), {})
|
self.assertEqual(json.loads(self.gotRequest.body), {})
|
||||||
|
|
||||||
def test_two_args_with_input(self):
|
def test_two_args_with_input(self):
|
||||||
with httmock.HTTMock(self.fakeSite):
|
with httmock.HTTMock(self.fakeSite):
|
||||||
self.client.two_args_with_input('a', 'b', {'x': 1})
|
self.client.two_args_with_input('a', 'b', {'x': 1})
|
||||||
self.assertEqual(self.gotUrl,
|
self.assertEqual(self.gotUrl,
|
||||||
'https://tc-tests.example.com/api/fake/v1/two_args_with_input/a/b')
|
'https://fake.taskcluster.net/v1/two_args_with_input/a/b')
|
||||||
self.assertEqual(json.loads(self.gotRequest.body), {"x": 1})
|
self.assertEqual(json.loads(self.gotRequest.body), {"x": 1})
|
||||||
|
|
||||||
def test_kwargs(self):
|
def test_kwargs(self):
|
||||||
|
@ -676,7 +653,7 @@ class TestMockHttpCalls(ClientTest):
|
||||||
self.client.two_args_with_input(
|
self.client.two_args_with_input(
|
||||||
{'x': 1}, arg0='a', arg1='b')
|
{'x': 1}, arg0='a', arg1='b')
|
||||||
self.assertEqual(self.gotUrl,
|
self.assertEqual(self.gotUrl,
|
||||||
'https://tc-tests.example.com/api/fake/v1/two_args_with_input/a/b')
|
'https://fake.taskcluster.net/v1/two_args_with_input/a/b')
|
||||||
self.assertEqual(json.loads(self.gotRequest.body), {"x": 1})
|
self.assertEqual(json.loads(self.gotRequest.body), {"x": 1})
|
||||||
|
|
||||||
|
|
||||||
|
@ -690,14 +667,14 @@ class TestAuthentication(base.TCTest):
|
||||||
@httmock.all_requests
|
@httmock.all_requests
|
||||||
def auth_response(url, request):
|
def auth_response(url, request):
|
||||||
self.assertEqual(urllib.parse.urlunsplit(url),
|
self.assertEqual(urllib.parse.urlunsplit(url),
|
||||||
'https://tc-tests.example.com/api/auth/v1/clients/abc')
|
'https://auth.taskcluster.net/v1/clients/abc')
|
||||||
self.failIf('Authorization' in request.headers)
|
self.failIf('Authorization' in request.headers)
|
||||||
headers = {'content-type': 'application/json'}
|
headers = {'content-type': 'application/json'}
|
||||||
content = {"clientId": "abc"}
|
content = {"clientId": "abc"}
|
||||||
return httmock.response(200, content, headers, None, 5, request)
|
return httmock.response(200, content, headers, None, 5, request)
|
||||||
|
|
||||||
with httmock.HTTMock(auth_response):
|
with httmock.HTTMock(auth_response):
|
||||||
client = subject.Auth({"rootUrl": "https://tc-tests.example.com", "credentials": {}})
|
client = subject.Auth({"credentials": {}})
|
||||||
result = client.client('abc')
|
result = client.client('abc')
|
||||||
self.assertEqual(result, {"clientId": "abc"})
|
self.assertEqual(result, {"clientId": "abc"})
|
||||||
|
|
||||||
|
@ -705,7 +682,6 @@ class TestAuthentication(base.TCTest):
|
||||||
"""we can call methods which require authentication with valid
|
"""we can call methods which require authentication with valid
|
||||||
permacreds"""
|
permacreds"""
|
||||||
client = subject.Auth({
|
client = subject.Auth({
|
||||||
'rootUrl': self.real_root_url,
|
|
||||||
'credentials': {
|
'credentials': {
|
||||||
'clientId': 'tester',
|
'clientId': 'tester',
|
||||||
'accessToken': 'no-secret',
|
'accessToken': 'no-secret',
|
||||||
|
@ -719,7 +695,6 @@ class TestAuthentication(base.TCTest):
|
||||||
|
|
||||||
def test_permacred_simple_authorizedScopes(self):
|
def test_permacred_simple_authorizedScopes(self):
|
||||||
client = subject.Auth({
|
client = subject.Auth({
|
||||||
'rootUrl': self.real_root_url,
|
|
||||||
'credentials': {
|
'credentials': {
|
||||||
'clientId': 'tester',
|
'clientId': 'tester',
|
||||||
'accessToken': 'no-secret',
|
'accessToken': 'no-secret',
|
||||||
|
@ -736,7 +711,6 @@ class TestAuthentication(base.TCTest):
|
||||||
def test_unicode_permacred_simple(self):
|
def test_unicode_permacred_simple(self):
|
||||||
"""Unicode strings that encode to ASCII in credentials do not cause issues"""
|
"""Unicode strings that encode to ASCII in credentials do not cause issues"""
|
||||||
client = subject.Auth({
|
client = subject.Auth({
|
||||||
'rootUrl': self.real_root_url,
|
|
||||||
'credentials': {
|
'credentials': {
|
||||||
'clientId': u'tester',
|
'clientId': u'tester',
|
||||||
'accessToken': u'no-secret',
|
'accessToken': u'no-secret',
|
||||||
|
@ -752,7 +726,6 @@ class TestAuthentication(base.TCTest):
|
||||||
"""Unicode strings that do not encode to ASCII in credentials cause issues"""
|
"""Unicode strings that do not encode to ASCII in credentials cause issues"""
|
||||||
with self.assertRaises(exc.TaskclusterAuthFailure):
|
with self.assertRaises(exc.TaskclusterAuthFailure):
|
||||||
subject.Auth({
|
subject.Auth({
|
||||||
'rootUrl': self.test_root_url,
|
|
||||||
'credentials': {
|
'credentials': {
|
||||||
'clientId': u"\U0001F4A9",
|
'clientId': u"\U0001F4A9",
|
||||||
'accessToken': u"\U0001F4A9",
|
'accessToken': u"\U0001F4A9",
|
||||||
|
@ -762,7 +735,6 @@ class TestAuthentication(base.TCTest):
|
||||||
def test_permacred_insufficient_scopes(self):
|
def test_permacred_insufficient_scopes(self):
|
||||||
"""A call with insufficient scopes results in an error"""
|
"""A call with insufficient scopes results in an error"""
|
||||||
client = subject.Auth({
|
client = subject.Auth({
|
||||||
'rootUrl': self.real_root_url,
|
|
||||||
'credentials': {
|
'credentials': {
|
||||||
'clientId': 'tester',
|
'clientId': 'tester',
|
||||||
'accessToken': 'no-secret',
|
'accessToken': 'no-secret',
|
||||||
|
@ -787,7 +759,6 @@ class TestAuthentication(base.TCTest):
|
||||||
['test:xyz'],
|
['test:xyz'],
|
||||||
)
|
)
|
||||||
client = subject.Auth({
|
client = subject.Auth({
|
||||||
'rootUrl': self.real_root_url,
|
|
||||||
'credentials': tempCred,
|
'credentials': tempCred,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -807,7 +778,6 @@ class TestAuthentication(base.TCTest):
|
||||||
name='credName'
|
name='credName'
|
||||||
)
|
)
|
||||||
client = subject.Auth({
|
client = subject.Auth({
|
||||||
'rootUrl': self.real_root_url,
|
|
||||||
'credentials': tempCred,
|
'credentials': tempCred,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -826,7 +796,6 @@ class TestAuthentication(base.TCTest):
|
||||||
['test:xyz:*'],
|
['test:xyz:*'],
|
||||||
)
|
)
|
||||||
client = subject.Auth({
|
client = subject.Auth({
|
||||||
'rootUrl': self.real_root_url,
|
|
||||||
'credentials': tempCred,
|
'credentials': tempCred,
|
||||||
'authorizedScopes': ['test:xyz:abc'],
|
'authorizedScopes': ['test:xyz:abc'],
|
||||||
})
|
})
|
||||||
|
@ -848,7 +817,6 @@ class TestAuthentication(base.TCTest):
|
||||||
name='credName'
|
name='credName'
|
||||||
)
|
)
|
||||||
client = subject.Auth({
|
client = subject.Auth({
|
||||||
'rootUrl': self.real_root_url,
|
|
||||||
'credentials': tempCred,
|
'credentials': tempCred,
|
||||||
'authorizedScopes': ['test:xyz:abc'],
|
'authorizedScopes': ['test:xyz:abc'],
|
||||||
})
|
})
|
||||||
|
@ -863,7 +831,6 @@ class TestAuthentication(base.TCTest):
|
||||||
def test_signed_url(self):
|
def test_signed_url(self):
|
||||||
"""we can use a signed url built with the python client"""
|
"""we can use a signed url built with the python client"""
|
||||||
client = subject.Auth({
|
client = subject.Auth({
|
||||||
'rootUrl': self.real_root_url,
|
|
||||||
'credentials': {
|
'credentials': {
|
||||||
'clientId': 'tester',
|
'clientId': 'tester',
|
||||||
'accessToken': 'no-secret',
|
'accessToken': 'no-secret',
|
||||||
|
@ -881,7 +848,6 @@ class TestAuthentication(base.TCTest):
|
||||||
|
|
||||||
def test_signed_url_bad_credentials(self):
|
def test_signed_url_bad_credentials(self):
|
||||||
client = subject.Auth({
|
client = subject.Auth({
|
||||||
'rootUrl': self.real_root_url,
|
|
||||||
'credentials': {
|
'credentials': {
|
||||||
'clientId': 'tester',
|
'clientId': 'tester',
|
||||||
'accessToken': 'wrong-secret',
|
'accessToken': 'wrong-secret',
|
||||||
|
@ -902,7 +868,6 @@ class TestAuthentication(base.TCTest):
|
||||||
['test:*'],
|
['test:*'],
|
||||||
)
|
)
|
||||||
client = subject.Auth({
|
client = subject.Auth({
|
||||||
'rootUrl': self.real_root_url,
|
|
||||||
'credentials': tempCred,
|
'credentials': tempCred,
|
||||||
})
|
})
|
||||||
signedUrl = client.buildSignedUrl('testAuthenticateGet')
|
signedUrl = client.buildSignedUrl('testAuthenticateGet')
|
||||||
|
@ -916,7 +881,6 @@ class TestAuthentication(base.TCTest):
|
||||||
|
|
||||||
def test_signed_url_authorizedScopes(self):
|
def test_signed_url_authorizedScopes(self):
|
||||||
client = subject.Auth({
|
client = subject.Auth({
|
||||||
'rootUrl': self.real_root_url,
|
|
||||||
'credentials': {
|
'credentials': {
|
||||||
'clientId': 'tester',
|
'clientId': 'tester',
|
||||||
'accessToken': 'no-secret',
|
'accessToken': 'no-secret',
|
||||||
|
@ -941,7 +905,6 @@ class TestAuthentication(base.TCTest):
|
||||||
['test:*'],
|
['test:*'],
|
||||||
)
|
)
|
||||||
client = subject.Auth({
|
client = subject.Auth({
|
||||||
'rootUrl': self.real_root_url,
|
|
||||||
'credentials': tempCred,
|
'credentials': tempCred,
|
||||||
'authorizedScopes': ['test:authenticate-get'],
|
'authorizedScopes': ['test:authenticate-get'],
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import datetime
|
import datetime
|
||||||
import uuid
|
import uuid
|
||||||
import os
|
|
||||||
|
|
||||||
import taskcluster.utils as subject
|
import taskcluster.utils as subject
|
||||||
import dateutil.parser
|
import dateutil.parser
|
||||||
|
@ -339,101 +338,3 @@ class TestIsExpired(TestCase):
|
||||||
}
|
}
|
||||||
""")
|
""")
|
||||||
self.assertEqual(isExpired, True)
|
self.assertEqual(isExpired, True)
|
||||||
|
|
||||||
|
|
||||||
class TestFromEnv(TestCase):
|
|
||||||
|
|
||||||
def clear_env(self):
|
|
||||||
for v in 'ROOT_URL', 'CLIENT_ID', 'ACCESS_TOKEN', 'CERTIFICATE':
|
|
||||||
v = 'TASKCLUSTER_' + v
|
|
||||||
if v in os.environ:
|
|
||||||
del os.environ[v]
|
|
||||||
|
|
||||||
@mock.patch.dict(os.environ)
|
|
||||||
def test_empty(self):
|
|
||||||
self.clear_env()
|
|
||||||
self.assertEqual(subject.optionsFromEnvironment(), {})
|
|
||||||
|
|
||||||
@mock.patch.dict(os.environ)
|
|
||||||
def test_all(self):
|
|
||||||
os.environ['TASKCLUSTER_ROOT_URL'] = 'https://tc.example.com'
|
|
||||||
os.environ['TASKCLUSTER_CLIENT_ID'] = 'me'
|
|
||||||
os.environ['TASKCLUSTER_ACCESS_TOKEN'] = 'shave-and-a-haircut'
|
|
||||||
os.environ['TASKCLUSTER_CERTIFICATE'] = '{"bits":2}'
|
|
||||||
self.assertEqual(subject.optionsFromEnvironment(), {
|
|
||||||
'rootUrl': 'https://tc.example.com',
|
|
||||||
'credentials': {
|
|
||||||
'clientId': 'me',
|
|
||||||
'accessToken': 'shave-and-a-haircut',
|
|
||||||
'certificate': '{"bits":2}',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
@mock.patch.dict(os.environ)
|
|
||||||
def test_cred_only(self):
|
|
||||||
os.environ['TASKCLUSTER_ACCESS_TOKEN'] = 'shave-and-a-haircut'
|
|
||||||
self.assertEqual(subject.optionsFromEnvironment(), {
|
|
||||||
'credentials': {
|
|
||||||
'accessToken': 'shave-and-a-haircut',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
@mock.patch.dict(os.environ)
|
|
||||||
def test_rooturl_only(self):
|
|
||||||
os.environ['TASKCLUSTER_ROOT_URL'] = 'https://tc.example.com'
|
|
||||||
self.assertEqual(subject.optionsFromEnvironment(), {
|
|
||||||
'rootUrl': 'https://tc.example.com',
|
|
||||||
})
|
|
||||||
|
|
||||||
@mock.patch.dict(os.environ)
|
|
||||||
def test_default_rooturl(self):
|
|
||||||
os.environ['TASKCLUSTER_CLIENT_ID'] = 'me'
|
|
||||||
os.environ['TASKCLUSTER_ACCESS_TOKEN'] = 'shave-and-a-haircut'
|
|
||||||
os.environ['TASKCLUSTER_CERTIFICATE'] = '{"bits":2}'
|
|
||||||
self.assertEqual(
|
|
||||||
subject.optionsFromEnvironment({'rootUrl': 'https://other.example.com'}), {
|
|
||||||
'rootUrl': 'https://other.example.com',
|
|
||||||
'credentials': {
|
|
||||||
'clientId': 'me',
|
|
||||||
'accessToken': 'shave-and-a-haircut',
|
|
||||||
'certificate': '{"bits":2}',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
@mock.patch.dict(os.environ)
|
|
||||||
def test_default_rooturl_overridden(self):
|
|
||||||
os.environ['TASKCLUSTER_ROOT_URL'] = 'https://tc.example.com'
|
|
||||||
self.assertEqual(
|
|
||||||
subject.optionsFromEnvironment({'rootUrl': 'https://other.example.com'}),
|
|
||||||
{'rootUrl': 'https://tc.example.com'})
|
|
||||||
|
|
||||||
@mock.patch.dict(os.environ)
|
|
||||||
def test_default_creds(self):
|
|
||||||
os.environ['TASKCLUSTER_ROOT_URL'] = 'https://tc.example.com'
|
|
||||||
os.environ['TASKCLUSTER_ACCESS_TOKEN'] = 'shave-and-a-haircut'
|
|
||||||
os.environ['TASKCLUSTER_CERTIFICATE'] = '{"bits":2}'
|
|
||||||
self.assertEqual(
|
|
||||||
subject.optionsFromEnvironment({'credentials': {'clientId': 'them'}}), {
|
|
||||||
'rootUrl': 'https://tc.example.com',
|
|
||||||
'credentials': {
|
|
||||||
'clientId': 'them',
|
|
||||||
'accessToken': 'shave-and-a-haircut',
|
|
||||||
'certificate': '{"bits":2}',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
@mock.patch.dict(os.environ)
|
|
||||||
def test_default_creds_overridden(self):
|
|
||||||
os.environ['TASKCLUSTER_ROOT_URL'] = 'https://tc.example.com'
|
|
||||||
os.environ['TASKCLUSTER_CLIENT_ID'] = 'me'
|
|
||||||
os.environ['TASKCLUSTER_ACCESS_TOKEN'] = 'shave-and-a-haircut'
|
|
||||||
os.environ['TASKCLUSTER_CERTIFICATE'] = '{"bits":2}'
|
|
||||||
self.assertEqual(
|
|
||||||
subject.optionsFromEnvironment({'credentials': {'clientId': 'them'}}), {
|
|
||||||
'rootUrl': 'https://tc.example.com',
|
|
||||||
'credentials': {
|
|
||||||
'clientId': 'me',
|
|
||||||
'accessToken': 'shave-and-a-haircut',
|
|
||||||
'certificate': '{"bits":2}',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче