Merge remote-tracking branch 'origin/master' into rustedbuilds

This commit is contained in:
jakedt 2014-02-17 16:40:28 -05:00
commit fc4983ed8b
75 changed files with 4280 additions and 700 deletions

View file

@ -262,7 +262,6 @@ def convert_user_to_organization():
@internal_api_call
def change_user_details():
user = current_user.db_user()
user_data = request.get_json()
try:
@ -315,6 +314,8 @@ def create_new_user():
@internal_api_call
def signin_user():
signin_data = request.get_json()
if not signin_data:
abort(404)
username = signin_data['username']
password = signin_data['password']
@ -421,6 +422,7 @@ def get_matching_entities(prefix):
team_data = [entity_team_view(team) for team in teams]
user_data = [user_view(user) for user in users]
return jsonify({
'results': team_data + user_data
})
@ -446,11 +448,16 @@ def create_organization():
existing = None
try:
existing = (model.get_organization(org_data['name']) or
model.get_user(org_data['name']))
existing = model.get_organization(org_data['name'])
except model.InvalidOrganizationException:
pass
if not existing:
try:
existing = model.get_user(org_data['name'])
except model.InvalidUserException:
pass
if existing:
msg = 'A user or organization with this name already exists'
return request_error(message=msg)
@ -604,9 +611,9 @@ def create_organization_prototype_permission(orgname):
'name' in details['activating_user']):
activating_username = details['activating_user']['name']
delegate = details['delegate']
delegate_kind = delegate['kind']
delegate_name = delegate['name']
delegate = details['delegate'] if 'delegate' in details else {}
delegate_kind = delegate.get('kind', None)
delegate_name = delegate.get('name', None)
delegate_username = delegate_name if delegate_kind == 'user' else None
delegate_teamname = delegate_name if delegate_kind == 'team' else None
@ -622,7 +629,7 @@ def create_organization_prototype_permission(orgname):
return request_error(message='Unknown activating user')
if not delegate_user and not delegate_team:
return request_error(message='Missing delagate user or team')
return request_error(message='Missing delegate user or team')
role_name = details['role']
@ -1148,7 +1155,9 @@ def build_status_view(build_obj):
'id': build_obj.uuid,
'phase': build_obj.phase,
'started': build_obj.started,
'display_name': build_obj.display_name,
'status': status,
'resource_key': build_obj.resource_key
}
@ -1189,38 +1198,12 @@ def get_repo_build_logs(namespace, repository, build_uuid):
build = model.get_repository_build(namespace, repository, build_uuid)
start_param = request.args.get('start', None)
end = int(request.args.get('end', -1))
start = int(request.args.get('start', 0))
last_command = None
include_commands = request.args.get('commands', 'false')
if include_commands.lower() not in {'0', 'false'}:
commands = [cmd for cmd in build_logs.get_commands(build.uuid)]
response_obj['commands'] = commands
if commands:
last_command = commands[-1]
elif start_param is None:
last_command = build_logs.get_last_command(build.uuid)
if start_param is None:
if last_command:
start = last_command['index']
else:
start = 0
else:
start = int(start_param)
count, logs = build_logs.get_log_entries(build.uuid, start, end)
if start < 0:
start = max(0, count + start)
if end < 0:
end = count + end
count, logs = build_logs.get_log_entries(build.uuid, start)
response_obj.update({
'start': start,
'end': end,
'total': count,
'logs': [log for log in logs],
})
@ -1239,18 +1222,28 @@ def request_repo_build(namespace, repository):
logger.debug('User requested repository initialization.')
dockerfile_id = request.get_json()['file_id']
# Check if the dockerfile resource has already been used. If so, then it can only be reused if the
# user has access to the repository for which it was used.
associated_repository = model.get_repository_for_resource(dockerfile_id)
if associated_repository:
if not ModifyRepositoryPermission(associated_repository.namespace, associated_repository.name):
abort(403)
# Start the build.
repo = model.get_repository(namespace, repository)
token = model.create_access_token(repo, 'write')
display_name = user_files.get_file_checksum(dockerfile_id)
logger.debug('**********Md5: %s' % display_name)
host = urlparse.urlparse(request.url).netloc
tag = '%s/%s/%s' % (host, repo.namespace, repo.name)
build_request = model.create_repository_build(repo, token, dockerfile_id,
tag)
tag, display_name)
dockerfile_build_queue.put(json.dumps({
'build_uuid': build_request.uuid,
'namespace': namespace,
'repository': repository,
}))
}), retries_remaining=1)
log_action('build_dockerfile', namespace,
{'repo': repository, 'namespace': namespace,
@ -1302,7 +1295,11 @@ def create_webhook(namespace, repository):
def get_webhook(namespace, repository, public_id):
permission = AdministerRepositoryPermission(namespace, repository)
if permission.can():
webhook = model.get_webhook(namespace, repository, public_id)
try:
webhook = model.get_webhook(namespace, repository, public_id)
except model.InvalidWebhookException:
abort(404)
return jsonify(webhook_view(webhook))
abort(403) # Permission denied
@ -1697,7 +1694,11 @@ def list_repo_tokens(namespace, repository):
def get_tokens(namespace, repository, code):
permission = AdministerRepositoryPermission(namespace, repository)
if permission.can():
perm = model.get_repo_delegate_token(namespace, repository, code)
try:
perm = model.get_repo_delegate_token(namespace, repository, code)
except model.InvalidTokenException:
abort(404)
return jsonify(token_view(perm))
abort(403) # Permission denied
@ -1834,6 +1835,8 @@ def set_card(user, token):
cus.save()
except stripe.CardError as e:
return carderror_response(e)
except stripe.InvalidRequestError as e:
return carderror_response(e)
return get_card(user)

View file

@ -5,9 +5,9 @@ import urlparse
from flask import request, make_response, jsonify, session, Blueprint
from functools import wraps
from data import model
from data import model, userevent
from data.queue import webhook_queue
from app import mixpanel
from app import mixpanel, app
from auth.auth import (process_auth, get_authenticated_user,
get_validated_token)
from util.names import parse_repository_name
@ -80,8 +80,16 @@ def create_user():
if existing_user:
verified = model.verify_user(username, password)
if verified:
# Mark that the user was logged in.
event = app.config['USER_EVENTS'].get_event(username)
event.publish_event_data('docker-cli', {'action': 'login'})
return make_response('Verified', 201)
else:
# Mark that the login failed.
event = app.config['USER_EVENTS'].get_event(username)
event.publish_event_data('docker-cli', {'action': 'loginfailure'})
abort(400, 'Invalid password.', issue='login-failure')
else:
@ -186,9 +194,21 @@ def create_repository(namespace, repository):
}
if get_authenticated_user():
mixpanel.track(get_authenticated_user().username, 'push_repo',
extra_params)
metadata['username'] = get_authenticated_user().username
username = get_authenticated_user().username
mixpanel.track(username, 'push_repo', extra_params)
metadata['username'] = username
# Mark that the user has started pushing the repo.
user_data = {
'action': 'push_repo',
'repository': repository,
'namespace': namespace
}
event = app.config['USER_EVENTS'].get_event(username)
event.publish_event_data('docker-cli', user_data)
else:
mixpanel.track(get_validated_token().code, 'push_repo', extra_params)
metadata['token'] = get_validated_token().friendly_name
@ -222,6 +242,21 @@ def update_images(namespace, repository):
updated_tags[image['Tag']] = image['id']
model.set_image_checksum(image['id'], repo, image['checksum'])
if get_authenticated_user():
username = get_authenticated_user().username
# Mark that the user has pushed the repo.
user_data = {
'action': 'pushed_repo',
'repository': repository,
'namespace': namespace
}
event = app.config['USER_EVENTS'].get_event(username)
event.publish_event_data('docker-cli', user_data)
num_removed = model.garbage_collect_repository(namespace, repository)
# Generate a job for each webhook that has been added to this repo
webhooks = model.list_webhooks(namespace, repository)
for webhook in webhooks:
@ -237,7 +272,8 @@ def update_images(namespace, repository):
'homepage': 'https://quay.io/repository/%s' % repo_string,
'visibility': repo.visibility.name,
'updated_tags': updated_tags,
'pushed_image_count': len(image_with_checksums),
'pushed_image_count': len(image_with_checksums),
'pruned_image_count': num_removed,
}
webhook_queue.put(json.dumps(webhook_data))

