Merge branch 'master' of github.com:coreos-inc/quay
This commit is contained in:
commit
f5138792ad
7 changed files with 78 additions and 15 deletions
9
app.py
9
app.py
|
@ -17,15 +17,15 @@ from data.userfiles import Userfiles
|
||||||
from data.users import UserAuthentication
|
from data.users import UserAuthentication
|
||||||
from util.analytics import Analytics
|
from util.analytics import Analytics
|
||||||
from util.exceptionlog import Sentry
|
from util.exceptionlog import Sentry
|
||||||
from util.queuemetrics import QueueMetrics
|
|
||||||
from util.names import urn_generator
|
from util.names import urn_generator
|
||||||
from util.oauth import GoogleOAuthConfig, GithubOAuthConfig
|
from util.oauth import GoogleOAuthConfig, GithubOAuthConfig
|
||||||
from data.billing import Billing
|
from data.billing import Billing
|
||||||
from data.buildlogs import BuildLogs
|
from data.buildlogs import BuildLogs
|
||||||
from data.archivedlogs import LogArchive
|
from data.archivedlogs import LogArchive
|
||||||
from data.queue import WorkQueue
|
|
||||||
from data.userevent import UserEventsBuilderModule
|
from data.userevent import UserEventsBuilderModule
|
||||||
from avatars.avatars import Avatar
|
from avatars.avatars import Avatar
|
||||||
|
from util.queuemetrics import QueueMetrics
|
||||||
|
from data.queue import WorkQueue
|
||||||
|
|
||||||
|
|
||||||
class Config(BaseConfig):
|
class Config(BaseConfig):
|
||||||
|
@ -130,16 +130,17 @@ analytics = Analytics(app)
|
||||||
billing = Billing(app)
|
billing = Billing(app)
|
||||||
sentry = Sentry(app)
|
sentry = Sentry(app)
|
||||||
build_logs = BuildLogs(app)
|
build_logs = BuildLogs(app)
|
||||||
queue_metrics = QueueMetrics(app)
|
|
||||||
authentication = UserAuthentication(app)
|
authentication = UserAuthentication(app)
|
||||||
userevents = UserEventsBuilderModule(app)
|
userevents = UserEventsBuilderModule(app)
|
||||||
|
queue_metrics = QueueMetrics(app)
|
||||||
|
|
||||||
|
tf = app.config['DB_TRANSACTION_FACTORY']
|
||||||
|
|
||||||
github_login = GithubOAuthConfig(app, 'GITHUB_LOGIN_CONFIG')
|
github_login = GithubOAuthConfig(app, 'GITHUB_LOGIN_CONFIG')
|
||||||
github_trigger = GithubOAuthConfig(app, 'GITHUB_TRIGGER_CONFIG')
|
github_trigger = GithubOAuthConfig(app, 'GITHUB_TRIGGER_CONFIG')
|
||||||
google_login = GoogleOAuthConfig(app, 'GOOGLE_LOGIN_CONFIG')
|
google_login = GoogleOAuthConfig(app, 'GOOGLE_LOGIN_CONFIG')
|
||||||
oauth_apps = [github_login, github_trigger, google_login]
|
oauth_apps = [github_login, github_trigger, google_login]
|
||||||
|
|
||||||
tf = app.config['DB_TRANSACTION_FACTORY']
|
|
||||||
image_diff_queue = WorkQueue(app.config['DIFFS_QUEUE_NAME'], tf)
|
image_diff_queue = WorkQueue(app.config['DIFFS_QUEUE_NAME'], tf)
|
||||||
dockerfile_build_queue = WorkQueue(app.config['DOCKERFILE_BUILD_QUEUE_NAME'], tf,
|
dockerfile_build_queue = WorkQueue(app.config['DOCKERFILE_BUILD_QUEUE_NAME'], tf,
|
||||||
reporter=queue_metrics.report)
|
reporter=queue_metrics.report)
|
||||||
|
|
|
@ -4,6 +4,12 @@
|
||||||
<meta name="viewport" content="width=device-width" />
|
<meta name="viewport" content="width=device-width" />
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
<title>{{ subject }}</title>
|
<title>{{ subject }}</title>
|
||||||
|
|
||||||
|
{% if action_metadata %}
|
||||||
|
<script type="application/ld+json">
|
||||||
|
{{ action_metadata }}
|
||||||
|
</script>
|
||||||
|
{% endif %}
|
||||||
</head>
|
</head>
|
||||||
<body bgcolor="#FFFFFF" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; width: 100% !important; height: 100%; margin: 0; padding: 0;"><style type="text/css">
|
<body bgcolor="#FFFFFF" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; width: 100% !important; height: 100%; margin: 0; padding: 0;"><style type="text/css">
|
||||||
@media only screen and (max-width: 600px) {
|
@media only screen and (max-width: 600px) {
|
||||||
|
|
|
@ -11,7 +11,7 @@ from random import SystemRandom
|
||||||
|
|
||||||
from data import model
|
from data import model
|
||||||
from data.database import db
|
from data.database import db
|
||||||
from app import app, login_manager, dockerfile_build_queue, notification_queue, oauth_apps
|
from app import app, login_manager, oauth_apps, dockerfile_build_queue
|
||||||
|
|
||||||
from auth.permissions import QuayDeferredPermissionUser
|
from auth.permissions import QuayDeferredPermissionUser
|
||||||
from auth import scopes
|
from auth import scopes
|
||||||
|
|
|
@ -7,7 +7,6 @@ from endpoints.index import index
|
||||||
from endpoints.tags import tags
|
from endpoints.tags import tags
|
||||||
from endpoints.registry import registry
|
from endpoints.registry import registry
|
||||||
|
|
||||||
|
|
||||||
application.register_blueprint(index, url_prefix='/v1')
|
application.register_blueprint(index, url_prefix='/v1')
|
||||||
application.register_blueprint(tags, url_prefix='/v1')
|
application.register_blueprint(tags, url_prefix='/v1')
|
||||||
application.register_blueprint(registry, url_prefix='/v1')
|
application.register_blueprint(registry, url_prefix='/v1')
|
||||||
|
|
|
@ -67,6 +67,7 @@ class SendToCloudWatch(Thread):
|
||||||
class QueueMetrics(object):
|
class QueueMetrics(object):
|
||||||
def __init__(self, app=None):
|
def __init__(self, app=None):
|
||||||
self.app = app
|
self.app = app
|
||||||
|
self.sender = None
|
||||||
if app is not None:
|
if app is not None:
|
||||||
self.state = self.init_app(app)
|
self.state = self.init_app(app)
|
||||||
else:
|
else:
|
||||||
|
@ -85,8 +86,7 @@ class QueueMetrics(object):
|
||||||
request_queue = Queue()
|
request_queue = Queue()
|
||||||
reporter = QueueingCloudWatchReporter(request_queue, namespace, req_capacity_name,
|
reporter = QueueingCloudWatchReporter(request_queue, namespace, req_capacity_name,
|
||||||
build_percent_name)
|
build_percent_name)
|
||||||
sender = SendToCloudWatch(request_queue, access_key, secret_key)
|
self.sender = SendToCloudWatch(request_queue, access_key, secret_key)
|
||||||
sender.start()
|
|
||||||
else:
|
else:
|
||||||
reporter = NullReporter()
|
reporter = NullReporter()
|
||||||
|
|
||||||
|
@ -95,5 +95,11 @@ class QueueMetrics(object):
|
||||||
app.extensions['queuemetrics'] = reporter
|
app.extensions['queuemetrics'] = reporter
|
||||||
return reporter
|
return reporter
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
logger.debug('Asked to start CloudWatch reporter')
|
||||||
|
if self.sender is not None:
|
||||||
|
logger.debug('Starting CloudWatch reporter')
|
||||||
|
self.sender.start()
|
||||||
|
|
||||||
def __getattr__(self, name):
|
def __getattr__(self, name):
|
||||||
return getattr(self.state, name, None)
|
return getattr(self.state, name, None)
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import logging
|
import logging
|
||||||
import traceback
|
import traceback
|
||||||
|
import json
|
||||||
|
|
||||||
from flask.ext.mail import Message
|
from flask.ext.mail import Message
|
||||||
|
|
||||||
|
@ -13,7 +14,42 @@ template_env = get_template_env("emails")
|
||||||
class CannotSendEmailException(Exception):
|
class CannotSendEmailException(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def send_email(recipient, subject, template_file, parameters):
|
class GmailAction(object):
|
||||||
|
""" Represents an action that can be taken in Gmail in response to the email. """
|
||||||
|
def __init__(self, metadata):
|
||||||
|
self.metadata = metadata
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def confirm(name, url, description):
|
||||||
|
return GmailAction({
|
||||||
|
"@context": "http://schema.org",
|
||||||
|
"@type": "EmailMessage",
|
||||||
|
"action": {
|
||||||
|
"@type": 'ConfirmAction',
|
||||||
|
"name": name,
|
||||||
|
"handler": {
|
||||||
|
"@type": "HttpActionHandler",
|
||||||
|
"url": get_app_url() + '/' + url
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"description": description
|
||||||
|
})
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def view(name, url, description):
|
||||||
|
return GmailAction({
|
||||||
|
"@context": "http://schema.org",
|
||||||
|
"@type": "EmailMessage",
|
||||||
|
"action": {
|
||||||
|
"@type": 'ViewAction',
|
||||||
|
"name": name,
|
||||||
|
"url": get_app_url() + '/' + url
|
||||||
|
},
|
||||||
|
"description": description
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
def send_email(recipient, subject, template_file, parameters, action=None):
|
||||||
app_title = app.config['REGISTRY_TITLE_SHORT']
|
app_title = app.config['REGISTRY_TITLE_SHORT']
|
||||||
app_url = get_app_url()
|
app_url = get_app_url()
|
||||||
|
|
||||||
|
@ -29,7 +65,8 @@ def send_email(recipient, subject, template_file, parameters):
|
||||||
'app_logo': 'https://quay.io/static/img/quay-logo.png', # TODO: make this pull from config
|
'app_logo': 'https://quay.io/static/img/quay-logo.png', # TODO: make this pull from config
|
||||||
'app_url': app_url,
|
'app_url': app_url,
|
||||||
'app_title': app_title,
|
'app_title': app_title,
|
||||||
'app_link': app_link_handler
|
'app_link': app_link_handler,
|
||||||
|
'action_metadata': json.dumps(action.metadata) if action else None
|
||||||
})
|
})
|
||||||
|
|
||||||
rendered_html = template_env.get_template(template_file + '.html').render(parameters)
|
rendered_html = template_env.get_template(template_file + '.html').render(parameters)
|
||||||
|
@ -61,25 +98,34 @@ def send_change_email(username, email, token):
|
||||||
})
|
})
|
||||||
|
|
||||||
def send_confirmation_email(username, email, token):
|
def send_confirmation_email(username, email, token):
|
||||||
|
action = GmailAction.confirm('Confirm E-mail', 'confirm?code=' + token,
|
||||||
|
'Verification of e-mail address')
|
||||||
|
|
||||||
send_email(email, 'Please confirm your e-mail address', 'confirmemail', {
|
send_email(email, 'Please confirm your e-mail address', 'confirmemail', {
|
||||||
'username': username,
|
'username': username,
|
||||||
'token': token
|
'token': token
|
||||||
})
|
}, action=action)
|
||||||
|
|
||||||
def send_repo_authorization_email(namespace, repository, email, token):
|
def send_repo_authorization_email(namespace, repository, email, token):
|
||||||
|
action = GmailAction.confirm('Verify E-mail', 'authrepoemail?code=' + token,
|
||||||
|
'Verification of e-mail address')
|
||||||
|
|
||||||
subject = 'Please verify your e-mail address for repository %s/%s' % (namespace, repository)
|
subject = 'Please verify your e-mail address for repository %s/%s' % (namespace, repository)
|
||||||
send_email(email, subject, 'repoauthorizeemail', {
|
send_email(email, subject, 'repoauthorizeemail', {
|
||||||
'namespace': namespace,
|
'namespace': namespace,
|
||||||
'repository': repository,
|
'repository': repository,
|
||||||
'token': token
|
'token': token
|
||||||
})
|
}, action=action)
|
||||||
|
|
||||||
def send_recovery_email(email, token):
|
def send_recovery_email(email, token):
|
||||||
|
action = GmailAction.view('Recover Account', 'recovery?code=' + token,
|
||||||
|
'Recovery of an account')
|
||||||
|
|
||||||
subject = 'Account recovery'
|
subject = 'Account recovery'
|
||||||
send_email(email, subject, 'recovery', {
|
send_email(email, subject, 'recovery', {
|
||||||
'email': email,
|
'email': email,
|
||||||
'token': token
|
'token': token
|
||||||
})
|
}, action=action)
|
||||||
|
|
||||||
def send_payment_failed(email, username):
|
def send_payment_failed(email, username):
|
||||||
send_email(email, 'Subscription Payment Failure', 'paymentfailure', {
|
send_email(email, 'Subscription Payment Failure', 'paymentfailure', {
|
||||||
|
@ -87,12 +133,15 @@ def send_payment_failed(email, username):
|
||||||
})
|
})
|
||||||
|
|
||||||
def send_org_invite_email(member_name, member_email, orgname, team, adder, code):
|
def send_org_invite_email(member_name, member_email, orgname, team, adder, code):
|
||||||
|
action = GmailAction.view('Join %s' % team, 'confirminvite?code=' + code,
|
||||||
|
'Invitation to join a team')
|
||||||
|
|
||||||
send_email(member_email, 'Invitation to join team', 'teaminvite', {
|
send_email(member_email, 'Invitation to join team', 'teaminvite', {
|
||||||
'inviter': adder,
|
'inviter': adder,
|
||||||
'token': code,
|
'token': code,
|
||||||
'organization': orgname,
|
'organization': orgname,
|
||||||
'teamname': team
|
'teamname': team
|
||||||
})
|
}, action=action)
|
||||||
|
|
||||||
|
|
||||||
def send_invoice_email(email, contents):
|
def send_invoice_email(email, contents):
|
||||||
|
|
4
web.py
4
web.py
|
@ -1,7 +1,7 @@
|
||||||
import logging
|
import logging
|
||||||
import logging.config
|
import logging.config
|
||||||
|
|
||||||
from app import app as application
|
from app import app as application, queue_metrics
|
||||||
|
|
||||||
from endpoints.api import api_bp
|
from endpoints.api import api_bp
|
||||||
from endpoints.web import web
|
from endpoints.web import web
|
||||||
|
@ -9,6 +9,8 @@ from endpoints.webhooks import webhooks
|
||||||
from endpoints.realtime import realtime
|
from endpoints.realtime import realtime
|
||||||
from endpoints.callbacks import callback
|
from endpoints.callbacks import callback
|
||||||
|
|
||||||
|
# Start the cloudwatch reporting.
|
||||||
|
queue_metrics.run()
|
||||||
|
|
||||||
application.register_blueprint(web)
|
application.register_blueprint(web)
|
||||||
application.register_blueprint(callback, url_prefix='/oauth2')
|
application.register_blueprint(callback, url_prefix='/oauth2')
|
||||||
|
|
Reference in a new issue