diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2019-09-19 14:50:12 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2019-09-19 14:50:12 +0300 |
commit | 6cd5b7dbfaa4ff630ecbbfe351a1faac5fc71a8d (patch) | |
tree | d3563b9f60936c18a02185e2e53b424bb1b83539 /app | |
parent | b3e0658cb1fbc7c8e7dd381467c656f2e675ee46 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
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 |