81
endpoints/realtime.py Normal file
View file

@ -0,0 +1,81 @@
import logging
import redis
import json
from functools import wraps
from flask import request, make_response, Blueprint, abort, Response
from flask.ext.login import current_user, logout_user
from data import model, userevent
from app import app
logger = logging.getLogger(__name__)
realtime = Blueprint('realtime', __name__)
def api_login_required(f):
@wraps(f)
def decorated_view(*args, **kwargs):
if not current_user.is_authenticated():
abort(401)
if (current_user and current_user.db_user() and
current_user.db_user().organization):
abort(401)
if (current_user and current_user.db_user() and
current_user.db_user().robot):
abort(401)
return f(*args, **kwargs)
return decorated_view
@realtime.route("/user/")
@api_login_required
def index():
debug_template = """
<html>
<head>
</head>
<body>
<h1>Server sent events</h1>
<div id="event"></div>
<script type="text/javascript">
var eventOutputContainer = document.getElementById("event");
var evtSrc = new EventSource("/realtime/user/subscribe?events=docker-cli");
evtSrc.onmessage = function(e) {
console.log(e.data);
eventOutputContainer.innerHTML = e.data;
};
</script>
</body>
</html>
"""
return(debug_template)
@realtime.route("/user/test")
@api_login_required
def user_test():
evt = userevent.UserEvent('logs.quay.io', current_user.db_user().username)
evt.publish_event_data('test', {'foo': 2})
return 'OK'
@realtime.route("/user/subscribe")
@api_login_required
def user_subscribe():
def wrapper(listener):
for event_id, data in listener.event_stream():
message = {'event': event_id, 'data': data}
json_string = json.dumps(message)
yield 'data: %s\n\n' % json_string
events = request.args.get('events', '').split(',')
if not events:
abort(404)
listener = userevent.UserEventListener('logs.quay.io', current_user.db_user().username, events)
return Response(wrapper(listener), mimetype="text/event-stream")

View file

@ -73,22 +73,8 @@ def delete_tag(namespace, repository, tag):
if permission.can():
model.delete_tag(namespace, repository, tag)
model.garbage_collect_repository(namespace, repository)
return make_response('Deleted', 204)
abort(403)
@tags.route('/repositories/<path:repository>/tags',
methods=['DELETE'])
@process_auth
@parse_repository_name
def delete_repository_tags(namespace, repository):
permission = ModifyRepositoryPermission(namespace, repository)
if permission.can():
model.delete_all_repository_tags(namespace, repository)
return make_response('Deleted', 204)
return make_response('Deleted', 200)
abort(403)

View file

@ -63,6 +63,12 @@ def guide():
return index('')
@web.route('/tutorial/')
@no_cache
def tutorial():
return index('')
@web.route('/organizations/')
@web.route('/organizations/new/')
@no_cache
@ -88,6 +94,12 @@ def contact():
return index('')
@web.route('/about/')
@no_cache
def about():
return index('')
@web.route('/new/')
@no_cache
def new():