Merge pull request #2850 from coreos-inc/jpmc-features
Features for JPMC
This commit is contained in:
commit
46e1bd9c75
8 changed files with 37 additions and 10 deletions
10
config.py
10
config.py
|
@ -269,6 +269,16 @@ class DefaultConfig(ImmutableConfig):
|
||||||
# only private repositories can be returned.
|
# only private repositories can be returned.
|
||||||
FEATURE_PUBLIC_CATALOG = False
|
FEATURE_PUBLIC_CATALOG = False
|
||||||
|
|
||||||
|
# Feature Flag: If set to true, build logs may be read by those with read access to the repo,
|
||||||
|
# rather than only write access or admin access.
|
||||||
|
FEATURE_READER_BUILD_LOGS = False
|
||||||
|
|
||||||
|
# If a namespace is defined in the public namespace list, then it will appear on *all*
|
||||||
|
# user's repository list pages, regardless of whether that user is a member of the namespace.
|
||||||
|
# Typically, this is used by an enterprise customer in configuring a set of "well-known"
|
||||||
|
# namespaces.
|
||||||
|
PUBLIC_NAMESPACES = []
|
||||||
|
|
||||||
# The namespace to use for library repositories.
|
# The namespace to use for library repositories.
|
||||||
# Note: This must remain 'library' until Docker removes their hard-coded namespace for libraries.
|
# Note: This must remain 'library' until Docker removes their hard-coded namespace for libraries.
|
||||||
# See: https://github.com/docker/docker/blob/master/registry/session.go#L320
|
# See: https://github.com/docker/docker/blob/master/registry/session.go#L320
|
||||||
|
|
|
@ -8,6 +8,8 @@ import os
|
||||||
from flask import request
|
from flask import request
|
||||||
from urlparse import urlparse
|
from urlparse import urlparse
|
||||||
|
|
||||||
|
import features
|
||||||
|
|
||||||
from app import userfiles as user_files, build_logs, log_archive, dockerfile_build_queue
|
from app import userfiles as user_files, build_logs, log_archive, dockerfile_build_queue
|
||||||
from auth.permissions import (ReadRepositoryPermission, ModifyRepositoryPermission,
|
from auth.permissions import (ReadRepositoryPermission, ModifyRepositoryPermission,
|
||||||
AdministerRepositoryPermission, AdministerOrganizationPermission,
|
AdministerRepositoryPermission, AdministerOrganizationPermission,
|
||||||
|
@ -147,7 +149,7 @@ def build_status_view(build_obj):
|
||||||
'error': error,
|
'error': error,
|
||||||
}
|
}
|
||||||
|
|
||||||
if can_write:
|
if can_write or features.READER_BUILD_LOGS:
|
||||||
if build_obj.resource_key is not None:
|
if build_obj.resource_key is not None:
|
||||||
resp['archive_url'] = user_files.get_file_url(build_obj.resource_key, requires_cors=True)
|
resp['archive_url'] = user_files.get_file_url(build_obj.resource_key, requires_cors=True)
|
||||||
elif job_config.get('archive_url', None):
|
elif job_config.get('archive_url', None):
|
||||||
|
@ -424,11 +426,15 @@ def get_logs_or_log_url(build):
|
||||||
@path_param('build_uuid', 'The UUID of the build')
|
@path_param('build_uuid', 'The UUID of the build')
|
||||||
class RepositoryBuildLogs(RepositoryParamResource):
|
class RepositoryBuildLogs(RepositoryParamResource):
|
||||||
""" Resource for loading repository build logs. """
|
""" Resource for loading repository build logs. """
|
||||||
@require_repo_write
|
@require_repo_read
|
||||||
@nickname('getRepoBuildLogs')
|
@nickname('getRepoBuildLogs')
|
||||||
@disallow_for_app_repositories
|
@disallow_for_app_repositories
|
||||||
def get(self, namespace, repository, build_uuid):
|
def get(self, namespace, repository, build_uuid):
|
||||||
""" Return the build logs for the build specified by the build uuid. """
|
""" Return the build logs for the build specified by the build uuid. """
|
||||||
|
can_write = ModifyRepositoryPermission(namespace, repository).can()
|
||||||
|
if not features.READER_BUILD_LOGS and not can_write:
|
||||||
|
raise Unauthorized()
|
||||||
|
|
||||||
build = model.build.get_repository_build(build_uuid)
|
build = model.build.get_repository_build(build_uuid)
|
||||||
if (not build or build.repository.name != repository or
|
if (not build or build.repository.name != repository or
|
||||||
build.repository.namespace_user.username != namespace):
|
build.repository.namespace_user.username != namespace):
|
||||||
|
|
|
@ -79,6 +79,7 @@ def user_view(user, previous_username=None):
|
||||||
'name': o.username,
|
'name': o.username,
|
||||||
'avatar': avatar.get_data_for_org(o),
|
'avatar': avatar.get_data_for_org(o),
|
||||||
'can_create_repo': CreateRepositoryPermission(o.username).can(),
|
'can_create_repo': CreateRepositoryPermission(o.username).can(),
|
||||||
|
'public': o.username in app.config.get('PUBLIC_NAMESPACES', []),
|
||||||
}
|
}
|
||||||
|
|
||||||
if user_admin:
|
if user_admin:
|
||||||
|
@ -89,7 +90,13 @@ def user_view(user, previous_username=None):
|
||||||
|
|
||||||
return org_response
|
return org_response
|
||||||
|
|
||||||
organizations = model.organization.get_user_organizations(user.username)
|
# Retrieve the organizations for the user.
|
||||||
|
organizations = list(model.organization.get_user_organizations(user.username))
|
||||||
|
|
||||||
|
# Add any public namespaces.
|
||||||
|
public_namespaces = app.config.get('PUBLIC_NAMESPACES', [])
|
||||||
|
if public_namespaces:
|
||||||
|
organizations.extend([model.user.get_namespace_user(ns) for ns in public_namespaces])
|
||||||
|
|
||||||
def login_view(login):
|
def login_view(login):
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<span class="build-mini-status-element">
|
<span class="build-mini-status-element">
|
||||||
<span class="anchor"
|
<span class="anchor"
|
||||||
href="/repository/{{ build.repository.namespace }}/{{ build.repository.name }}/build/{{ build.id }}"
|
href="/repository/{{ build.repository.namespace }}/{{ build.repository.name }}/build/{{ build.id }}"
|
||||||
is-only-text="!isAdmin">
|
is-only-text="!canView">
|
||||||
<div>
|
<div>
|
||||||
<span class="build-state-icon" build="build"></span>
|
<span class="build-state-icon" build="build"></span>
|
||||||
<span class="timing">
|
<span class="timing">
|
||||||
|
|
|
@ -40,7 +40,7 @@
|
||||||
<!-- Builds -->
|
<!-- Builds -->
|
||||||
<div ng-if="builds && builds.length">
|
<div ng-if="builds && builds.length">
|
||||||
<div class="build-mini-status" ng-repeat="build in builds" build="build"
|
<div class="build-mini-status" ng-repeat="build in builds" build="build"
|
||||||
is-admin="repository.can_admin"></div>
|
can-view="repository.can_write || Features.READER_BUILD_LOGS"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- View All -->
|
<!-- View All -->
|
||||||
|
|
|
@ -13,7 +13,9 @@ angular.module('quay').directive('repoPanelInfo', function () {
|
||||||
'builds': '=builds',
|
'builds': '=builds',
|
||||||
'isEnabled': '=isEnabled'
|
'isEnabled': '=isEnabled'
|
||||||
},
|
},
|
||||||
controller: function($scope, $element, ApiService, Config) {
|
controller: function($scope, $element, ApiService, Config, Features) {
|
||||||
|
$scope.Features = Features;
|
||||||
|
|
||||||
$scope.$watch('repository', function(repository) {
|
$scope.$watch('repository', function(repository) {
|
||||||
if (!$scope.repository) { return; }
|
if (!$scope.repository) { return; }
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ angular.module('quay').directive('buildMiniStatus', function () {
|
||||||
restrict: 'C',
|
restrict: 'C',
|
||||||
scope: {
|
scope: {
|
||||||
'build': '=build',
|
'build': '=build',
|
||||||
'isAdmin': '=isAdmin'
|
'canView': '=canView'
|
||||||
},
|
},
|
||||||
controller: function($scope, $element, BuildService) {
|
controller: function($scope, $element, BuildService) {
|
||||||
$scope.isBuilding = function(build) {
|
$scope.isBuilding = function(build) {
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
|
|
||||||
function RepoListCtrl($scope, $sanitize, $q, Restangular, UserService, ApiService, Features) {
|
function RepoListCtrl($scope, $sanitize, $q, Restangular, UserService, ApiService, Features, Config) {
|
||||||
$scope.namespace = null;
|
$scope.namespace = null;
|
||||||
$scope.page = 1;
|
$scope.page = 1;
|
||||||
$scope.publicPageCount = null;
|
$scope.publicPageCount = null;
|
||||||
|
@ -35,7 +35,8 @@
|
||||||
user.organizations.map(function(org) {
|
user.organizations.map(function(org) {
|
||||||
$scope.namespaces.push({
|
$scope.namespaces.push({
|
||||||
'name': org.name,
|
'name': org.name,
|
||||||
'avatar': org.avatar
|
'avatar': org.avatar,
|
||||||
|
'public': org.public
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -99,7 +100,8 @@
|
||||||
var options = {
|
var options = {
|
||||||
'namespace': namespace.name,
|
'namespace': namespace.name,
|
||||||
'last_modified': true,
|
'last_modified': true,
|
||||||
'popularity': true
|
'popularity': true,
|
||||||
|
'public': namespace.public
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace.repositories = ApiService.listReposAsResource().withOptions(options).get(function(resp) {
|
namespace.repositories = ApiService.listReposAsResource().withOptions(options).get(function(resp) {
|
||||||
|
|
Reference in a new issue