From 6438df3a1e0fb944485cebf07976160184697d72 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 20 Jan 2021 13:34:23 -0600 Subject: Add latest changes from gitlab-org/gitlab@13-8-stable-ee --- spec/tooling/lib/tooling/kubernetes_client_spec.rb | 176 +++++++++++++++------ spec/tooling/lib/tooling/test_file_finder_spec.rb | 175 -------------------- 2 files changed, 129 insertions(+), 222 deletions(-) delete mode 100644 spec/tooling/lib/tooling/test_file_finder_spec.rb (limited to 'spec/tooling') diff --git a/spec/tooling/lib/tooling/kubernetes_client_spec.rb b/spec/tooling/lib/tooling/kubernetes_client_spec.rb index fdd56aa0189..2511295206c 100644 --- a/spec/tooling/lib/tooling/kubernetes_client_spec.rb +++ b/spec/tooling/lib/tooling/kubernetes_client_spec.rb @@ -17,84 +17,111 @@ RSpec.describe Tooling::KubernetesClient do end end - describe '#cleanup' do + describe '#cleanup_by_release' do before do allow(subject).to receive(:raw_resource_names).and_return(raw_resource_names) end + shared_examples 'a kubectl command to delete resources' do + let(:wait) { true } + let(:release_names_in_command) { release_name.respond_to?(:join) ? %(-l 'release in (#{release_name.join(', ')})') : %(-l release="#{release_name}") } + + specify do + expect(Gitlab::Popen).to receive(:popen_with_detail) + .with(["kubectl delete #{described_class::RESOURCE_LIST} " + + %(--namespace "#{namespace}" --now --ignore-not-found --include-uninitialized --wait=#{wait} #{release_names_in_command})]) + .and_return(Gitlab::Popen::Result.new([], '', '', double(success?: true))) + + expect(Gitlab::Popen).to receive(:popen_with_detail) + .with([%(kubectl delete --namespace "#{namespace}" --ignore-not-found #{pod_for_release})]) + .and_return(Gitlab::Popen::Result.new([], '', '', double(success?: true))) + + # We're not verifying the output here, just silencing it + expect { subject.cleanup_by_release(release_name: release_name) }.to output.to_stdout + end + end + it 'raises an error if the Kubernetes command fails' do expect(Gitlab::Popen).to receive(:popen_with_detail) .with(["kubectl delete #{described_class::RESOURCE_LIST} " + %(--namespace "#{namespace}" --now --ignore-not-found --include-uninitialized --wait=true -l release="#{release_name}")]) .and_return(Gitlab::Popen::Result.new([], '', '', double(success?: false))) - expect { subject.cleanup(release_name: release_name) }.to raise_error(described_class::CommandFailedError) + expect { subject.cleanup_by_release(release_name: release_name) }.to raise_error(described_class::CommandFailedError) end - it 'calls kubectl with the correct arguments' do - expect(Gitlab::Popen).to receive(:popen_with_detail) - .with(["kubectl delete #{described_class::RESOURCE_LIST} " + - %(--namespace "#{namespace}" --now --ignore-not-found --include-uninitialized --wait=true -l release="#{release_name}")]) - .and_return(Gitlab::Popen::Result.new([], '', '', double(success?: true))) + it_behaves_like 'a kubectl command to delete resources' - expect(Gitlab::Popen).to receive(:popen_with_detail) - .with([%(kubectl delete --namespace "#{namespace}" --ignore-not-found #{pod_for_release})]) - .and_return(Gitlab::Popen::Result.new([], '', '', double(success?: true))) + context 'with multiple releases' do + let(:release_name) { %w[my-release my-release-2] } - # We're not verifying the output here, just silencing it - expect { subject.cleanup(release_name: release_name) }.to output.to_stdout + it_behaves_like 'a kubectl command to delete resources' end - context 'with multiple releases' do - let(:release_name) { %w[my-release my-release-2] } + context 'with `wait: false`' do + let(:wait) { false } - it 'raises an error if the Kubernetes command fails' do - expect(Gitlab::Popen).to receive(:popen_with_detail) - .with(["kubectl delete #{described_class::RESOURCE_LIST} " + - %(--namespace "#{namespace}" --now --ignore-not-found --include-uninitialized --wait=true -l 'release in (#{release_name.join(', ')})')]) - .and_return(Gitlab::Popen::Result.new([], '', '', double(success?: false))) + it_behaves_like 'a kubectl command to delete resources' + end + end - expect { subject.cleanup(release_name: release_name) }.to raise_error(described_class::CommandFailedError) - end + describe '#cleanup_by_created_at' do + let(:two_days_ago) { Time.now - 3600 * 24 * 2 } + let(:resource_type) { 'pvc' } + let(:resource_names) { [pod_for_release] } - it 'calls kubectl with the correct arguments' do - expect(Gitlab::Popen).to receive(:popen_with_detail) - .with(["kubectl delete #{described_class::RESOURCE_LIST} " + - %(--namespace "#{namespace}" --now --ignore-not-found --include-uninitialized --wait=true -l 'release in (#{release_name.join(', ')})')]) - .and_return(Gitlab::Popen::Result.new([], '', '', double(success?: true))) + before do + allow(subject).to receive(:resource_names_created_before).with(resource_type: resource_type, created_before: two_days_ago).and_return(resource_names) + end + + shared_examples 'a kubectl command to delete resources by older than given creation time' do + let(:wait) { true } + let(:release_names_in_command) { resource_names.join(' ') } + specify do expect(Gitlab::Popen).to receive(:popen_with_detail) - .with([%(kubectl delete --namespace "#{namespace}" --ignore-not-found #{pod_for_release})]) - .and_return(Gitlab::Popen::Result.new([], '', '', double(success?: true))) + .with(["kubectl delete #{resource_type} ".squeeze(' ') + + %(--namespace "#{namespace}" --now --ignore-not-found --include-uninitialized --wait=#{wait} #{release_names_in_command})]) + .and_return(Gitlab::Popen::Result.new([], '', '', double(success?: true))) # We're not verifying the output here, just silencing it - expect { subject.cleanup(release_name: release_name) }.to output.to_stdout + expect { subject.cleanup_by_created_at(resource_type: resource_type, created_before: two_days_ago) }.to output.to_stdout end end + it 'raises an error if the Kubernetes command fails' do + expect(Gitlab::Popen).to receive(:popen_with_detail) + .with(["kubectl delete #{resource_type} " + + %(--namespace "#{namespace}" --now --ignore-not-found --include-uninitialized --wait=true #{pod_for_release})]) + .and_return(Gitlab::Popen::Result.new([], '', '', double(success?: false))) + + expect { subject.cleanup_by_created_at(resource_type: resource_type, created_before: two_days_ago) }.to raise_error(described_class::CommandFailedError) + end + + it_behaves_like 'a kubectl command to delete resources by older than given creation time' + + context 'with multiple resource names' do + let(:resource_names) { %w[pod-1 pod-2] } + + it_behaves_like 'a kubectl command to delete resources by older than given creation time' + end + context 'with `wait: false`' do - it 'raises an error if the Kubernetes command fails' do - expect(Gitlab::Popen).to receive(:popen_with_detail) - .with(["kubectl delete #{described_class::RESOURCE_LIST} " + - %(--namespace "#{namespace}" --now --ignore-not-found --include-uninitialized --wait=false -l release="#{release_name}")]) - .and_return(Gitlab::Popen::Result.new([], '', '', double(success?: false))) + let(:wait) { false } - expect { subject.cleanup(release_name: release_name, wait: false) }.to raise_error(described_class::CommandFailedError) - end + it_behaves_like 'a kubectl command to delete resources by older than given creation time' + end - it 'calls kubectl with the correct arguments' do - expect(Gitlab::Popen).to receive(:popen_with_detail) - .with(["kubectl delete #{described_class::RESOURCE_LIST} " + - %(--namespace "#{namespace}" --now --ignore-not-found --include-uninitialized --wait=false -l release="#{release_name}")]) - .and_return(Gitlab::Popen::Result.new([], '', '', double(success?: true))) + context 'with no resource_type given' do + let(:resource_type) { nil } - expect(Gitlab::Popen).to receive(:popen_with_detail) - .with([%(kubectl delete --namespace "#{namespace}" --ignore-not-found #{pod_for_release})]) - .and_return(Gitlab::Popen::Result.new([], '', '', double(success?: true))) + it_behaves_like 'a kubectl command to delete resources by older than given creation time' + end - # We're not verifying the output here, just silencing it - expect { subject.cleanup(release_name: release_name, wait: false) }.to output.to_stdout - end + context 'with multiple resource_type given' do + let(:resource_type) { 'pvc,service' } + + it_behaves_like 'a kubectl command to delete resources by older than given creation time' end end @@ -108,4 +135,59 @@ RSpec.describe Tooling::KubernetesClient do expect(subject.__send__(:raw_resource_names)).to eq(raw_resource_names) end end + + describe '#resource_names_created_before' do + let(:three_days_ago) { Time.now - 3600 * 24 * 3 } + let(:two_days_ago) { Time.now - 3600 * 24 * 2 } + let(:pvc_created_three_days_ago) { 'pvc-created-three-days-ago' } + let(:resource_type) { 'pvc' } + let(:raw_resources) do + { + items: [ + { + apiVersion: "v1", + kind: "PersistentVolumeClaim", + metadata: { + creationTimestamp: three_days_ago, + name: pvc_created_three_days_ago + } + }, + { + apiVersion: "v1", + kind: "PersistentVolumeClaim", + metadata: { + creationTimestamp: Time.now, + name: 'another-pvc' + } + } + ] + }.to_json + end + + shared_examples 'a kubectl command to retrieve resource names sorted by creationTimestamp' do + specify do + expect(Gitlab::Popen).to receive(:popen_with_detail) + .with(["kubectl get #{resource_type} ".squeeze(' ') + + %(--namespace "#{namespace}" ) + + "--sort-by='{.metadata.creationTimestamp}' -o json"]) + .and_return(Gitlab::Popen::Result.new([], raw_resources, '', double(success?: true))) + + expect(subject.__send__(:resource_names_created_before, resource_type: resource_type, created_before: two_days_ago)).to contain_exactly(pvc_created_three_days_ago) + end + end + + it_behaves_like 'a kubectl command to retrieve resource names sorted by creationTimestamp' + + context 'with no resource_type given' do + let(:resource_type) { nil } + + it_behaves_like 'a kubectl command to retrieve resource names sorted by creationTimestamp' + end + + context 'with multiple resource_type given' do + let(:resource_type) { 'pvc,service' } + + it_behaves_like 'a kubectl command to retrieve resource names sorted by creationTimestamp' + end + end end diff --git a/spec/tooling/lib/tooling/test_file_finder_spec.rb b/spec/tooling/lib/tooling/test_file_finder_spec.rb deleted file mode 100644 index 683bc647b8a..00000000000 --- a/spec/tooling/lib/tooling/test_file_finder_spec.rb +++ /dev/null @@ -1,175 +0,0 @@ -# frozen_string_literal: true - -require_relative '../../../../tooling/lib/tooling/test_file_finder' - -RSpec.describe Tooling::TestFileFinder do - subject { described_class.new(file) } - - describe '#test_files' do - context 'when given non .rb files' do - let(:file) { 'app/assets/images/emoji.png' } - - it 'does not return a test file' do - expect(subject.test_files).to be_empty - end - end - - context 'when given file in app/' do - let(:file) { 'app/finders/admin/projects_finder.rb' } - - it 'returns the matching app spec file' do - expect(subject.test_files).to contain_exactly('spec/finders/admin/projects_finder_spec.rb') - end - end - - context 'when given file in lib/' do - let(:file) { 'lib/banzai/color_parser.rb' } - - it 'returns the matching app spec file' do - expect(subject.test_files).to contain_exactly('spec/lib/banzai/color_parser_spec.rb') - end - end - - context 'when given a file in tooling/' do - let(:file) { 'tooling/lib/tooling/test_file_finder.rb' } - - it 'returns the matching tooling test' do - expect(subject.test_files).to contain_exactly('spec/tooling/lib/tooling/test_file_finder_spec.rb') - end - end - - context 'when given a test file' do - let(:file) { 'spec/lib/banzai/color_parser_spec.rb' } - - it 'returns the matching test file itself' do - expect(subject.test_files).to contain_exactly('spec/lib/banzai/color_parser_spec.rb') - end - end - - context 'when given an app file in ee/' do - let(:file) { 'ee/app/models/analytics/cycle_analytics/group_level.rb' } - - it 'returns the matching ee/ test file' do - expect(subject.test_files).to contain_exactly('ee/spec/models/analytics/cycle_analytics/group_level_spec.rb') - end - end - - context 'when given an ee extension module file' do - let(:file) { 'ee/app/models/ee/user.rb' } - - it 'returns the matching ee/ class test file, ee extension module test file and the foss class test file' do - test_files = ['ee/spec/models/user_spec.rb', 'ee/spec/models/ee/user_spec.rb', 'spec/app/models/user_spec.rb'] - expect(subject.test_files).to contain_exactly(*test_files) - end - end - - context 'when given a test file in ee/' do - let(:file) { 'ee/spec/models/container_registry/event_spec.rb' } - - it 'returns the test file itself' do - expect(subject.test_files).to contain_exactly('ee/spec/models/container_registry/event_spec.rb') - end - end - - context 'when given a module test file in ee/' do - let(:file) { 'ee/spec/models/ee/appearance_spec.rb' } - - it 'returns the matching module test file itself and the corresponding spec model test file' do - test_files = ['ee/spec/models/ee/appearance_spec.rb', 'spec/models/appearance_spec.rb'] - expect(subject.test_files).to contain_exactly(*test_files) - end - end - - context 'when given a factory file' do - let(:file) { 'spec/factories/users.rb' } - - it 'returns spec/factories_spec.rb file' do - expect(subject.test_files).to contain_exactly('spec/factories_spec.rb') - end - end - - context 'when given an ee factory file' do - let(:file) { 'ee/spec/factories/users.rb' } - - it 'returns spec/factories_spec.rb file' do - expect(subject.test_files).to contain_exactly('spec/factories_spec.rb') - end - end - - context 'when given db/structure.sql' do - let(:file) { 'db/structure.sql' } - - it 'returns spec/db/schema_spec.rb' do - expect(subject.test_files).to contain_exactly('spec/db/schema_spec.rb') - end - end - - context 'when given an initializer' do - let(:file) { 'config/initializers/action_mailer_hooks.rb' } - - it 'returns the matching initializer spec' do - expect(subject.test_files).to contain_exactly('spec/initializers/action_mailer_hooks_spec.rb') - end - end - - context 'when given a haml view' do - let(:file) { 'app/views/admin/users/_user.html.haml' } - - it 'returns the matching view spec' do - expect(subject.test_files).to contain_exactly('spec/views/admin/users/_user.html.haml_spec.rb') - end - end - - context 'when given a haml view in ee/' do - let(:file) { 'ee/app/views/admin/users/_user.html.haml' } - - it 'returns the matching view spec' do - expect(subject.test_files).to contain_exactly('ee/spec/views/admin/users/_user.html.haml_spec.rb') - end - end - - context 'when given a migration file' do - let(:file) { 'db/migrate/20191023152913_add_default_and_free_plans.rb' } - - it 'returns the matching migration spec' do - test_files = %w[ - spec/migrations/add_default_and_free_plans_spec.rb - spec/migrations/20191023152913_add_default_and_free_plans_spec.rb - ] - expect(subject.test_files).to contain_exactly(*test_files) - end - end - - context 'when given a post-migration file' do - let(:file) { 'db/post_migrate/20200608072931_backfill_imported_snippet_repositories.rb' } - - it 'returns the matching migration spec' do - test_files = %w[ - spec/migrations/backfill_imported_snippet_repositories_spec.rb - spec/migrations/20200608072931_backfill_imported_snippet_repositories_spec.rb - ] - expect(subject.test_files).to contain_exactly(*test_files) - end - end - - context 'with foss_test_only: true' do - subject { Tooling::TestFileFinder.new(file, foss_test_only: true) } - - context 'when given a module file in ee/' do - let(:file) { 'ee/app/models/ee/user.rb' } - - it 'returns only the corresponding spec model test file in foss' do - expect(subject.test_files).to contain_exactly('spec/app/models/user_spec.rb') - end - end - - context 'when given an app file in ee/' do - let(:file) { 'ee/app/models/approval.rb' } - - it 'returns no test file in foss' do - expect(subject.test_files).to be_empty - end - end - end - end -end -- cgit v1.2.3