diff options
Diffstat (limited to 'spec')
-rw-r--r-- | spec/controllers/projects/registry/tags_controller_spec.rb | 20 | ||||
-rw-r--r-- | spec/features/container_registry_spec.rb | 4 | ||||
-rw-r--r-- | spec/features/issues_spec.rb | 3 | ||||
-rw-r--r-- | spec/frontend/helpers/vue_resource_helper.js | 11 | ||||
-rw-r--r-- | spec/javascripts/helpers/vue_resource_helper.js | 11 | ||||
-rw-r--r-- | spec/javascripts/test_bundle.js | 9 | ||||
-rw-r--r-- | spec/lib/container_registry/client_spec.rb | 65 | ||||
-rw-r--r-- | spec/lib/container_registry/tag_spec.rb | 4 | ||||
-rw-r--r-- | spec/requests/api/project_container_repositories_spec.rb | 36 | ||||
-rw-r--r-- | spec/services/projects/container_repository/delete_tags_service_spec.rb | 120 | ||||
-rw-r--r-- | spec/support/helpers/wait_for_requests.rb | 6 |
11 files changed, 234 insertions, 55 deletions
diff --git a/spec/controllers/projects/registry/tags_controller_spec.rb b/spec/controllers/projects/registry/tags_controller_spec.rb index c6e063d8229..8da0d217e9e 100644 --- a/spec/controllers/projects/registry/tags_controller_spec.rb +++ b/spec/controllers/projects/registry/tags_controller_spec.rb @@ -10,6 +10,8 @@ describe Projects::Registry::TagsController do create(:container_repository, name: 'image', project: project) end + let(:service) { double('service') } + before do sign_in(user) stub_container_registry_config(enabled: true) @@ -84,17 +86,17 @@ describe Projects::Registry::TagsController do context 'when there is matching tag present' do before do - stub_container_registry_tags(repository: repository.path, tags: %w[rc1 test.]) + stub_container_registry_tags(repository: repository.path, tags: %w[rc1], with_manifest: true) end it 'makes it possible to delete regular tag' do - expect_any_instance_of(ContainerRegistry::Tag).to receive(:delete) + expect_delete_tags(%w[rc1]) destroy_tag('rc1') end it 'makes it possible to delete a tag that ends with a dot' do - expect_any_instance_of(ContainerRegistry::Tag).to receive(:delete) + expect_delete_tags(%w[test.]) destroy_tag('test.') end @@ -125,11 +127,12 @@ describe Projects::Registry::TagsController do stub_container_registry_tags(repository: repository.path, tags: %w[rc1 test.]) end + let(:tags) { %w[tc1 test.] } + it 'makes it possible to delete tags in bulk' do - allow_any_instance_of(ContainerRegistry::Tag).to receive(:delete) { |*args| ContainerRegistry::Tag.delete(*args) } - expect(ContainerRegistry::Tag).to receive(:delete).exactly(2).times + expect_delete_tags(tags) - bulk_destroy_tags(['rc1', 'test.']) + bulk_destroy_tags(tags) end end end @@ -146,4 +149,9 @@ describe Projects::Registry::TagsController do format: :json end end + + def expect_delete_tags(tags, status = :success) + expect(service).to receive(:execute).with(repository) { { status: status } } + expect(Projects::ContainerRepository::DeleteTagsService).to receive(:new).with(repository.project, user, tags: tags) { service } + end end diff --git a/spec/features/container_registry_spec.rb b/spec/features/container_registry_spec.rb index aefdc4d6d4f..dfd08483430 100644 --- a/spec/features/container_registry_spec.rb +++ b/spec/features/container_registry_spec.rb @@ -53,7 +53,9 @@ describe 'Container Registry', :js do find('.js-toggle-repo').click wait_for_requests - expect_any_instance_of(ContainerRegistry::Tag).to receive(:delete).and_return(true) + service = double('service') + expect(service).to receive(:execute).with(container_repository) { { status: :success } } + expect(Projects::ContainerRepository::DeleteTagsService).to receive(:new).with(container_repository.project, user, tags: ['latest']) { service } click_on(class: 'js-delete-registry-row', visible: false) expect(find('.modal .modal-title')).to have_content 'Remove image' diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb index 5bdd9113b06..f9e83af352d 100644 --- a/spec/features/issues_spec.rb +++ b/spec/features/issues_spec.rb @@ -470,9 +470,6 @@ describe 'Issues' do expect(page).to have_content 'None' end - # wait_for_requests does not work with vue-resource at the moment - sleep 1 - expect(issue.reload.assignees).to be_empty end diff --git a/spec/frontend/helpers/vue_resource_helper.js b/spec/frontend/helpers/vue_resource_helper.js deleted file mode 100644 index 0f58af09933..00000000000 --- a/spec/frontend/helpers/vue_resource_helper.js +++ /dev/null @@ -1,11 +0,0 @@ -// eslint-disable-next-line import/prefer-default-export -export const headersInterceptor = (request, next) => { - next(response => { - const headers = {}; - response.headers.forEach((value, key) => { - headers[key] = value; - }); - // eslint-disable-next-line no-param-reassign - response.headers = headers; - }); -}; diff --git a/spec/javascripts/helpers/vue_resource_helper.js b/spec/javascripts/helpers/vue_resource_helper.js deleted file mode 100644 index 0f58af09933..00000000000 --- a/spec/javascripts/helpers/vue_resource_helper.js +++ /dev/null @@ -1,11 +0,0 @@ -// eslint-disable-next-line import/prefer-default-export -export const headersInterceptor = (request, next) => { - next(response => { - const headers = {}; - response.headers.forEach((value, key) => { - headers[key] = value; - }); - // eslint-disable-next-line no-param-reassign - response.headers = headers; - }); -}; diff --git a/spec/javascripts/test_bundle.js b/spec/javascripts/test_bundle.js index c0a999cfaa6..191df3cc709 100644 --- a/spec/javascripts/test_bundle.js +++ b/spec/javascripts/test_bundle.js @@ -7,7 +7,6 @@ import 'core-js/features/set-immediate'; import 'vendor/jasmine-jquery'; import '~/commons'; import Vue from 'vue'; -import VueResource from 'vue-resource'; import Translate from '~/vue_shared/translate'; import jasmineDiff from 'jasmine-diff'; import { config as testUtilsConfig } from '@vue/test-utils'; @@ -46,7 +45,6 @@ Vue.config.errorHandler = function(err) { fail(err); }; -Vue.use(VueResource); Vue.use(Translate); // enable test fixtures @@ -102,13 +100,6 @@ afterEach(__rewire_reset_all__); // eslint-disable-line // to run our unit tests. beforeEach(done => done()); -const builtinVueHttpInterceptors = Vue.http.interceptors.slice(); - -beforeEach(() => { - // restore interceptors so we have no remaining ones from previous tests - Vue.http.interceptors = builtinVueHttpInterceptors.slice(); -}); - let longRunningTestTimeoutHandle; beforeEach(done => { diff --git a/spec/lib/container_registry/client_spec.rb b/spec/lib/container_registry/client_spec.rb index 6c2b338bfcd..3782c30e88a 100644 --- a/spec/lib/container_registry/client_spec.rb +++ b/spec/lib/container_registry/client_spec.rb @@ -73,4 +73,69 @@ describe ContainerRegistry::Client do expect(response).to eq('Successfully redirected') end end + + def stub_upload(path, content, digest, status = 200) + stub_request(:post, "http://container-registry/v2/#{path}/blobs/uploads/") + .to_return(status: status, body: "", headers: { 'location' => 'http://container-registry/next_upload?id=someid' }) + + stub_request(:put, "http://container-registry/next_upload?digest=#{digest}&id=someid") + .with(body: content) + .to_return(status: status, body: "", headers: {}) + end + + describe '#upload_blob' do + subject { client.upload_blob('path', 'content', 'sha256:123') } + + context 'with successful uploads' do + it 'starts the upload and posts the blob' do + stub_upload('path', 'content', 'sha256:123') + + expect(subject).to be_success + end + end + + context 'with a failed upload' do + before do + stub_upload('path', 'content', 'sha256:123', 400) + end + + it 'returns nil' do + expect(subject).to be nil + end + end + end + + describe '#generate_empty_manifest' do + subject { client.generate_empty_manifest('path') } + + let(:result_manifest) do + { + schemaVersion: 2, + mediaType: 'application/vnd.docker.distribution.manifest.v2+json', + config: { + mediaType: 'application/vnd.docker.container.image.v1+json', + size: 21, + digest: 'sha256:4435000728ee66e6a80e55637fc22725c256b61de344a2ecdeaac6bdb36e8bc3' + } + } + end + + it 'uploads a random image and returns the manifest' do + stub_upload('path', "{\n \"config\": {\n }\n}", 'sha256:4435000728ee66e6a80e55637fc22725c256b61de344a2ecdeaac6bdb36e8bc3') + + expect(subject).to eq(result_manifest) + end + end + + describe '#put_tag' do + subject { client.put_tag('path', 'tagA', { foo: :bar }) } + + it 'uploads the manifest and returns the digest' do + stub_request(:put, "http://container-registry/v2/path/manifests/tagA") + .with(body: "{\n \"foo\": \"bar\"\n}") + .to_return(status: 200, body: "", headers: { 'docker-content-digest' => 'sha256:123' }) + + expect(subject).to eq 'sha256:123' + end + end end diff --git a/spec/lib/container_registry/tag_spec.rb b/spec/lib/container_registry/tag_spec.rb index 110f006536b..3115dfe852f 100644 --- a/spec/lib/container_registry/tag_spec.rb +++ b/spec/lib/container_registry/tag_spec.rb @@ -179,7 +179,7 @@ describe ContainerRegistry::Tag do end end - describe '#delete' do + describe '#unsafe_delete' do before do stub_request(:delete, 'http://registry.gitlab/v2/group/test/manifests/sha256:digest') .with(headers: headers) @@ -187,7 +187,7 @@ describe ContainerRegistry::Tag do end it 'correctly deletes the tag' do - expect(tag.delete).to be_truthy + expect(tag.unsafe_delete).to be_truthy end end end diff --git a/spec/requests/api/project_container_repositories_spec.rb b/spec/requests/api/project_container_repositories_spec.rb index f1dc4e6f0b2..3ac7ff7656b 100644 --- a/spec/requests/api/project_container_repositories_spec.rb +++ b/spec/requests/api/project_container_repositories_spec.rb @@ -150,7 +150,7 @@ describe API::ProjectContainerRepositories do expect(response).to have_gitlab_http_status(:accepted) end - context 'called multiple times in one hour' do + context 'called multiple times in one hour', :clean_gitlab_redis_shared_state do it 'returns 400 with an error message' do stub_exclusive_lease_taken(lease_key, timeout: 1.hour) subject @@ -202,6 +202,8 @@ describe API::ProjectContainerRepositories do end describe 'DELETE /projects/:id/registry/repositories/:repository_id/tags/:tag_name' do + let(:service) { double('service') } + subject { delete api("/projects/#{project.id}/registry/repositories/#{root_repository.id}/tags/rootA", api_user) } it_behaves_like 'rejected container repository access', :reporter, :forbidden @@ -210,18 +212,34 @@ describe API::ProjectContainerRepositories do context 'for developer' do let(:api_user) { developer } - before do - stub_container_registry_tags(repository: root_repository.path, tags: %w(rootA), with_manifest: true) + context 'when there are multiple tags' do + before do + stub_container_registry_tags(repository: root_repository.path, tags: %w(rootA rootB), with_manifest: true) + end + + it 'properly removes tag' do + expect(service).to receive(:execute).with(root_repository) { { status: :success } } + expect(Projects::ContainerRepository::DeleteTagsService).to receive(:new).with(root_repository.project, api_user, tags: %w[rootA]) { service } + + subject + + expect(response).to have_gitlab_http_status(:ok) + end end - it 'properly removes tag' do - expect_any_instance_of(ContainerRegistry::Client) - .to receive(:delete_repository_tag).with(root_repository.path, - 'sha256:4c8e63ca4cb663ce6c688cb06f1c372b088dac5b6d7ad7d49cd620d85cf72a15') + context 'when there\'s only one tag' do + before do + stub_container_registry_tags(repository: root_repository.path, tags: %w(rootA), with_manifest: true) + end - subject + it 'properly removes tag' do + expect(service).to receive(:execute).with(root_repository) { { status: :success } } + expect(Projects::ContainerRepository::DeleteTagsService).to receive(:new).with(root_repository.project, api_user, tags: %w[rootA]) { service } - expect(response).to have_gitlab_http_status(:ok) + subject + + expect(response).to have_gitlab_http_status(:ok) + end end end end diff --git a/spec/services/projects/container_repository/delete_tags_service_spec.rb b/spec/services/projects/container_repository/delete_tags_service_spec.rb new file mode 100644 index 00000000000..2ec5850c69e --- /dev/null +++ b/spec/services/projects/container_repository/delete_tags_service_spec.rb @@ -0,0 +1,120 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Projects::ContainerRepository::DeleteTagsService do + set(:user) { create(:user) } + set(:project) { create(:project, :private) } + set(:repository) { create(:container_repository, :root, project: project) } + + let(:params) { { tags: tags } } + let(:service) { described_class.new(project, user, params) } + + before do + stub_container_registry_config(enabled: true, + api_url: 'http://registry.gitlab', + host_port: 'registry.gitlab') + + stub_container_registry_tags( + repository: repository.path, + tags: %w(latest A Ba Bb C D E)) + + stub_tag_digest('latest', 'sha256:configA') + stub_tag_digest('A', 'sha256:configA') + stub_tag_digest('Ba', 'sha256:configB') + end + + describe '#execute' do + let(:tags) { %w[A] } + subject { service.execute(repository) } + + context 'without permissions' do + it { is_expected.to include(status: :error) } + end + + context 'with permissions' do + before do + project.add_developer(user) + end + + context 'when no params are specified' do + let(:params) { {} } + + it 'does not remove anything' do + expect_any_instance_of(ContainerRegistry::Client).not_to receive(:delete_repository_tag) + + is_expected.to include(status: :error) + end + end + + context 'with empty tags' do + let(:tags) { [] } + + it 'does not remove anything' do + expect_any_instance_of(ContainerRegistry::Client).not_to receive(:delete_repository_tag) + + is_expected.to include(status: :error) + end + end + + context 'with dummy tags disabled' do + let(:tags) { %w[A Ba] } + + before do + stub_feature_flags(container_registry_smart_delete: false) + end + + it 'deletes tags one by one' do + expect_delete_tag('sha256:configA') + expect_delete_tag('sha256:configB') + is_expected.to include(status: :success) + end + end + + context 'with dummy tags enabled' do + let(:tags) { %w[A Ba] } + + it 'deletes the tags using a dummy image' do + stub_upload("{\n \"config\": {\n }\n}", 'sha256:4435000728ee66e6a80e55637fc22725c256b61de344a2ecdeaac6bdb36e8bc3') + + stub_request(:put, "http://registry.gitlab/v2/#{repository.path}/manifests/A") + .to_return(status: 200, body: "", headers: { 'docker-content-digest' => 'sha256:dummy' }) + + stub_request(:put, "http://registry.gitlab/v2/#{repository.path}/manifests/Ba") + .to_return(status: 200, body: "", headers: { 'docker-content-digest' => 'sha256:dummy' }) + + expect_delete_tag('sha256:dummy') + + is_expected.to include(status: :success) + end + end + end + end + + private + + def stub_tag_digest(tag, digest) + stub_request(:head, "http://registry.gitlab/v2/#{repository.path}/manifests/#{tag}") + .to_return(status: 200, body: "", headers: { 'docker-content-digest' => digest }) + end + + def stub_digest_config(digest, created_at) + allow_any_instance_of(ContainerRegistry::Client) + .to receive(:blob) + .with(repository.path, digest, nil) do + { 'created' => created_at.to_datetime.rfc3339 }.to_json if created_at + end + end + + def stub_upload(content, digest) + expect_any_instance_of(ContainerRegistry::Client) + .to receive(:upload_blob) + .with(repository.path, content, digest) { double(success?: true ) } + end + + def expect_delete_tag(digest) + expect_any_instance_of(ContainerRegistry::Client) + .to receive(:delete_repository_tag) + .with(repository.path, digest) { true } + end +end diff --git a/spec/support/helpers/wait_for_requests.rb b/spec/support/helpers/wait_for_requests.rb index 30dff1063b5..d5483d0b0a7 100644 --- a/spec/support/helpers/wait_for_requests.rb +++ b/spec/support/helpers/wait_for_requests.rb @@ -49,11 +49,11 @@ module WaitForRequests return true unless javascript_test? finished_all_ajax_requests? && - finished_all_vue_resource_requests? + finished_all_axios_requests? end - def finished_all_vue_resource_requests? - Capybara.page.evaluate_script('window.activeVueResources || 0').zero? + def finished_all_axios_requests? + Capybara.page.evaluate_script('window.pendingRequests || 0').zero? end def finished_all_ajax_requests? |