Merge branch 'master' of github.com:coreos-inc/quay
This commit is contained in:
commit
e7054a8690
8 changed files with 101 additions and 27 deletions
|
@ -380,6 +380,11 @@ def get_search():
|
||||||
resp.mimetype = 'application/json'
|
resp.mimetype = 'application/json'
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
|
# Note: This is *not* part of the Docker index spec. This is here for our own health check,
|
||||||
|
# since we have nginx handle the _ping below.
|
||||||
|
@index.route('/_internal_ping')
|
||||||
|
def internal_ping():
|
||||||
|
return make_response('true', 200)
|
||||||
|
|
||||||
@index.route('/_ping')
|
@index.route('/_ping')
|
||||||
@index.route('/_ping')
|
@index.route('/_ping')
|
||||||
|
|
|
@ -156,11 +156,21 @@ def v1():
|
||||||
@web.route('/health', methods=['GET'])
|
@web.route('/health', methods=['GET'])
|
||||||
@no_cache
|
@no_cache
|
||||||
def health():
|
def health():
|
||||||
|
client = app.config['HTTPCLIENT']
|
||||||
|
|
||||||
db_healthy = model.check_health()
|
db_healthy = model.check_health()
|
||||||
buildlogs_healthy = build_logs.check_health()
|
buildlogs_healthy = build_logs.check_health()
|
||||||
|
|
||||||
|
hostname_parts = app.config['SERVER_HOSTNAME'].split(':')
|
||||||
|
port = ''
|
||||||
|
if len(hostname_parts) == 2:
|
||||||
|
port = ':' + hostname_parts[1]
|
||||||
|
|
||||||
|
registry_url = '%s://localhost%s/v1/_internal_ping' % (app.config['PREFERRED_URL_SCHEME'], port)
|
||||||
|
registry_healthy = client.get(registry_url, verify=False, timeout=2).status_code == 200
|
||||||
|
|
||||||
check = HealthCheck.get_check(app.config['HEALTH_CHECKER'][0], app.config['HEALTH_CHECKER'][1])
|
check = HealthCheck.get_check(app.config['HEALTH_CHECKER'][0], app.config['HEALTH_CHECKER'][1])
|
||||||
(data, is_healthy) = check.conduct_healthcheck(db_healthy, buildlogs_healthy)
|
(data, is_healthy) = check.conduct_healthcheck(db_healthy, buildlogs_healthy, registry_healthy)
|
||||||
|
|
||||||
response = jsonify(dict(data=data, is_healthy=is_healthy))
|
response = jsonify(dict(data=data, is_healthy=is_healthy))
|
||||||
response.status_code = 200 if is_healthy else 503
|
response.status_code = 200 if is_healthy else 503
|
||||||
|
|
|
@ -7,7 +7,7 @@ class HealthCheck(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def conduct_healthcheck(self, db_healthy, buildlogs_healthy):
|
def conduct_healthcheck(self, db_healthy, buildlogs_healthy, registry_healthy):
|
||||||
"""
|
"""
|
||||||
Conducts any custom healthcheck work, returning a dict representing the HealthCheck
|
Conducts any custom healthcheck work, returning a dict representing the HealthCheck
|
||||||
output and a boolean indicating whether the instance is healthy.
|
output and a boolean indicating whether the instance is healthy.
|
||||||
|
@ -31,10 +31,11 @@ class LocalHealthCheck(HealthCheck):
|
||||||
def check_name(cls):
|
def check_name(cls):
|
||||||
return 'LocalHealthCheck'
|
return 'LocalHealthCheck'
|
||||||
|
|
||||||
def conduct_healthcheck(self, db_healthy, buildlogs_healthy):
|
def conduct_healthcheck(self, db_healthy, buildlogs_healthy, registry_healthy):
|
||||||
data = {
|
data = {
|
||||||
'db_healthy': db_healthy,
|
'db_healthy': db_healthy,
|
||||||
'buildlogs_healthy': buildlogs_healthy
|
'buildlogs_healthy': buildlogs_healthy,
|
||||||
|
'registry_healthy': registry_healthy
|
||||||
}
|
}
|
||||||
|
|
||||||
return (data, db_healthy and buildlogs_healthy)
|
return (data, db_healthy and buildlogs_healthy)
|
||||||
|
@ -49,10 +50,11 @@ class ProductionHealthCheck(HealthCheck):
|
||||||
def check_name(cls):
|
def check_name(cls):
|
||||||
return 'ProductionHealthCheck'
|
return 'ProductionHealthCheck'
|
||||||
|
|
||||||
def conduct_healthcheck(self, db_healthy, buildlogs_healthy):
|
def conduct_healthcheck(self, db_healthy, buildlogs_healthy, registry_healthy):
|
||||||
data = {
|
data = {
|
||||||
'db_healthy': db_healthy,
|
'db_healthy': db_healthy,
|
||||||
'buildlogs_healthy': buildlogs_healthy
|
'buildlogs_healthy': buildlogs_healthy,
|
||||||
|
'registry_healthy': registry_healthy
|
||||||
}
|
}
|
||||||
|
|
||||||
# Only report unhealthy if the machine cannot connect to the DB. Redis isn't required for
|
# Only report unhealthy if the machine cannot connect to the DB. Redis isn't required for
|
||||||
|
@ -79,6 +81,6 @@ class ProductionHealthCheck(HealthCheck):
|
||||||
|
|
||||||
# If RDS is down, then we still report the machine as healthy, so that it can handle
|
# If RDS is down, then we still report the machine as healthy, so that it can handle
|
||||||
# requests once RDS comes back up.
|
# requests once RDS comes back up.
|
||||||
return (data, not is_rds_working)
|
return (data, not is_rds_working and registry_healthy)
|
||||||
|
|
||||||
return (data, db_healthy)
|
return (data, db_healthy and registry_healthy)
|
||||||
|
|
|
@ -1096,12 +1096,6 @@ i.toggle-icon:hover {
|
||||||
border: 1px dashed #ccc;
|
border: 1px dashed #ccc;
|
||||||
}
|
}
|
||||||
|
|
||||||
.new-repo .initialize-repo .init-description {
|
|
||||||
color: #444;
|
|
||||||
font-size: 12px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.new-repo .initialize-repo .file-drop {
|
.new-repo .initialize-repo .file-drop {
|
||||||
margin: 10px;
|
margin: 10px;
|
||||||
}
|
}
|
||||||
|
@ -4915,3 +4909,20 @@ i.slack-icon {
|
||||||
#gen-token input[type="checkbox"] {
|
#gen-token input[type="checkbox"] {
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dockerfile-build-form table td {
|
||||||
|
vertical-align: top;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dockerfile-build-form input[type="file"] {
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dockerfile-build-form .help-text {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #aaa;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
padding-left: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="dockerfile-build-form" repository="repository" upload-failed="handleBuildFailed(message)"
|
<div class="dockerfile-build-form" repository="repository" upload-failed="handleBuildFailed(message)"
|
||||||
build-started="handleBuildStarted(build)" build-failed="handleBuildFailed(message)" start-now="startCounter"
|
build-started="handleBuildStarted(build)" build-failed="handleBuildFailed(message)" start-now="startCounter"
|
||||||
has-dockerfile="hasDockerfile" uploading="uploading" building="building"></div>
|
is-ready="hasDockerfile" uploading="uploading" building="building"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-primary" ng-click="startBuild()" ng-disabled="building || uploading || !hasDockerfile">Start Build</button>
|
<button type="button" class="btn btn-primary" ng-click="startBuild()" ng-disabled="building || uploading || !hasDockerfile">Start Build</button>
|
||||||
|
|
|
@ -11,9 +11,44 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="container" ng-show="!uploading && !building">
|
<div class="container" ng-show="!uploading && !building">
|
||||||
<div class="init-description">
|
<table>
|
||||||
Upload a <b>Dockerfile</b> or an archive (<code>.zip</code> or <code>.tar.gz</code>) containing a Dockerfile <b>in the root directory</b>
|
<tr>
|
||||||
</div>
|
<td style="vertical-align: middle;">Dockerfile or <code>.tar.gz</code> or <code>.zip</code>:</td>
|
||||||
<input id="file-drop" class="file-drop" type="file" file-present="internal.hasDockerfile">
|
<td><input id="file-drop" class="file-drop" type="file" file-present="internal.hasDockerfile">
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<td>
|
||||||
|
<div class="help-text">If an archive, the Dockerfile must be at the root</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Base Image Pull Credentials:</td>
|
||||||
|
<td>
|
||||||
|
<!-- Select credentials -->
|
||||||
|
<div class="btn-group btn-group-sm">
|
||||||
|
<button type="button" class="btn btn-default"
|
||||||
|
ng-class="is_public ? 'active btn-info' : ''"
|
||||||
|
ng-click="is_public = true">
|
||||||
|
None
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-default"
|
||||||
|
ng-class="is_public ? '' : 'active btn-info'"
|
||||||
|
ng-click="is_public = false">
|
||||||
|
<i class="fa fa-wrench"></i>
|
||||||
|
Robot account
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Robot Select -->
|
||||||
|
<div ng-show="!is_public" style="margin-top: 10px">
|
||||||
|
<div class="entity-search" namespace="repository.namespace"
|
||||||
|
placeholder="'Select robot account for pulling...'"
|
||||||
|
current-entity="pull_entity"
|
||||||
|
allowed-entities="['robot']"></div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -4354,6 +4354,8 @@ quayApp.directive('entitySearch', function () {
|
||||||
|
|
||||||
if (classes.length > 1) {
|
if (classes.length > 1) {
|
||||||
classes[classes.length - 1] = 'or ' + classes[classes.length - 1];
|
classes[classes.length - 1] = 'or ' + classes[classes.length - 1];
|
||||||
|
} else if (classes.length == 0) {
|
||||||
|
return '<div class="tt-empty">No matching entities found</div>';
|
||||||
}
|
}
|
||||||
|
|
||||||
var class_string = '';
|
var class_string = '';
|
||||||
|
@ -4439,7 +4441,6 @@ quayApp.directive('entitySearch', function () {
|
||||||
|
|
||||||
$scope.$watch('namespace', function(namespace) {
|
$scope.$watch('namespace', function(namespace) {
|
||||||
if (!namespace) { return; }
|
if (!namespace) { return; }
|
||||||
|
|
||||||
$scope.isAdmin = UserService.isNamespaceAdmin(namespace);
|
$scope.isAdmin = UserService.isNamespaceAdmin(namespace);
|
||||||
$scope.isOrganization = !!UserService.getOrganization(namespace);
|
$scope.isOrganization = !!UserService.getOrganization(namespace);
|
||||||
});
|
});
|
||||||
|
@ -6229,7 +6230,7 @@ quayApp.directive('dockerfileBuildForm', function () {
|
||||||
scope: {
|
scope: {
|
||||||
'repository': '=repository',
|
'repository': '=repository',
|
||||||
'startNow': '=startNow',
|
'startNow': '=startNow',
|
||||||
'hasDockerfile': '=hasDockerfile',
|
'isReady': '=isReady',
|
||||||
'uploadFailed': '&uploadFailed',
|
'uploadFailed': '&uploadFailed',
|
||||||
'uploadStarted': '&uploadStarted',
|
'uploadStarted': '&uploadStarted',
|
||||||
'buildStarted': '&buildStarted',
|
'buildStarted': '&buildStarted',
|
||||||
|
@ -6240,6 +6241,8 @@ quayApp.directive('dockerfileBuildForm', function () {
|
||||||
},
|
},
|
||||||
controller: function($scope, $element, ApiService) {
|
controller: function($scope, $element, ApiService) {
|
||||||
$scope.internal = {'hasDockerfile': false};
|
$scope.internal = {'hasDockerfile': false};
|
||||||
|
$scope.pull_entity = null;
|
||||||
|
$scope.is_public = true;
|
||||||
|
|
||||||
var handleBuildFailed = function(message) {
|
var handleBuildFailed = function(message) {
|
||||||
message = message || 'Dockerfile build failed to start';
|
message = message || 'Dockerfile build failed to start';
|
||||||
|
@ -6313,8 +6316,12 @@ quayApp.directive('dockerfileBuildForm', function () {
|
||||||
'file_id': fileId
|
'file_id': fileId
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (!$scope.is_public && $scope.pull_entity) {
|
||||||
|
data['pull_robot'] = $scope.pull_entity['name'];
|
||||||
|
}
|
||||||
|
|
||||||
var params = {
|
var params = {
|
||||||
'repository': repo.namespace + '/' + repo.name
|
'repository': repo.namespace + '/' + repo.name,
|
||||||
};
|
};
|
||||||
|
|
||||||
ApiService.requestRepoBuild(data, params).then(function(resp) {
|
ApiService.requestRepoBuild(data, params).then(function(resp) {
|
||||||
|
@ -6392,9 +6399,13 @@ quayApp.directive('dockerfileBuildForm', function () {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.$watch('internal.hasDockerfile', function(d) {
|
var checkIsReady = function() {
|
||||||
$scope.hasDockerfile = d;
|
$scope.isReady = $scope.internal.hasDockerfile && ($scope.is_public || $scope.pull_entity);
|
||||||
});
|
};
|
||||||
|
|
||||||
|
$scope.$watch('pull_entity', checkIsReady);
|
||||||
|
$scope.$watch('is_public', checkIsReady);
|
||||||
|
$scope.$watch('internal.hasDockerfile', checkIsReady);
|
||||||
|
|
||||||
$scope.$watch('startNow', function() {
|
$scope.$watch('startNow', function() {
|
||||||
if ($scope.startNow && $scope.repository && !$scope.uploading && !$scope.building) {
|
if ($scope.startNow && $scope.repository && !$scope.uploading && !$scope.building) {
|
||||||
|
|
|
@ -143,9 +143,9 @@
|
||||||
<div class="section-title">Upload <span ng-if="repo.initialize == 'dockerfile'">Dockerfile</span><span ng-if="repo.initialize == 'zipfile'">Archive</span></div>
|
<div class="section-title">Upload <span ng-if="repo.initialize == 'dockerfile'">Dockerfile</span><span ng-if="repo.initialize == 'zipfile'">Archive</span></div>
|
||||||
<div style="padding-top: 20px;">
|
<div style="padding-top: 20px;">
|
||||||
<div class="initialize-repo">
|
<div class="initialize-repo">
|
||||||
<div class="dockerfile-build-form" repository="createdForBuild" upload-failed="handleBuildFailed(message)"
|
<div class="dockerfile-build-form" repository="createdForBuild || repo" upload-failed="handleBuildFailed(message)"
|
||||||
build-started="handleBuildStarted()" build-failed="handleBuildFailed(message)" start-now="createdForBuild"
|
build-started="handleBuildStarted()" build-failed="handleBuildFailed(message)" start-now="createdForBuild"
|
||||||
has-dockerfile="hasDockerfile" uploading="uploading" building="building"></div>
|
is-ready="hasDockerfile" uploading="uploading" building="building"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
Reference in a new issue