Merge remote-tracking branch 'origin/master' into ephemeral
This commit is contained in:
commit
e53b6b0e21
18 changed files with 163 additions and 60 deletions
|
@ -1,23 +1,30 @@
|
||||||
# vim:ft=dockerfile
|
# vim:ft=dockerfile
|
||||||
|
|
||||||
|
###############################
|
||||||
|
# BEGIN COMMON SECION
|
||||||
|
###############################
|
||||||
|
|
||||||
FROM phusion/baseimage:0.9.15
|
FROM phusion/baseimage:0.9.15
|
||||||
|
|
||||||
ENV DEBIAN_FRONTEND noninteractive
|
ENV DEBIAN_FRONTEND noninteractive
|
||||||
ENV HOME /root
|
ENV HOME /root
|
||||||
|
|
||||||
# Install the dependencies.
|
# Install the dependencies.
|
||||||
RUN apt-get update # 20NOV2014
|
RUN apt-get update # 11DEC2014
|
||||||
|
|
||||||
# New ubuntu packages should be added as their own apt-get install lines below the existing install commands
|
# New ubuntu packages should be added as their own apt-get install lines below the existing install commands
|
||||||
RUN apt-get install -y git python-virtualenv python-dev libjpeg8 libjpeg62 libjpeg62-dev libevent-2.0.5 libevent-dev gdebi-core g++ libmagic1 phantomjs nodejs npm libldap-2.4-2 libldap2-dev libsasl2-modules libsasl2-dev libpq5 libpq-dev
|
RUN apt-get install -y git python-virtualenv python-dev libjpeg8 libjpeg62 libjpeg62-dev libevent-2.0.5 libevent-dev gdebi-core g++ libmagic1 phantomjs nodejs npm libldap-2.4-2 libldap2-dev libsasl2-modules libsasl2-dev libpq5 libpq-dev libfreetype6-dev libffi-dev
|
||||||
|
|
||||||
# Build the python dependencies
|
# Build the python dependencies
|
||||||
ADD requirements.txt requirements.txt
|
ADD requirements.txt requirements.txt
|
||||||
RUN virtualenv --distribute venv
|
RUN virtualenv --distribute venv
|
||||||
RUN venv/bin/pip install -r requirements.txt
|
RUN venv/bin/pip install -r requirements.txt
|
||||||
|
|
||||||
RUN apt-get remove -y --auto-remove python-dev g++ libjpeg62-dev libevent-dev libldap2-dev libsasl2-dev libpq-dev
|
RUN apt-get remove -y --auto-remove python-dev g++ libjpeg62-dev libevent-dev libldap2-dev libsasl2-dev libpq-dev libffi-dev
|
||||||
|
|
||||||
### End common section ###
|
###############################
|
||||||
|
# END COMMON SECION
|
||||||
|
###############################
|
||||||
|
|
||||||
RUN apt-get install -y lxc aufs-tools
|
RUN apt-get install -y lxc aufs-tools
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
# vim:ft=dockerfile
|
# vim:ft=dockerfile
|
||||||
|
|
||||||
|
###############################
|
||||||
|
# BEGIN COMMON SECION
|
||||||
|
###############################
|
||||||
|
|
||||||
FROM phusion/baseimage:0.9.15
|
FROM phusion/baseimage:0.9.15
|
||||||
|
|
||||||
ENV DEBIAN_FRONTEND noninteractive
|
ENV DEBIAN_FRONTEND noninteractive
|
||||||
|
@ -8,16 +13,18 @@ ENV HOME /root
|
||||||
RUN apt-get update # 11DEC2014
|
RUN apt-get update # 11DEC2014
|
||||||
|
|
||||||
# New ubuntu packages should be added as their own apt-get install lines below the existing install commands
|
# New ubuntu packages should be added as their own apt-get install lines below the existing install commands
|
||||||
RUN apt-get install -y git python-virtualenv python-dev libjpeg8 libjpeg62 libjpeg62-dev libevent-2.0.5 libevent-dev gdebi-core g++ libmagic1 phantomjs nodejs npm libldap-2.4-2 libldap2-dev libsasl2-modules libsasl2-dev libpq5 libpq-dev libfreetype6-dev
|
RUN apt-get install -y git python-virtualenv python-dev libjpeg8 libjpeg62 libjpeg62-dev libevent-2.0.5 libevent-dev gdebi-core g++ libmagic1 phantomjs nodejs npm libldap-2.4-2 libldap2-dev libsasl2-modules libsasl2-dev libpq5 libpq-dev libfreetype6-dev libffi-dev
|
||||||
|
|
||||||
# Build the python dependencies
|
# Build the python dependencies
|
||||||
ADD requirements.txt requirements.txt
|
ADD requirements.txt requirements.txt
|
||||||
RUN virtualenv --distribute venv
|
RUN virtualenv --distribute venv
|
||||||
RUN venv/bin/pip install -r requirements.txt
|
RUN venv/bin/pip install -r requirements.txt
|
||||||
|
|
||||||
RUN apt-get remove -y --auto-remove python-dev g++ libjpeg62-dev libevent-dev libldap2-dev libsasl2-dev libpq-dev
|
RUN apt-get remove -y --auto-remove python-dev g++ libjpeg62-dev libevent-dev libldap2-dev libsasl2-dev libpq-dev libffi-dev
|
||||||
|
|
||||||
### End common section ###
|
###############################
|
||||||
|
# END COMMON SECION
|
||||||
|
###############################
|
||||||
|
|
||||||
# Remove SSH.
|
# Remove SSH.
|
||||||
RUN rm -rf /etc/service/sshd /etc/my_init.d/00_regen_ssh_host_keys.sh
|
RUN rm -rf /etc/service/sshd /etc/my_init.d/00_regen_ssh_host_keys.sh
|
||||||
|
|
|
@ -49,8 +49,8 @@ def run_build_manager():
|
||||||
if os.environ.get('SSL_CONFIG'):
|
if os.environ.get('SSL_CONFIG'):
|
||||||
logger.debug('Loading SSL cert and key')
|
logger.debug('Loading SSL cert and key')
|
||||||
ssl_context = SSLContext()
|
ssl_context = SSLContext()
|
||||||
ssl_context.load_cert_chain(os.environ.get('SSL_CONFIG') + '/ssl.cert',
|
ssl_context.load_cert_chain(os.path.join(os.environ.get('SSL_CONFIG'), 'ssl.cert'),
|
||||||
os.environ.get('SSL_CONFIG') + '/ssl.key')
|
os.path.join(os.environ.get('SSL_CONFIG'), 'ssl.key'))
|
||||||
|
|
||||||
server = BuilderServer(app.config['SERVER_HOSTNAME'], dockerfile_build_queue, build_logs,
|
server = BuilderServer(app.config['SERVER_HOSTNAME'], dockerfile_build_queue, build_logs,
|
||||||
user_files, manager_klass, build_manager_config[1], public_ip)
|
user_files, manager_klass, build_manager_config[1], public_ip)
|
||||||
|
|
|
@ -59,3 +59,9 @@ class BaseManager(object):
|
||||||
automatically requeued.
|
automatically requeued.
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def num_workers(self):
|
||||||
|
""" Returns the number of active build workers currently registered. This includes those
|
||||||
|
that are currently busy and awaiting more work.
|
||||||
|
"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
|
@ -74,3 +74,5 @@ class EnterpriseManager(BaseManager):
|
||||||
if build_component in self.ready_components:
|
if build_component in self.ready_components:
|
||||||
self.ready_components.remove(build_component)
|
self.ready_components.remove(build_component)
|
||||||
|
|
||||||
|
def num_workers(self):
|
||||||
|
return len(self.build_components)
|
||||||
|
|
|
@ -9,7 +9,7 @@ from aiowsgi import create_server as create_wsgi_server
|
||||||
from flask import Flask
|
from flask import Flask
|
||||||
from threading import Event
|
from threading import Event
|
||||||
from trollius.coroutines import From
|
from trollius.coroutines import From
|
||||||
from datetime import datetime, timedelta
|
from datetime import timedelta
|
||||||
|
|
||||||
from buildman.jobutil.buildjob import BuildJob, BuildJobLoadException
|
from buildman.jobutil.buildjob import BuildJob, BuildJobLoadException
|
||||||
from data.queue import WorkQueue
|
from data.queue import WorkQueue
|
||||||
|
@ -140,7 +140,7 @@ class BuilderServer(object):
|
||||||
@trollius.coroutine
|
@trollius.coroutine
|
||||||
def _work_checker(self):
|
def _work_checker(self):
|
||||||
while self._current_status == 'running':
|
while self._current_status == 'running':
|
||||||
logger.debug('Checking for more work')
|
logger.debug('Checking for more work for %d active workers', self._lifecycle_manager.num_workers())
|
||||||
job_item = self._queue.get(processing_time=self._lifecycle_manager.setup_time())
|
job_item = self._queue.get(processing_time=self._lifecycle_manager.setup_time())
|
||||||
if job_item is None:
|
if job_item is None:
|
||||||
logger.debug('No additional work found. Going to sleep for %s seconds', WORK_CHECK_TIMEOUT)
|
logger.debug('No additional work found. Going to sleep for %s seconds', WORK_CHECK_TIMEOUT)
|
||||||
|
|
|
@ -3,6 +3,6 @@
|
||||||
echo 'Starting gunicon'
|
echo 'Starting gunicon'
|
||||||
|
|
||||||
cd /
|
cd /
|
||||||
venv/bin/gunicorn -c conf/gunicorn_registry.py registry:application
|
nice -n 10 venv/bin/gunicorn -c conf/gunicorn_registry.py registry:application
|
||||||
|
|
||||||
echo 'Gunicorn exited'
|
echo 'Gunicorn exited'
|
|
@ -3,6 +3,6 @@
|
||||||
echo 'Starting gunicon'
|
echo 'Starting gunicon'
|
||||||
|
|
||||||
cd /
|
cd /
|
||||||
nice -10 venv/bin/gunicorn -c conf/gunicorn_verbs.py verbs:application
|
nice -n 10 venv/bin/gunicorn -c conf/gunicorn_verbs.py verbs:application
|
||||||
|
|
||||||
echo 'Gunicorn exited'
|
echo 'Gunicorn exited'
|
|
@ -1,11 +1,5 @@
|
||||||
include root-base.conf;
|
include root-base.conf;
|
||||||
|
|
||||||
worker_processes 2;
|
|
||||||
|
|
||||||
user root nogroup;
|
|
||||||
|
|
||||||
daemon off;
|
|
||||||
|
|
||||||
http {
|
http {
|
||||||
include http-base.conf;
|
include http-base.conf;
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,5 @@
|
||||||
include root-base.conf;
|
include root-base.conf;
|
||||||
|
|
||||||
worker_processes 2;
|
|
||||||
|
|
||||||
user root nogroup;
|
|
||||||
|
|
||||||
daemon off;
|
|
||||||
|
|
||||||
http {
|
http {
|
||||||
include http-base.conf;
|
include http-base.conf;
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,15 @@
|
||||||
pid /tmp/nginx.pid;
|
pid /tmp/nginx.pid;
|
||||||
error_log /var/log/nginx/nginx.error.log;
|
error_log /var/log/nginx/nginx.error.log;
|
||||||
|
|
||||||
|
worker_processes 2;
|
||||||
|
worker_priority -10;
|
||||||
|
worker_rlimit_nofile 10240;
|
||||||
|
|
||||||
|
user root nogroup;
|
||||||
|
|
||||||
|
daemon off;
|
||||||
|
|
||||||
events {
|
events {
|
||||||
worker_connections 1024;
|
worker_connections 10240;
|
||||||
accept_mutex off;
|
accept_mutex off;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
"""Convert slack webhook data
|
||||||
|
|
||||||
|
Revision ID: 5b84373e5db
|
||||||
|
Revises: 1c5b738283a5
|
||||||
|
Create Date: 2014-12-16 12:02:55.167744
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '5b84373e5db'
|
||||||
|
down_revision = '1c5b738283a5'
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from util.migrateslackwebhook import run_slackwebhook_migration
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade(tables):
|
||||||
|
run_slackwebhook_migration()
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade(tables):
|
||||||
|
pass
|
|
@ -1,14 +1,10 @@
|
||||||
import logging
|
import logging
|
||||||
import io
|
|
||||||
import os.path
|
|
||||||
import tarfile
|
|
||||||
import base64
|
|
||||||
import json
|
import json
|
||||||
import requests
|
import requests
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from flask.ext.mail import Message
|
from flask.ext.mail import Message
|
||||||
from app import mail, app, get_app_url
|
from app import mail, app
|
||||||
from data import model
|
from data import model
|
||||||
from workers.worker import JobException
|
from workers.worker import JobException
|
||||||
|
|
||||||
|
@ -363,11 +359,8 @@ class SlackMethod(NotificationMethod):
|
||||||
return 'slack'
|
return 'slack'
|
||||||
|
|
||||||
def validate(self, repository, config_data):
|
def validate(self, repository, config_data):
|
||||||
if not config_data.get('token', ''):
|
if not config_data.get('url', ''):
|
||||||
raise CannotValidateNotificationMethodException('Missing Slack Token')
|
raise CannotValidateNotificationMethodException('Missing Slack Callback URL')
|
||||||
|
|
||||||
if not config_data.get('subdomain', '').isalnum():
|
|
||||||
raise CannotValidateNotificationMethodException('Missing Slack Subdomain Name')
|
|
||||||
|
|
||||||
def format_for_slack(self, message):
|
def format_for_slack(self, message):
|
||||||
message = message.replace('\n', '')
|
message = message.replace('\n', '')
|
||||||
|
@ -378,10 +371,8 @@ class SlackMethod(NotificationMethod):
|
||||||
def perform(self, notification, event_handler, notification_data):
|
def perform(self, notification, event_handler, notification_data):
|
||||||
config_data = json.loads(notification.config_json)
|
config_data = json.loads(notification.config_json)
|
||||||
|
|
||||||
token = config_data.get('token', '')
|
url = config_data.get('url', '')
|
||||||
subdomain = config_data.get('subdomain', '')
|
if not url:
|
||||||
|
|
||||||
if not token or not subdomain:
|
|
||||||
return
|
return
|
||||||
|
|
||||||
owner = model.get_user_or_org(notification.repository.namespace_user.username)
|
owner = model.get_user_or_org(notification.repository.namespace_user.username)
|
||||||
|
@ -389,8 +380,6 @@ class SlackMethod(NotificationMethod):
|
||||||
# Something went wrong.
|
# Something went wrong.
|
||||||
return
|
return
|
||||||
|
|
||||||
url = 'https://%s.slack.com/services/hooks/incoming-webhook?token=%s' % (subdomain, token)
|
|
||||||
|
|
||||||
level = event_handler.get_level(notification_data['event_data'], notification_data)
|
level = event_handler.get_level(notification_data['event_data'], notification_data)
|
||||||
color = {
|
color = {
|
||||||
'info': '#ffffff',
|
'info': '#ffffff',
|
||||||
|
@ -426,5 +415,5 @@ class SlackMethod(NotificationMethod):
|
||||||
raise NotificationMethodPerformException(error_message)
|
raise NotificationMethodPerformException(error_message)
|
||||||
|
|
||||||
except requests.exceptions.RequestException as ex:
|
except requests.exceptions.RequestException as ex:
|
||||||
logger.exception('Slack method was unable to be sent: %s' % ex.message)
|
logger.exception('Slack method was unable to be sent: %s', ex.message)
|
||||||
raise NotificationMethodPerformException(ex.message)
|
raise NotificationMethodPerformException(ex.message)
|
||||||
|
|
|
@ -19,7 +19,7 @@ from util.cache import no_cache
|
||||||
from endpoints.common import common_login, render_page_template, route_show_if, param_required
|
from endpoints.common import common_login, render_page_template, route_show_if, param_required
|
||||||
from endpoints.csrf import csrf_protect, generate_csrf_token
|
from endpoints.csrf import csrf_protect, generate_csrf_token
|
||||||
from endpoints.registry import set_cache_headers
|
from endpoints.registry import set_cache_headers
|
||||||
from util.names import parse_repository_name
|
from util.names import parse_repository_name, parse_repository_name_and_tag
|
||||||
from util.useremails import send_email_changed
|
from util.useremails import send_email_changed
|
||||||
from auth import scopes
|
from auth import scopes
|
||||||
|
|
||||||
|
@ -224,14 +224,14 @@ def robots():
|
||||||
@web.route('/<path:repository>')
|
@web.route('/<path:repository>')
|
||||||
@no_cache
|
@no_cache
|
||||||
@process_oauth
|
@process_oauth
|
||||||
@parse_repository_name
|
@parse_repository_name_and_tag
|
||||||
def redirect_to_repository(namespace, reponame):
|
def redirect_to_repository(namespace, reponame, tag):
|
||||||
permission = ReadRepositoryPermission(namespace, reponame)
|
permission = ReadRepositoryPermission(namespace, reponame)
|
||||||
is_public = model.repository_is_public(namespace, reponame)
|
is_public = model.repository_is_public(namespace, reponame)
|
||||||
|
|
||||||
if permission.can() or is_public:
|
if permission.can() or is_public:
|
||||||
repository_name = '/'.join([namespace, reponame])
|
repository_name = '/'.join([namespace, reponame])
|
||||||
return redirect(url_for('web.repository', path=repository_name))
|
return redirect(url_for('web.repository', path=repository_name, tag=tag))
|
||||||
|
|
||||||
abort(404)
|
abort(404)
|
||||||
|
|
||||||
|
|
|
@ -73,7 +73,7 @@
|
||||||
<tr ng-if="currentMethod.fields.length"><td colspan="2"><hr></td></tr>
|
<tr ng-if="currentMethod.fields.length"><td colspan="2"><hr></td></tr>
|
||||||
|
|
||||||
<tr ng-repeat="field in currentMethod.fields">
|
<tr ng-repeat="field in currentMethod.fields">
|
||||||
<td valign="top">{{ field.title }}:</td>
|
<td valign="top" style="padding-top: 10px">{{ field.title }}:</td>
|
||||||
<td>
|
<td>
|
||||||
<div ng-switch on="field.type">
|
<div ng-switch on="field.type">
|
||||||
<span ng-switch-when="email">
|
<span ng-switch-when="email">
|
||||||
|
@ -81,6 +81,9 @@
|
||||||
</span>
|
</span>
|
||||||
<input type="url" class="form-control" ng-model="currentConfig[field.name]" ng-switch-when="url" required>
|
<input type="url" class="form-control" ng-model="currentConfig[field.name]" ng-switch-when="url" required>
|
||||||
<input type="text" class="form-control" ng-model="currentConfig[field.name]" ng-switch-when="string" required>
|
<input type="text" class="form-control" ng-model="currentConfig[field.name]" ng-switch-when="string" required>
|
||||||
|
<input type="text" class="form-control" ng-model="currentConfig[field.name]" ng-switch-when="regex" required
|
||||||
|
ng-pattern="getPattern(field)"
|
||||||
|
placeholder="{{ field.placeholder }}">
|
||||||
<div class="entity-search" namespace="repository.namespace"
|
<div class="entity-search" namespace="repository.namespace"
|
||||||
placeholder="''"
|
placeholder="''"
|
||||||
current-entity="currentConfig[field.name]"
|
current-entity="currentConfig[field.name]"
|
||||||
|
|
|
@ -1484,15 +1484,12 @@ quayApp = angular.module('quay', quayDependencies, function($provide, cfpLoading
|
||||||
'icon': 'slack-icon',
|
'icon': 'slack-icon',
|
||||||
'fields': [
|
'fields': [
|
||||||
{
|
{
|
||||||
'name': 'subdomain',
|
'name': 'url',
|
||||||
'type': 'string',
|
'type': 'regex',
|
||||||
'title': 'Slack Subdomain'
|
'title': 'Webhook URL',
|
||||||
},
|
'regex': '^https://hooks\\.slack\\.com/services/[A-Z0-9]+/[A-Z0-9]+/[a-zA-Z0-9]+$',
|
||||||
{
|
'help_url': 'https://slack.com/services/new/incoming-webhook',
|
||||||
'name': 'token',
|
'placeholder': 'https://hooks.slack.com/service/{some}/{token}/{here}'
|
||||||
'type': 'string',
|
|
||||||
'title': 'Token',
|
|
||||||
'help_url': 'https://{subdomain}.slack.com/services/new/incoming-webhook'
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -5905,6 +5902,10 @@ quayApp.directive('createExternalNotificationDialog', function () {
|
||||||
$scope.events = ExternalNotificationData.getSupportedEvents();
|
$scope.events = ExternalNotificationData.getSupportedEvents();
|
||||||
$scope.methods = ExternalNotificationData.getSupportedMethods();
|
$scope.methods = ExternalNotificationData.getSupportedMethods();
|
||||||
|
|
||||||
|
$scope.getPattern = function(field) {
|
||||||
|
return new RegExp(field.regex);
|
||||||
|
};
|
||||||
|
|
||||||
$scope.setEvent = function(event) {
|
$scope.setEvent = function(event) {
|
||||||
$scope.currentEvent = event;
|
$scope.currentEvent = event;
|
||||||
};
|
};
|
||||||
|
|
52
util/migrateslackwebhook.py
Normal file
52
util/migrateslackwebhook.py
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
import logging
|
||||||
|
import json
|
||||||
|
|
||||||
|
from app import app
|
||||||
|
from data.database import configure, RepositoryNotification, ExternalNotificationMethod
|
||||||
|
|
||||||
|
configure(app.config)
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
def run_slackwebhook_migration():
|
||||||
|
slack_method = ExternalNotificationMethod.get(ExternalNotificationMethod.name == "slack")
|
||||||
|
|
||||||
|
encountered = set()
|
||||||
|
while True:
|
||||||
|
found = list(RepositoryNotification.select().where(
|
||||||
|
RepositoryNotification.method == slack_method,
|
||||||
|
RepositoryNotification.config_json ** "%subdomain%",
|
||||||
|
~(RepositoryNotification.config_json ** "%url%")))
|
||||||
|
|
||||||
|
found = [f for f in found if not f.uuid in encountered]
|
||||||
|
|
||||||
|
if not found:
|
||||||
|
logger.debug('No additional records found')
|
||||||
|
return
|
||||||
|
|
||||||
|
logger.debug('Found %s records to be changed', len(found))
|
||||||
|
for notification in found:
|
||||||
|
encountered.add(notification.uuid)
|
||||||
|
|
||||||
|
try:
|
||||||
|
config = json.loads(notification.config_json)
|
||||||
|
except:
|
||||||
|
logging.error("Cannot parse config for noticification %s", notification.uuid)
|
||||||
|
continue
|
||||||
|
|
||||||
|
logger.debug("Checking notification %s", notification.uuid)
|
||||||
|
if 'subdomain' in config and 'token' in config:
|
||||||
|
subdomain = config['subdomain']
|
||||||
|
token = config['token']
|
||||||
|
new_url = 'https://%s.slack.com/services/hooks/incoming-webhook?token=%s' % (subdomain, token)
|
||||||
|
config['url'] = new_url
|
||||||
|
|
||||||
|
logger.debug("Updating notification %s to URL: %s", notification.uuid, new_url)
|
||||||
|
notification.config_json = json.dumps(config)
|
||||||
|
notification.save()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
|
logging.getLogger('boto').setLevel(logging.CRITICAL)
|
||||||
|
|
||||||
|
run_slackwebhook_migration()
|
|
@ -4,16 +4,25 @@ from functools import wraps
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
|
|
||||||
def parse_namespace_repository(repository):
|
def parse_namespace_repository(repository, tag=False):
|
||||||
parts = repository.rstrip('/').split('/', 1)
|
parts = repository.rstrip('/').split('/', 1)
|
||||||
if len(parts) < 2:
|
if len(parts) < 2:
|
||||||
namespace = 'library'
|
namespace = 'library'
|
||||||
repository = parts[0]
|
repository = parts[0]
|
||||||
else:
|
else:
|
||||||
(namespace, repository) = parts
|
(namespace, repository) = parts
|
||||||
repository = urllib.quote_plus(repository)
|
|
||||||
return (namespace, repository)
|
|
||||||
|
|
||||||
|
if tag:
|
||||||
|
parts = repository.split(':', 1)
|
||||||
|
if len(parts) < 2:
|
||||||
|
tag = None
|
||||||
|
else:
|
||||||
|
(repository, tag) = parts
|
||||||
|
|
||||||
|
repository = urllib.quote_plus(repository)
|
||||||
|
if tag:
|
||||||
|
return (namespace, repository, tag)
|
||||||
|
return (namespace, repository)
|
||||||
|
|
||||||
def parse_repository_name(f):
|
def parse_repository_name(f):
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
|
@ -22,6 +31,13 @@ def parse_repository_name(f):
|
||||||
return f(namespace, repository, *args, **kwargs)
|
return f(namespace, repository, *args, **kwargs)
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
def parse_repository_name_and_tag(f):
|
||||||
|
@wraps(f)
|
||||||
|
def wrapper(repository, *args, **kwargs):
|
||||||
|
(namespace, repository, tag) = parse_namespace_repository(repository, tag=True)
|
||||||
|
return f(namespace, repository, tag, *args, **kwargs)
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
def format_robot_username(parent_username, robot_shortname):
|
def format_robot_username(parent_username, robot_shortname):
|
||||||
return '%s+%s' % (parent_username, robot_shortname)
|
return '%s+%s' % (parent_username, robot_shortname)
|
||||||
|
|
Reference in a new issue