Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2019-09-19 14:50:12 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2019-09-19 14:50:12 +0300
commit6cd5b7dbfaa4ff630ecbbfe351a1faac5fc71a8d (patch)
treed3563b9f60936c18a02185e2e53b424bb1b83539 /app
parentb3e0658cb1fbc7c8e7dd381467c656f2e675ee46 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/boards/index.js1
-rw-r--r--app/assets/javascripts/commons/vue.js1
-rw-r--r--app/assets/javascripts/diff_notes/services/resolve.js1
-rw-r--r--app/assets/javascripts/issue_show/index.js1
-rw-r--r--app/assets/javascripts/lib/utils/axios_utils.js8
-rw-r--r--app/assets/javascripts/vue_shared/components/pagination/table_pagination.vue3
-rw-r--r--app/assets/javascripts/vue_shared/vue_resource_interceptor.js34
-rw-r--r--app/assets/stylesheets/framework/contextual_sidebar.scss2
-rw-r--r--app/controllers/projects/registry/tags_controller.rb35
-rw-r--r--app/services/projects/container_repository/cleanup_tags_service.rb2
-rw-r--r--app/services/projects/container_repository/delete_tags_service.rb66
11 files changed, 83 insertions, 71 deletions
diff --git a/app/assets/javascripts/boards/index.js b/app/assets/javascripts/boards/index.js
index 3bded4a3258..da2669e7cde 100644
--- a/app/assets/javascripts/boards/index.js
+++ b/app/assets/javascripts/boards/index.js
@@ -22,7 +22,6 @@ import Board from 'ee_else_ce/boards/components/board';
import BoardSidebar from 'ee_else_ce/boards/components/board_sidebar';
import initNewListDropdown from 'ee_else_ce/boards/components/new_list_dropdown';
import BoardAddIssuesModal from '~/boards/components/modal/index.vue';
-import '~/vue_shared/vue_resource_interceptor';
import {
NavigationType,
convertObjectPropsToCamelCase,
diff --git a/app/assets/javascripts/commons/vue.js b/app/assets/javascripts/commons/vue.js
index 798623b94fb..8b62d78c043 100644
--- a/app/assets/javascripts/commons/vue.js
+++ b/app/assets/javascripts/commons/vue.js
@@ -1,5 +1,4 @@
import Vue from 'vue';
-import '../vue_shared/vue_resource_interceptor';
if (process.env.NODE_ENV !== 'production') {
Vue.config.productionTip = false;
diff --git a/app/assets/javascripts/diff_notes/services/resolve.js b/app/assets/javascripts/diff_notes/services/resolve.js
index 0687028ca54..27990b0a45e 100644
--- a/app/assets/javascripts/diff_notes/services/resolve.js
+++ b/app/assets/javascripts/diff_notes/services/resolve.js
@@ -2,7 +2,6 @@
import Vue from 'vue';
import Flash from '../../flash';
-import '../../vue_shared/vue_resource_interceptor';
import { __ } from '~/locale';
window.gl = window.gl || {};
diff --git a/app/assets/javascripts/issue_show/index.js b/app/assets/javascripts/issue_show/index.js
index 5a9dd91817e..4bcba72bc23 100644
--- a/app/assets/javascripts/issue_show/index.js
+++ b/app/assets/javascripts/issue_show/index.js
@@ -2,7 +2,6 @@ import Vue from 'vue';
import { initSidebarTracking } from 'ee_else_ce/event_tracking/issue_sidebar';
import issuableApp from './components/app.vue';
import { parseIssuableData } from './utils/parse_data';
-import '../vue_shared/vue_resource_interceptor';
export default function initIssueableApp() {
return new Vue({
diff --git a/app/assets/javascripts/lib/utils/axios_utils.js b/app/assets/javascripts/lib/utils/axios_utils.js
index 37721cd030c..c17f62c671c 100644
--- a/app/assets/javascripts/lib/utils/axios_utils.js
+++ b/app/assets/javascripts/lib/utils/axios_utils.js
@@ -8,19 +8,19 @@ axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
// Maintain a global counter for active requests
// see: spec/support/wait_for_requests.rb
axios.interceptors.request.use(config => {
- window.activeVueResources = window.activeVueResources || 0;
- window.activeVueResources += 1;
+ window.pendingRequests = window.pendingRequests || 0;
+ window.pendingRequests += 1;
return config;
});
// Remove the global counter
axios.interceptors.response.use(
response => {
- window.activeVueResources -= 1;
+ window.pendingRequests -= 1;
return response;
},
err => {
- window.activeVueResources -= 1;
+ window.pendingRequests -= 1;
return Promise.reject(err);
},
);
diff --git a/app/assets/javascripts/vue_shared/components/pagination/table_pagination.vue b/app/assets/javascripts/vue_shared/components/pagination/table_pagination.vue
index 1e2d4ffa7e3..e89638130f5 100644
--- a/app/assets/javascripts/vue_shared/components/pagination/table_pagination.vue
+++ b/app/assets/javascripts/vue_shared/components/pagination/table_pagination.vue
@@ -27,8 +27,7 @@ export default {
/**
pageInfo will come from the headers of the API call
- in the `.then` clause of the VueResource API call
- there should be a function that contructs the pageInfo for this component
+ there should be a function that constructs the pageInfo for this component
This is an example:
diff --git a/app/assets/javascripts/vue_shared/vue_resource_interceptor.js b/app/assets/javascripts/vue_shared/vue_resource_interceptor.js
deleted file mode 100644
index 754025207c8..00000000000
--- a/app/assets/javascripts/vue_shared/vue_resource_interceptor.js
+++ /dev/null
@@ -1,34 +0,0 @@
-import Vue from 'vue';
-import VueResource from 'vue-resource';
-import csrf from '../lib/utils/csrf';
-
-Vue.use(VueResource);
-
-// Maintain a global counter for active requests
-// see: spec/support/wait_for_requests.rb
-Vue.http.interceptors.push((request, next) => {
- window.activeVueResources = window.activeVueResources || 0;
- window.activeVueResources += 1;
-
- next(() => {
- window.activeVueResources -= 1;
- });
-});
-
-// Inject CSRF token and parse headers.
-// New Vue Resource version uses Headers, we are expecting a plain object to render pagination
-// and polling.
-Vue.http.interceptors.push((request, next) => {
- request.headers.set(csrf.headerKey, csrf.token);
-
- next(response => {
- // Headers object has a `forEach` property that iterates through all values.
- const headers = {};
-
- response.headers.forEach((value, key) => {
- headers[key] = value;
- });
- // eslint-disable-next-line no-param-reassign
- response.headers = headers;
- });
-});
diff --git a/app/assets/stylesheets/framework/contextual_sidebar.scss b/app/assets/stylesheets/framework/contextual_sidebar.scss
index 3238b01c6c0..05ae9b1d1ed 100644
--- a/app/assets/stylesheets/framework/contextual_sidebar.scss
+++ b/app/assets/stylesheets/framework/contextual_sidebar.scss
@@ -141,7 +141,7 @@
}
.sidebar-top-level-items > li > a {
- min-height: 44px;
+ min-height: 45px;
}
.fly-out-top-item {
diff --git a/app/controllers/projects/registry/tags_controller.rb b/app/controllers/projects/registry/tags_controller.rb
index 54e2faa2dd7..b5ee0ba9beb 100644
--- a/app/controllers/projects/registry/tags_controller.rb
+++ b/app/controllers/projects/registry/tags_controller.rb
@@ -19,14 +19,12 @@ module Projects
end
def destroy
- if tag.delete
- respond_to do |format|
- format.json { head :no_content }
- end
- else
- respond_to do |format|
- format.json { head :bad_request }
- end
+ result = Projects::ContainerRepository::DeleteTagsService
+ .new(image.project, current_user, tags: [params[:id]])
+ .execute(image)
+
+ respond_to do |format|
+ format.json { head(result[:status] == :success ? :ok : bad_request) }
end
end
@@ -42,21 +40,12 @@ module Projects
return
end
- @tags = tag_names.map { |tag_name| image.tag(tag_name) }
- unless @tags.all? { |tag| tag.valid_name? }
- head :bad_request
- return
- end
-
- success_count = 0
- @tags.each do |tag|
- if tag.delete
- success_count += 1
- end
- end
+ result = Projects::ContainerRepository::DeleteTagsService
+ .new(image.project, current_user, tags: tag_names)
+ .execute(image)
respond_to do |format|
- format.json { head(success_count == @tags.size ? :no_content : :bad_request) }
+ format.json { head(result[:status] == :success ? :no_content : :bad_request) }
end
end
@@ -70,10 +59,6 @@ module Projects
@image ||= project.container_repositories
.find(params[:repository_id])
end
-
- def tag
- @tag ||= image.tag(params[:id])
- end
end
end
end
diff --git a/app/services/projects/container_repository/cleanup_tags_service.rb b/app/services/projects/container_repository/cleanup_tags_service.rb
index d1d9b9f22e8..1b880a7aab1 100644
--- a/app/services/projects/container_repository/cleanup_tags_service.rb
+++ b/app/services/projects/container_repository/cleanup_tags_service.rb
@@ -40,7 +40,7 @@ module Projects
return unless tags.count == other_tags.count
# delete all tags
- tags.map(&:delete)
+ tags.map(&:unsafe_delete)
end
def group_by_digest(tags)
diff --git a/app/services/projects/container_repository/delete_tags_service.rb b/app/services/projects/container_repository/delete_tags_service.rb
new file mode 100644
index 00000000000..21dc1621e5c
--- /dev/null
+++ b/app/services/projects/container_repository/delete_tags_service.rb
@@ -0,0 +1,66 @@
+# frozen_string_literal: true
+
+module Projects
+ module ContainerRepository
+ class DeleteTagsService < BaseService
+ def execute(container_repository)
+ return error('access denied') unless can?(current_user, :destroy_container_image, project)
+
+ tag_names = params[:tags]
+ return error('not tags specified') if tag_names.blank?
+
+ if can_use?
+ smart_delete(container_repository, tag_names)
+ else
+ unsafe_delete(container_repository, tag_names)
+ end
+ end
+
+ private
+
+ def unsafe_delete(container_repository, tag_names)
+ deleted_tags = tag_names.select do |tag_name|
+ container_repository.tag(tag_name).unsafe_delete
+ end
+
+ return error('could not delete tags') if deleted_tags.empty?
+
+ success(deleted: deleted_tags)
+ end
+
+ # Replace a tag on the registry with a dummy tag.
+ # This is a hack as the registry doesn't support deleting individual
+ # tags. This code effectively pushes a dummy image and assigns the tag to it.
+ # This way when the tag is deleted only the dummy image is affected.
+ # See https://gitlab.com/gitlab-org/gitlab-ce/issues/21405 for a discussion
+ def smart_delete(container_repository, tag_names)
+ # generates the blobs for the dummy image
+ dummy_manifest = container_repository.client.generate_empty_manifest(container_repository.path)
+
+ # update the manifests of the tags with the new dummy image
+ tag_digests = tag_names.map do |name|
+ container_repository.client.put_tag(container_repository.path, name, dummy_manifest)
+ end
+
+ # make sure the digests are the same (it should always be)
+ tag_digests.uniq!
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ Gitlab::Sentry.track_exception(ArgumentError.new('multiple tag digests')) if tag_digests.many?
+
+ # deletes the dummy image
+ # all created tag digests are the same since they all have the same dummy image.
+ # a single delete is sufficient to remove all tags with it
+ if container_repository.client.delete_repository_tag(container_repository.path, tag_digests.first)
+ success(deleted: tag_names)
+ else
+ error('could not delete tags')
+ end
+ end
+
+ def can_use?
+ Feature.enabled?(:container_registry_smart_delete, project, default_enabled: true)
+ end
+ end
+ end
+end