diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-07-05 10:20:03 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-07-05 10:20:03 +0000 |
commit | d2612b42b9da6638d70b9d7144f6d427070d042d (patch) | |
tree | ed7de87d4b112cae8a45ba186d717ca9768c7d4e /spec | |
parent | d80373b353005e70f44eca8a3bc4a4c5cfbf0e9e (diff) | |
download | gitlab-ce-d2612b42b9da6638d70b9d7144f6d427070d042d.tar.gz |
Add latest changes from gitlab-org/gitlab@15-1-stable-ee
Diffstat (limited to 'spec')
-rw-r--r-- | spec/config/object_store_settings_spec.rb | 76 | ||||
-rw-r--r-- | spec/features/projects/new_project_spec.rb | 14 | ||||
-rw-r--r-- | spec/frontend/clusters/agents/components/create_token_button_spec.js | 255 | ||||
-rw-r--r-- | spec/frontend/clusters/agents/components/create_token_modal_spec.js | 223 | ||||
-rw-r--r-- | spec/frontend/clusters/agents/components/token_table_spec.js | 6 | ||||
-rw-r--r-- | spec/lib/gitlab/kubernetes/kube_client_spec.rb | 98 | ||||
-rw-r--r-- | spec/support/helpers/kubernetes_helpers.rb | 37 | ||||
-rw-r--r-- | spec/uploaders/object_storage_spec.rb | 44 |
8 files changed, 413 insertions, 340 deletions
diff --git a/spec/config/object_store_settings_spec.rb b/spec/config/object_store_settings_spec.rb index 56ad0943377..1555124fe03 100644 --- a/spec/config/object_store_settings_spec.rb +++ b/spec/config/object_store_settings_spec.rb @@ -73,6 +73,7 @@ RSpec.describe ObjectStoreSettings do expect(settings.artifacts['object_store']['background_upload']).to be false expect(settings.artifacts['object_store']['proxy_download']).to be false expect(settings.artifacts['object_store']['remote_directory']).to eq('artifacts') + expect(settings.artifacts['object_store']['bucket_prefix']).to eq(nil) expect(settings.artifacts['object_store']['consolidated_settings']).to be true expect(settings.artifacts).to eq(settings['artifacts']) @@ -83,6 +84,7 @@ RSpec.describe ObjectStoreSettings do expect(settings.lfs['object_store']['background_upload']).to be false expect(settings.lfs['object_store']['proxy_download']).to be true expect(settings.lfs['object_store']['remote_directory']).to eq('lfs-objects') + expect(settings.lfs['object_store']['bucket_prefix']).to eq(nil) expect(settings.lfs['object_store']['consolidated_settings']).to be true expect(settings.lfs).to eq(settings['lfs']) @@ -90,6 +92,7 @@ RSpec.describe ObjectStoreSettings do expect(settings.pages['object_store']['enabled']).to be true expect(settings.pages['object_store']['connection']).to eq(connection) expect(settings.pages['object_store']['remote_directory']).to eq('pages') + expect(settings.pages['object_store']['bucket_prefix']).to eq(nil) expect(settings.pages['object_store']['consolidated_settings']).to be true expect(settings.pages).to eq(settings['pages']) @@ -98,6 +101,18 @@ RSpec.describe ObjectStoreSettings do expect(settings.external_diffs).to eq(settings['external_diffs']) end + it 'supports bucket prefixes' do + config['object_store']['objects']['artifacts']['bucket'] = 'gitlab/artifacts' + config['object_store']['objects']['lfs']['bucket'] = 'gitlab/lfs' + + subject + + expect(settings.artifacts['object_store']['remote_directory']).to eq('gitlab') + expect(settings.artifacts['object_store']['bucket_prefix']).to eq('artifacts') + expect(settings.lfs['object_store']['remote_directory']).to eq('gitlab') + expect(settings.lfs['object_store']['bucket_prefix']).to eq('lfs') + end + it 'raises an error when a bucket is missing' do config['object_store']['objects']['lfs'].delete('bucket') @@ -152,6 +167,7 @@ RSpec.describe ObjectStoreSettings do expect(settings.artifacts['enabled']).to be true expect(settings.artifacts['object_store']['remote_directory']).to be_nil + expect(settings.artifacts['object_store']['bucket_prefix']).to be_nil expect(settings.artifacts['object_store']['enabled']).to be_falsey expect(settings.artifacts['object_store']['consolidated_settings']).to be_falsey end @@ -177,6 +193,7 @@ RSpec.describe ObjectStoreSettings do expect(settings.artifacts['object_store']).to be_nil expect(settings.lfs['object_store']['remote_directory']).to eq('some-bucket') + expect(settings.lfs['object_store']['bucket_prefix']).to eq(nil) # Disable background_upload, regardless of the input config expect(settings.lfs['object_store']['direct_upload']).to eq(true) expect(settings.lfs['object_store']['background_upload']).to eq(false) @@ -203,6 +220,7 @@ RSpec.describe ObjectStoreSettings do expect(settings.artifacts['object_store']).to be_nil expect(settings.lfs['object_store']['remote_directory']).to eq('some-bucket') + expect(settings.lfs['object_store']['bucket_prefix']).to eq(nil) # Enable background_upload if the environment variable is available expect(settings.lfs['object_store']['direct_upload']).to eq(false) expect(settings.lfs['object_store']['background_upload']).to eq(true) @@ -220,6 +238,7 @@ RSpec.describe ObjectStoreSettings do expect(settings['direct_upload']).to be true expect(settings['background_upload']).to be false expect(settings['remote_directory']).to be nil + expect(settings['bucket_prefix']).to be nil end it 'respects original values' do @@ -234,6 +253,18 @@ RSpec.describe ObjectStoreSettings do expect(settings['direct_upload']).to be true expect(settings['background_upload']).to be false expect(settings['remote_directory']).to eq 'artifacts' + expect(settings['bucket_prefix']).to be nil + end + + it 'supports bucket prefixes' do + original_settings = Settingslogic.new({ + 'enabled' => true, + 'remote_directory' => 'gitlab/artifacts' + }) + + settings = described_class.legacy_parse(original_settings, 'artifacts') + expect(settings['remote_directory']).to eq 'gitlab' + expect(settings['bucket_prefix']).to eq 'artifacts' end context 'legacy background upload environment variable is enabled' do @@ -253,6 +284,7 @@ RSpec.describe ObjectStoreSettings do expect(settings['direct_upload']).to be false expect(settings['background_upload']).to be true expect(settings['remote_directory']).to eq 'artifacts' + expect(settings['bucket_prefix']).to eq nil end end @@ -273,6 +305,50 @@ RSpec.describe ObjectStoreSettings do expect(settings['direct_upload']).to be true expect(settings['background_upload']).to be false expect(settings['remote_directory']).to eq 'artifacts' + expect(settings['bucket_prefix']).to eq nil + end + end + end + + describe '.split_bucket_prefix' do + using RSpec::Parameterized::TableSyntax + + subject { described_class.split_bucket_prefix(input) } + + context 'valid inputs' do + where(:input, :bucket, :prefix) do + nil | nil | nil + '' | nil | nil + 'bucket' | 'bucket' | nil + 'bucket/prefix' | 'bucket' | 'prefix' + 'bucket/pre/fix' | 'bucket' | 'pre/fix' + end + + with_them do + it { expect(subject).to eq([bucket, prefix]) } + end + end + + context 'invalid inputs' do + where(:input) do + [ + ['bucket/'], + ['bucket/.'], + ['bucket/..'], + ['bucket/prefix/'], + ['bucket/prefix/.'], + ['bucket/prefix/..'], + ['/bucket/prefix'], + ['./bucket/prefix'], + ['../bucket/prefix'], + ['bucket//prefix'], + ['bucket/./prefix'], + ['bucket/../prefix'] + ] + end + + with_them do + it { expect { subject }.to raise_error(/invalid bucket/) } end end end diff --git a/spec/features/projects/new_project_spec.rb b/spec/features/projects/new_project_spec.rb index c323e60bb71..a1e92a79516 100644 --- a/spec/features/projects/new_project_spec.rb +++ b/spec/features/projects/new_project_spec.rb @@ -298,6 +298,20 @@ RSpec.describe 'New project', :js do end end + context 'Import project options without any sources', :js do + before do + stub_application_setting(import_sources: []) + + visit new_project_path + click_link 'Import project' + end + + it 'displays the no import options message' do + expect(page).to have_text s_('ProjectsNew|No import options available') + expect(page).to have_text s_('ProjectsNew|Contact an administrator to enable options for importing your project.') + end + end + context 'Import project options', :js do before do visit new_project_path diff --git a/spec/frontend/clusters/agents/components/create_token_button_spec.js b/spec/frontend/clusters/agents/components/create_token_button_spec.js index fb1a3aa2963..73856b74a8d 100644 --- a/spec/frontend/clusters/agents/components/create_token_button_spec.js +++ b/spec/frontend/clusters/agents/components/create_token_button_spec.js @@ -1,262 +1,71 @@ -import { GlButton, GlTooltip, GlModal, GlFormInput, GlFormTextarea, GlAlert } from '@gitlab/ui'; -import Vue from 'vue'; -import VueApollo from 'vue-apollo'; -import createMockApollo from 'helpers/mock_apollo_helper'; -import waitForPromises from 'helpers/wait_for_promises'; +import { GlButton, GlTooltip } from '@gitlab/ui'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; -import { mockTracking } from 'helpers/tracking_helper'; -import { - EVENT_LABEL_MODAL, - EVENT_ACTIONS_OPEN, - TOKEN_NAME_LIMIT, - TOKEN_STATUS_ACTIVE, - MAX_LIST_COUNT, - CREATE_TOKEN_MODAL, -} from '~/clusters/agents/constants'; -import createNewAgentToken from '~/clusters/agents/graphql/mutations/create_new_agent_token.mutation.graphql'; -import getClusterAgentQuery from '~/clusters/agents/graphql/queries/get_cluster_agent.query.graphql'; -import AgentToken from '~/clusters_list/components/agent_token.vue'; +import { createMockDirective, getBinding } from 'helpers/vue_mock_directive'; import CreateTokenButton from '~/clusters/agents/components/create_token_button.vue'; -import { - clusterAgentToken, - getTokenResponse, - createAgentTokenErrorResponse, -} from '../../mock_data'; - -Vue.use(VueApollo); +import { CREATE_TOKEN_MODAL } from '~/clusters/agents/constants'; describe('CreateTokenButton', () => { let wrapper; - let apolloProvider; - let trackingSpy; - let createResponse; - - const clusterAgentId = 'cluster-agent-id'; - const cursor = { - first: MAX_LIST_COUNT, - last: null, - }; - const agentName = 'cluster-agent'; - const projectPath = 'path/to/project'; const defaultProvide = { - agentName, - projectPath, canAdminCluster: true, }; - const propsData = { - clusterAgentId, - cursor, - }; - const findModal = () => wrapper.findComponent(GlModal); - const findBtn = () => wrapper.findComponent(GlButton); - const findInput = () => wrapper.findComponent(GlFormInput); - const findTextarea = () => wrapper.findComponent(GlFormTextarea); - const findAlert = () => wrapper.findComponent(GlAlert); + const findButton = () => wrapper.findComponent(GlButton); const findTooltip = () => wrapper.findComponent(GlTooltip); - const findAgentInstructions = () => findModal().findComponent(AgentToken); - const findButtonByVariant = (variant) => - findModal() - .findAll(GlButton) - .wrappers.find((button) => button.props('variant') === variant); - const findActionButton = () => findButtonByVariant('confirm'); - const findCancelButton = () => wrapper.findByTestId('agent-token-close-button'); - - const expectDisabledAttribute = (element, disabled) => { - if (disabled) { - expect(element.attributes('disabled')).toBe('true'); - } else { - expect(element.attributes('disabled')).toBeUndefined(); - } - }; - - const createMockApolloProvider = ({ mutationResponse }) => { - createResponse = jest.fn().mockResolvedValue(mutationResponse); - - return createMockApollo([[createNewAgentToken, createResponse]]); - }; - - const writeQuery = () => { - apolloProvider.clients.defaultClient.cache.writeQuery({ - query: getClusterAgentQuery, - data: getTokenResponse.data, - variables: { - agentName, - projectPath, - tokenStatus: TOKEN_STATUS_ACTIVE, - ...cursor, - }, - }); - }; - const createWrapper = async ({ provideData = {} } = {}) => { + const createWrapper = ({ provideData = {} } = {}) => { wrapper = shallowMountExtended(CreateTokenButton, { - apolloProvider, provide: { ...defaultProvide, ...provideData, }, - propsData, + directives: { + GlModalDirective: createMockDirective(), + }, stubs: { - GlModal, GlTooltip, }, }); - wrapper.vm.$refs.modal.hide = jest.fn(); - - trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn); }; - const mockCreatedResponse = (mutationResponse) => { - apolloProvider = createMockApolloProvider({ mutationResponse }); - writeQuery(); - - createWrapper(); - - findInput().vm.$emit('input', 'new-token'); - findTextarea().vm.$emit('input', 'new-token-description'); - findActionButton().vm.$emit('click'); - - return waitForPromises(); - }; - - beforeEach(() => { - createWrapper(); - }); - afterEach(() => { wrapper.destroy(); - apolloProvider = null; - createResponse = null; }); - describe('create agent token action', () => { - it('displays create agent token button', () => { - expect(findBtn().text()).toBe('Create token'); + describe('when user can create token', () => { + beforeEach(() => { + createWrapper(); }); - describe('when user cannot create token', () => { - beforeEach(() => { - createWrapper({ provideData: { canAdminCluster: false } }); - }); - - it('disabled the button', () => { - expect(findBtn().attributes('disabled')).toBe('true'); - }); - - it('shows a disabled tooltip', () => { - expect(findTooltip().attributes('title')).toBe( - 'Requires a Maintainer or greater role to perform these actions', - ); - }); + it('displays create agent token button', () => { + expect(findButton().text()).toBe('Create token'); }); - describe('when user can create a token and clicks the button', () => { - beforeEach(() => { - findBtn().vm.$emit('click'); - }); - - it('displays a token creation modal', () => { - expect(findModal().isVisible()).toBe(true); - }); - - describe('initial state', () => { - it('renders an input for the token name', () => { - expect(findInput().exists()).toBe(true); - expectDisabledAttribute(findInput(), false); - expect(findInput().attributes('max-length')).toBe(TOKEN_NAME_LIMIT.toString()); - }); - - it('renders a textarea for the token description', () => { - expect(findTextarea().exists()).toBe(true); - expectDisabledAttribute(findTextarea(), false); - }); - - it('renders a cancel button', () => { - expect(findCancelButton().isVisible()).toBe(true); - expectDisabledAttribute(findCancelButton(), false); - }); - - it('renders a disabled next button', () => { - expect(findActionButton().text()).toBe('Create token'); - expectDisabledAttribute(findActionButton(), true); - }); - - it('sends tracking event for modal shown', () => { - findModal().vm.$emit('show'); - expect(trackingSpy).toHaveBeenCalledWith(undefined, EVENT_ACTIONS_OPEN, { - label: EVENT_LABEL_MODAL, - }); - }); - }); - - describe('when user inputs the token name', () => { - beforeEach(() => { - expectDisabledAttribute(findActionButton(), true); - findInput().vm.$emit('input', 'new-token'); - }); - - it('enables the next button', () => { - expectDisabledAttribute(findActionButton(), false); - }); - }); - - describe('when user clicks the create-token button', () => { - beforeEach(async () => { - const loadingResponse = new Promise(() => {}); - await mockCreatedResponse(loadingResponse); - - findInput().vm.$emit('input', 'new-token'); - findActionButton().vm.$emit('click'); - }); - - it('disables the create-token button', () => { - expectDisabledAttribute(findActionButton(), true); - }); - - it('hides the cancel button', () => { - expect(findCancelButton().exists()).toBe(false); - }); - }); - - describe('creating a new token', () => { - beforeEach(async () => { - await mockCreatedResponse(clusterAgentToken); - }); + it('displays create agent token button as not disabled', () => { + expect(findButton().attributes('disabled')).toBeUndefined(); + }); - it('creates a token', () => { - expect(createResponse).toHaveBeenCalledWith({ - input: { clusterAgentId, name: 'new-token', description: 'new-token-description' }, - }); - }); + it('triggers the modal', () => { + const binding = getBinding(findButton().element, 'gl-modal-directive'); - it('shows agent instructions', () => { - expect(findAgentInstructions().props()).toMatchObject({ - agentName, - agentToken: 'token-secret', - modalId: CREATE_TOKEN_MODAL, - }); - }); + expect(binding.value).toBe(CREATE_TOKEN_MODAL); + }); + }); - it('renders a close button', () => { - expect(findActionButton().isVisible()).toBe(true); - expect(findActionButton().text()).toBe('Close'); - expectDisabledAttribute(findActionButton(), false); - }); - }); + describe('when user cannot create token', () => { + beforeEach(() => { + createWrapper({ provideData: { canAdminCluster: false } }); + }); - describe('error creating a new token', () => { - beforeEach(async () => { - await mockCreatedResponse(createAgentTokenErrorResponse); - }); + it('disabled the button', () => { + expect(findButton().attributes('disabled')).toBe('true'); + }); - it('displays the error message', async () => { - expect(findAlert().text()).toBe( - createAgentTokenErrorResponse.data.clusterAgentTokenCreate.errors[0], - ); - }); - }); + it('shows a disabled tooltip', () => { + expect(findTooltip().attributes('title')).toBe( + 'Requires a Maintainer or greater role to perform these actions', + ); }); }); }); diff --git a/spec/frontend/clusters/agents/components/create_token_modal_spec.js b/spec/frontend/clusters/agents/components/create_token_modal_spec.js new file mode 100644 index 00000000000..ad48afe10b6 --- /dev/null +++ b/spec/frontend/clusters/agents/components/create_token_modal_spec.js @@ -0,0 +1,223 @@ +import { GlButton, GlModal, GlFormInput, GlFormTextarea, GlAlert } from '@gitlab/ui'; +import Vue from 'vue'; +import VueApollo from 'vue-apollo'; +import createMockApollo from 'helpers/mock_apollo_helper'; +import waitForPromises from 'helpers/wait_for_promises'; +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import { mockTracking } from 'helpers/tracking_helper'; +import { + EVENT_LABEL_MODAL, + EVENT_ACTIONS_OPEN, + TOKEN_NAME_LIMIT, + TOKEN_STATUS_ACTIVE, + MAX_LIST_COUNT, + CREATE_TOKEN_MODAL, +} from '~/clusters/agents/constants'; +import createNewAgentToken from '~/clusters/agents/graphql/mutations/create_new_agent_token.mutation.graphql'; +import getClusterAgentQuery from '~/clusters/agents/graphql/queries/get_cluster_agent.query.graphql'; +import AgentToken from '~/clusters_list/components/agent_token.vue'; +import CreateTokenModal from '~/clusters/agents/components/create_token_modal.vue'; +import { + clusterAgentToken, + getTokenResponse, + createAgentTokenErrorResponse, +} from '../../mock_data'; + +Vue.use(VueApollo); + +describe('CreateTokenModal', () => { + let wrapper; + let apolloProvider; + let trackingSpy; + let createResponse; + + const clusterAgentId = 'cluster-agent-id'; + const cursor = { + first: MAX_LIST_COUNT, + last: null, + }; + const agentName = 'cluster-agent'; + const projectPath = 'path/to/project'; + + const provide = { + agentName, + projectPath, + }; + const propsData = { + clusterAgentId, + cursor, + }; + + const findModal = () => wrapper.findComponent(GlModal); + const findInput = () => wrapper.findComponent(GlFormInput); + const findTextarea = () => wrapper.findComponent(GlFormTextarea); + const findAlert = () => wrapper.findComponent(GlAlert); + const findAgentInstructions = () => findModal().findComponent(AgentToken); + const findButtonByVariant = (variant) => + findModal() + .findAll(GlButton) + .wrappers.find((button) => button.props('variant') === variant); + const findActionButton = () => findButtonByVariant('confirm'); + const findCancelButton = () => wrapper.findByTestId('agent-token-close-button'); + + const expectDisabledAttribute = (element, disabled) => { + if (disabled) { + expect(element.attributes('disabled')).toBe('true'); + } else { + expect(element.attributes('disabled')).toBeUndefined(); + } + }; + + const createMockApolloProvider = ({ mutationResponse }) => { + createResponse = jest.fn().mockResolvedValue(mutationResponse); + + return createMockApollo([[createNewAgentToken, createResponse]]); + }; + + const writeQuery = () => { + apolloProvider.clients.defaultClient.cache.writeQuery({ + query: getClusterAgentQuery, + data: getTokenResponse.data, + variables: { + agentName, + projectPath, + tokenStatus: TOKEN_STATUS_ACTIVE, + ...cursor, + }, + }); + }; + + const createWrapper = () => { + wrapper = shallowMountExtended(CreateTokenModal, { + apolloProvider, + provide, + propsData, + stubs: { + GlModal, + }, + }); + wrapper.vm.$refs.modal.hide = jest.fn(); + + trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn); + }; + + const mockCreatedResponse = (mutationResponse) => { + apolloProvider = createMockApolloProvider({ mutationResponse }); + writeQuery(); + + createWrapper(); + + findInput().vm.$emit('input', 'new-token'); + findTextarea().vm.$emit('input', 'new-token-description'); + findActionButton().vm.$emit('click'); + + return waitForPromises(); + }; + + beforeEach(() => { + createWrapper(); + }); + + afterEach(() => { + wrapper.destroy(); + apolloProvider = null; + createResponse = null; + }); + + describe('initial state', () => { + it('renders an input for the token name', () => { + expect(findInput().exists()).toBe(true); + expectDisabledAttribute(findInput(), false); + expect(findInput().attributes('max-length')).toBe(TOKEN_NAME_LIMIT.toString()); + }); + + it('renders a textarea for the token description', () => { + expect(findTextarea().exists()).toBe(true); + expectDisabledAttribute(findTextarea(), false); + }); + + it('renders a cancel button', () => { + expect(findCancelButton().isVisible()).toBe(true); + expectDisabledAttribute(findCancelButton(), false); + }); + + it('renders a disabled next button', () => { + expect(findActionButton().text()).toBe('Create token'); + expectDisabledAttribute(findActionButton(), true); + }); + + it('sends tracking event for modal shown', () => { + findModal().vm.$emit('show'); + expect(trackingSpy).toHaveBeenCalledWith(undefined, EVENT_ACTIONS_OPEN, { + label: EVENT_LABEL_MODAL, + }); + }); + }); + + describe('when user inputs the token name', () => { + beforeEach(() => { + expectDisabledAttribute(findActionButton(), true); + findInput().vm.$emit('input', 'new-token'); + }); + + it('enables the next button', () => { + expectDisabledAttribute(findActionButton(), false); + }); + }); + + describe('when user clicks the create-token button', () => { + beforeEach(async () => { + const loadingResponse = new Promise(() => {}); + await mockCreatedResponse(loadingResponse); + + findInput().vm.$emit('input', 'new-token'); + findActionButton().vm.$emit('click'); + }); + + it('disables the create-token button', () => { + expectDisabledAttribute(findActionButton(), true); + }); + + it('hides the cancel button', () => { + expect(findCancelButton().exists()).toBe(false); + }); + }); + + describe('creating a new token', () => { + beforeEach(async () => { + await mockCreatedResponse(clusterAgentToken); + }); + + it('creates a token', () => { + expect(createResponse).toHaveBeenCalledWith({ + input: { clusterAgentId, name: 'new-token', description: 'new-token-description' }, + }); + }); + + it('shows agent instructions', () => { + expect(findAgentInstructions().props()).toMatchObject({ + agentName, + agentToken: 'token-secret', + modalId: CREATE_TOKEN_MODAL, + }); + }); + + it('renders a close button', () => { + expect(findActionButton().isVisible()).toBe(true); + expect(findActionButton().text()).toBe('Close'); + expectDisabledAttribute(findActionButton(), false); + }); + }); + + describe('error creating a new token', () => { + beforeEach(async () => { + await mockCreatedResponse(createAgentTokenErrorResponse); + }); + + it('displays the error message', async () => { + expect(findAlert().text()).toBe( + createAgentTokenErrorResponse.data.clusterAgentTokenCreate.errors[0], + ); + }); + }); +}); diff --git a/spec/frontend/clusters/agents/components/token_table_spec.js b/spec/frontend/clusters/agents/components/token_table_spec.js index f6baaf87fa4..6caeaf5c192 100644 --- a/spec/frontend/clusters/agents/components/token_table_spec.js +++ b/spec/frontend/clusters/agents/components/token_table_spec.js @@ -2,6 +2,7 @@ import { GlEmptyState, GlTooltip, GlTruncate } from '@gitlab/ui'; import { mount } from '@vue/test-utils'; import TokenTable from '~/clusters/agents/components/token_table.vue'; import CreateTokenButton from '~/clusters/agents/components/create_token_button.vue'; +import CreateTokenModal from '~/clusters/agents/components/create_token_modal.vue'; import { useFakeDate } from 'helpers/fake_date'; import { extendedWrapper } from 'helpers/vue_test_utils_helper'; import { MAX_LIST_COUNT } from '~/clusters/agents/constants'; @@ -50,6 +51,7 @@ describe('ClusterAgentTokenTable', () => { const findEmptyState = () => wrapper.findComponent(GlEmptyState); const findCreateTokenBtn = () => wrapper.findComponent(CreateTokenButton); + const findCreateModal = () => wrapper.findComponent(CreateTokenModal); beforeEach(() => { return createComponent(defaultTokens); @@ -63,8 +65,8 @@ describe('ClusterAgentTokenTable', () => { expect(findCreateTokenBtn().exists()).toBe(true); }); - it('passes the correct params to the create token component', () => { - expect(findCreateTokenBtn().props()).toMatchObject({ + it('passes the correct params to the create token modal component', () => { + expect(findCreateModal().props()).toMatchObject({ clusterAgentId, cursor, }); diff --git a/spec/lib/gitlab/kubernetes/kube_client_spec.rb b/spec/lib/gitlab/kubernetes/kube_client_spec.rb index dfd5092b54d..8abd041fd4e 100644 --- a/spec/lib/gitlab/kubernetes/kube_client_spec.rb +++ b/spec/lib/gitlab/kubernetes/kube_client_spec.rb @@ -171,20 +171,6 @@ RSpec.describe Gitlab::Kubernetes::KubeClient do end end - describe '#extensions_client' do - subject { client.extensions_client } - - it_behaves_like 'a Kubeclient' - - it 'has the extensions API group endpoint' do - expect(subject.api_endpoint.to_s).to match(%r{\/apis\/extensions\Z}) - end - - it 'has the api_version' do - expect(subject.instance_variable_get(:@api_version)).to eq('v1beta1') - end - end - describe '#istio_client' do subject { client.istio_client } @@ -307,86 +293,38 @@ RSpec.describe Gitlab::Kubernetes::KubeClient do end end - describe '#get_deployments' do - let(:extensions_client) { client.extensions_client } + describe 'apps/v1 API group' do let(:apps_client) { client.apps_client } - include_examples 'redirection not allowed', 'get_deployments' - include_examples 'dns rebinding not allowed', 'get_deployments' - - it 'delegates to the extensions client' do - expect(extensions_client).to receive(:get_deployments) - - client.get_deployments - end - - context 'extensions does not have deployments for Kubernetes 1.16+ clusters' do - before do - WebMock - .stub_request(:get, api_url + '/apis/extensions/v1beta1') - .to_return(kube_response(kube_1_16_extensions_v1beta1_discovery_body)) - end + describe 'get_deployments' do + include_examples 'redirection not allowed', 'get_deployments' + include_examples 'dns rebinding not allowed', 'get_deployments' it 'delegates to the apps client' do - expect(apps_client).to receive(:get_deployments) - - client.get_deployments + expect(client).to delegate_method(:get_deployments).to(:apps_client) end - end - end - - describe '#get_ingresses' do - let(:extensions_client) { client.extensions_client } - let(:networking_client) { client.networking_client } - - include_examples 'redirection not allowed', 'get_ingresses' - include_examples 'dns rebinding not allowed', 'get_ingresses' - it 'delegates to the extensions client' do - expect(extensions_client).to receive(:get_ingresses) - - client.get_ingresses - end - - context 'extensions does not have deployments for Kubernetes 1.22+ clusters' do - before do - WebMock - .stub_request(:get, api_url + '/apis/extensions/v1beta1') - .to_return(kube_response(kube_1_22_extensions_v1beta1_discovery_body)) - end - - it 'delegates to the apps client' do - expect(networking_client).to receive(:get_ingresses) - - client.get_ingresses + it 'responds to the method' do + expect(client).to respond_to :get_deployments end end end - describe '#patch_ingress' do - let(:extensions_client) { client.extensions_client } + describe 'networking.k8s.io/v1 API group' do let(:networking_client) { client.networking_client } - include_examples 'redirection not allowed', 'patch_ingress' - include_examples 'dns rebinding not allowed', 'patch_ingress' - - it 'delegates to the extensions client' do - expect(extensions_client).to receive(:patch_ingress) - - client.patch_ingress - end - - context 'extensions does not have ingress for Kubernetes 1.22+ clusters' do - before do - WebMock - .stub_request(:get, api_url + '/apis/extensions/v1beta1') - .to_return(kube_response(kube_1_22_extensions_v1beta1_discovery_body)) - end + [:get_ingresses, :patch_ingress].each do |method| + describe "##{method}" do + include_examples 'redirection not allowed', method + include_examples 'dns rebinding not allowed', method - it 'delegates to the apps client' do - expect(networking_client).to receive(:patch_ingress) + it 'delegates to the networking client' do + expect(client).to delegate_method(method).to(:networking_client) + end - client.patch_ingress + it 'responds to the method' do + expect(client).to respond_to method + end end end end diff --git a/spec/support/helpers/kubernetes_helpers.rb b/spec/support/helpers/kubernetes_helpers.rb index ff61cceba06..29064f01913 100644 --- a/spec/support/helpers/kubernetes_helpers.rb +++ b/spec/support/helpers/kubernetes_helpers.rb @@ -40,9 +40,6 @@ module KubernetesHelpers def stub_kubeclient_discover_base(api_url) WebMock.stub_request(:get, api_url + '/api/v1').to_return(kube_response(kube_v1_discovery_body)) WebMock - .stub_request(:get, api_url + '/apis/extensions/v1beta1') - .to_return(kube_response(kube_extensions_v1beta1_discovery_body)) - WebMock .stub_request(:get, api_url + '/apis/apps/v1') .to_return(kube_response(kube_apps_v1_discovery_body)) WebMock @@ -149,7 +146,7 @@ module KubernetesHelpers def stub_kubeclient_deployments(namespace, status: nil) stub_kubeclient_discover(service.api_url) - deployments_url = service.api_url + "/apis/extensions/v1beta1/namespaces/#{namespace}/deployments" + deployments_url = service.api_url + "/apis/apps/v1/namespaces/#{namespace}/deployments" response = { status: status } if status WebMock.stub_request(:get, deployments_url).to_return(response || kube_deployments_response) @@ -157,7 +154,7 @@ module KubernetesHelpers def stub_kubeclient_ingresses(namespace, status: nil, method: :get, resource_path: "", response: kube_ingresses_response) stub_kubeclient_discover(service.api_url) - ingresses_url = service.api_url + "/apis/extensions/v1beta1/namespaces/#{namespace}/ingresses#{resource_path}" + ingresses_url = service.api_url + "/apis/networking.k8s.io/v1/namespaces/#{namespace}/ingresses#{resource_path}" response = { status: status } if status WebMock.stub_request(method, ingresses_url).to_return(response) @@ -314,24 +311,6 @@ module KubernetesHelpers } end - # From Kubernetes 1.16+ Deployments are no longer served from apis/extensions - def kube_1_16_extensions_v1beta1_discovery_body - { - "kind" => "APIResourceList", - "resources" => [ - { "name" => "ingresses", "namespaced" => true, "kind" => "Deployment" } - ] - } - end - - # From Kubernetes 1.22+ Ingresses are no longer served from apis/extensions - def kube_1_22_extensions_v1beta1_discovery_body - { - "kind" => "APIResourceList", - "resources" => [] - } - end - def kube_knative_discovery_body { "kind" => "APIResourceList", @@ -339,18 +318,6 @@ module KubernetesHelpers } end - def kube_extensions_v1beta1_discovery_body - { - "kind" => "APIResourceList", - "resources" => [ - { "name" => "deployments", "namespaced" => true, "kind" => "Deployment" }, - { "name" => "ingresses", "namespaced" => true, "kind" => "Ingress" } - ] - } - end - - # Yes, deployments are defined in both apis/extensions/v1beta1 and apis/v1 - # (for Kubernetes < 1.16). This matches what Kubenetes API server returns. def kube_apps_v1_discovery_body { "kind" => "APIResourceList", diff --git a/spec/uploaders/object_storage_spec.rb b/spec/uploaders/object_storage_spec.rb index 13f70e3f85b..1bcc43b81a8 100644 --- a/spec/uploaders/object_storage_spec.rb +++ b/spec/uploaders/object_storage_spec.rb @@ -48,6 +48,28 @@ RSpec.describe ObjectStorage do expect(uploader.store_dir).to start_with("uploads/-/system/user/") end end + + describe '#store_path' do + subject { uploader.store_path('filename') } + + it 'uses store_dir' do + expect(subject).to eq("uploads/-/system/user/#{object.id}/filename") + end + + context 'when a bucket prefix is configured' do + before do + allow(uploader_class).to receive(:object_store_options) do + double( + bucket_prefix: 'my/prefix' + ) + end + end + + it 'uses store_dir and ignores prefix' do + expect(subject).to eq("uploads/-/system/user/#{object.id}/filename") + end + end + end end context 'object_store is Store::REMOTE' do @@ -60,6 +82,28 @@ RSpec.describe ObjectStorage do expect(uploader.store_dir).to start_with("user/") end end + + describe '#store_path' do + subject { uploader.store_path('filename') } + + it 'uses store_dir' do + expect(subject).to eq("user/#{object.id}/filename") + end + + context 'when a bucket prefix is configured' do + before do + allow(uploader_class).to receive(:object_store_options) do + double( + bucket_prefix: 'my/prefix' + ) + end + end + + it 'uses the prefix and store_dir' do + expect(subject).to eq("my/prefix/user/#{object.id}/filename") + end + end + end end end |