diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2019-11-08 06:06:24 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2019-11-08 06:06:24 +0000 |
commit | 1ef4b65f55f4fc6524a47050b4f6d686beb81d3a (patch) | |
tree | 3efc2710e564b86e5e2420d65457f656454006bb /spec | |
parent | 18a102a5b95198b6bc8db2589de6353997a33543 (diff) | |
download | gitlab-ce-1ef4b65f55f4fc6524a47050b4f6d686beb81d3a.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
18 files changed, 783 insertions, 15 deletions
diff --git a/spec/controllers/admin/clusters_controller_spec.rb b/spec/controllers/admin/clusters_controller_spec.rb index 233710b9fc3..d3192593a78 100644 --- a/spec/controllers/admin/clusters_controller_spec.rb +++ b/spec/controllers/admin/clusters_controller_spec.rb @@ -73,7 +73,7 @@ describe Admin::ClustersController do end describe 'GET #new' do - def get_new(provider: 'gke') + def get_new(provider: 'gcp') get :new, params: { provider: provider } end @@ -318,6 +318,51 @@ describe Admin::ClustersController do end end + describe 'POST authorize AWS role for EKS cluster' do + let(:role_arn) { 'arn:aws:iam::123456789012:role/role-name' } + let(:role_external_id) { '12345' } + + let(:params) do + { + cluster: { + role_arn: role_arn, + role_external_id: role_external_id + } + } + end + + def go + post :authorize_aws_role, params: params + end + + it 'creates an Aws::Role record' do + expect { go }.to change { Aws::Role.count } + + expect(response.status).to eq 201 + + role = Aws::Role.last + expect(role.user).to eq admin + expect(role.role_arn).to eq role_arn + expect(role.role_external_id).to eq role_external_id + end + + context 'role cannot be created' do + let(:role_arn) { 'invalid-role' } + + it 'does not create a record' do + expect { go }.not_to change { Aws::Role.count } + + expect(response.status).to eq 422 + end + end + + describe 'security' do + it { expect { go }.to be_allowed_for(:admin) } + it { expect { go }.to be_denied_for(:user) } + it { expect { go }.to be_denied_for(:external) } + end + end + describe 'GET #cluster_status' do let(:cluster) { create(:cluster, :providing_by_gcp, :instance) } diff --git a/spec/controllers/groups/clusters_controller_spec.rb b/spec/controllers/groups/clusters_controller_spec.rb index 51a6dcca640..538a270f567 100644 --- a/spec/controllers/groups/clusters_controller_spec.rb +++ b/spec/controllers/groups/clusters_controller_spec.rb @@ -85,7 +85,7 @@ describe Groups::ClustersController do end describe 'GET new' do - def go(provider: 'gke') + def go(provider: 'gcp') get :new, params: { group_id: group, provider: provider } end @@ -372,6 +372,56 @@ describe Groups::ClustersController do end end + describe 'POST authorize AWS role for EKS cluster' do + let(:role_arn) { 'arn:aws:iam::123456789012:role/role-name' } + let(:role_external_id) { '12345' } + + let(:params) do + { + cluster: { + role_arn: role_arn, + role_external_id: role_external_id + } + } + end + + def go + post :authorize_aws_role, params: params.merge(group_id: group) + end + + it 'creates an Aws::Role record' do + expect { go }.to change { Aws::Role.count } + + expect(response.status).to eq 201 + + role = Aws::Role.last + expect(role.user).to eq user + expect(role.role_arn).to eq role_arn + expect(role.role_external_id).to eq role_external_id + end + + context 'role cannot be created' do + let(:role_arn) { 'invalid-role' } + + it 'does not create a record' do + expect { go }.not_to change { Aws::Role.count } + + expect(response.status).to eq 422 + end + end + + describe 'security' do + it { expect { go }.to be_allowed_for(:admin) } + it { expect { go }.to be_allowed_for(:owner).of(group) } + it { expect { go }.to be_allowed_for(:maintainer).of(group) } + it { expect { go }.to be_denied_for(:developer).of(group) } + it { expect { go }.to be_denied_for(:reporter).of(group) } + it { expect { go }.to be_denied_for(:guest).of(group) } + it { expect { go }.to be_denied_for(:user) } + it { expect { go }.to be_denied_for(:external) } + end + end + describe 'GET cluster_status' do let(:cluster) { create(:cluster, :providing_by_gcp, cluster_type: :group_type, groups: [group]) } diff --git a/spec/controllers/projects/clusters_controller_spec.rb b/spec/controllers/projects/clusters_controller_spec.rb index e1f6d571d27..1b6b0ff025e 100644 --- a/spec/controllers/projects/clusters_controller_spec.rb +++ b/spec/controllers/projects/clusters_controller_spec.rb @@ -79,7 +79,7 @@ describe Projects::ClustersController do end describe 'GET new' do - def go(provider: 'gke') + def go(provider: 'gcp') get :new, params: { namespace_id: project.namespace, project_id: project, @@ -373,6 +373,56 @@ describe Projects::ClustersController do end end + describe 'POST authorize AWS role for EKS cluster' do + let(:role_arn) { 'arn:aws:iam::123456789012:role/role-name' } + let(:role_external_id) { '12345' } + + let(:params) do + { + cluster: { + role_arn: role_arn, + role_external_id: role_external_id + } + } + end + + def go + post :authorize_aws_role, params: params.merge(namespace_id: project.namespace, project_id: project) + end + + it 'creates an Aws::Role record' do + expect { go }.to change { Aws::Role.count } + + expect(response.status).to eq 201 + + role = Aws::Role.last + expect(role.user).to eq user + expect(role.role_arn).to eq role_arn + expect(role.role_external_id).to eq role_external_id + end + + context 'role cannot be created' do + let(:role_arn) { 'invalid-role' } + + it 'does not create a record' do + expect { go }.not_to change { Aws::Role.count } + + expect(response.status).to eq 422 + end + end + + describe 'security' do + it { expect { go }.to be_allowed_for(:admin) } + it { expect { go }.to be_allowed_for(:owner).of(project) } + it { expect { go }.to be_allowed_for(:maintainer).of(project) } + it { expect { go }.to be_denied_for(:developer).of(project) } + it { expect { go }.to be_denied_for(:reporter).of(project) } + it { expect { go }.to be_denied_for(:guest).of(project) } + it { expect { go }.to be_denied_for(:user) } + it { expect { go }.to be_denied_for(:external) } + end + end + describe 'GET cluster_status' do let(:cluster) { create(:cluster, :providing_by_gcp, projects: [project]) } diff --git a/spec/db/schema_spec.rb b/spec/db/schema_spec.rb index e4eddb87858..d340cec8b70 100644 --- a/spec/db/schema_spec.rb +++ b/spec/db/schema_spec.rb @@ -13,7 +13,7 @@ describe 'Database schema' do # EE: edit the ee/spec/db/schema_support.rb IGNORED_FK_COLUMNS = { abuse_reports: %w[reporter_id user_id], - application_settings: %w[performance_bar_allowed_group_id slack_app_id snowplow_site_id], + application_settings: %w[performance_bar_allowed_group_id slack_app_id snowplow_site_id eks_account_id eks_access_key_id], approvers: %w[target_id user_id], approvals: %w[user_id], approver_groups: %w[target_id], diff --git a/spec/features/projects/clusters/eks_spec.rb b/spec/features/projects/clusters/eks_spec.rb index 9cb989b8004..e0ebccd85ac 100644 --- a/spec/features/projects/clusters/eks_spec.rb +++ b/spec/features/projects/clusters/eks_spec.rb @@ -10,6 +10,7 @@ describe 'AWS EKS Cluster', :js do project.add_maintainer(user) gitlab_sign_in(user) allow(Projects::ClustersController).to receive(:STATUS_POLLING_INTERVAL) { 100 } + stub_application_setting(eks_integration_enabled: true) end context 'when user does not have a cluster and visits cluster index page' do diff --git a/spec/frontend/create_cluster/eks_cluster/components/create_eks_cluster_spec.js b/spec/frontend/create_cluster/eks_cluster/components/create_eks_cluster_spec.js new file mode 100644 index 00000000000..4bf3ac430f5 --- /dev/null +++ b/spec/frontend/create_cluster/eks_cluster/components/create_eks_cluster_spec.js @@ -0,0 +1,91 @@ +import Vuex from 'vuex'; +import { shallowMount, createLocalVue } from '@vue/test-utils'; + +import CreateEksCluster from '~/create_cluster/eks_cluster/components/create_eks_cluster.vue'; +import EksClusterConfigurationForm from '~/create_cluster/eks_cluster/components/eks_cluster_configuration_form.vue'; +import ServiceCredentialsForm from '~/create_cluster/eks_cluster/components/service_credentials_form.vue'; + +const localVue = createLocalVue(); +localVue.use(Vuex); + +describe('CreateEksCluster', () => { + let vm; + let state; + const gitlabManagedClusterHelpPath = 'gitlab-managed-cluster-help-path'; + const accountAndExternalIdsHelpPath = 'account-and-external-id-help-path'; + const createRoleArnHelpPath = 'role-arn-help-path'; + const kubernetesIntegrationHelpPath = 'kubernetes-integration'; + const externalLinkIcon = 'external-link'; + + beforeEach(() => { + state = { hasCredentials: false }; + const store = new Vuex.Store({ + state, + }); + + vm = shallowMount(CreateEksCluster, { + propsData: { + gitlabManagedClusterHelpPath, + accountAndExternalIdsHelpPath, + createRoleArnHelpPath, + externalLinkIcon, + kubernetesIntegrationHelpPath, + }, + localVue, + store, + }); + }); + afterEach(() => vm.destroy()); + + describe('when credentials are provided', () => { + beforeEach(() => { + state.hasCredentials = true; + }); + + it('displays eks cluster configuration form when credentials are valid', () => { + expect(vm.find(EksClusterConfigurationForm).exists()).toBe(true); + }); + + describe('passes to the cluster configuration form', () => { + it('help url for kubernetes integration documentation', () => { + expect(vm.find(EksClusterConfigurationForm).props('gitlabManagedClusterHelpPath')).toBe( + gitlabManagedClusterHelpPath, + ); + }); + + it('help url for gitlab managed cluster documentation', () => { + expect(vm.find(EksClusterConfigurationForm).props('kubernetesIntegrationHelpPath')).toBe( + kubernetesIntegrationHelpPath, + ); + }); + }); + }); + + describe('when credentials are invalid', () => { + beforeEach(() => { + state.hasCredentials = false; + }); + + it('displays service credentials form', () => { + expect(vm.find(ServiceCredentialsForm).exists()).toBe(true); + }); + + describe('passes to the service credentials form', () => { + it('help url for account and external ids', () => { + expect(vm.find(ServiceCredentialsForm).props('accountAndExternalIdsHelpPath')).toBe( + accountAndExternalIdsHelpPath, + ); + }); + + it('external link icon', () => { + expect(vm.find(ServiceCredentialsForm).props('externalLinkIcon')).toBe(externalLinkIcon); + }); + + it('help url to create a role ARN', () => { + expect(vm.find(ServiceCredentialsForm).props('createRoleArnHelpPath')).toBe( + createRoleArnHelpPath, + ); + }); + }); + }); +}); diff --git a/spec/frontend/create_cluster/eks_cluster/components/service_credentials_form_spec.js b/spec/frontend/create_cluster/eks_cluster/components/service_credentials_form_spec.js new file mode 100644 index 00000000000..0be723b48f0 --- /dev/null +++ b/spec/frontend/create_cluster/eks_cluster/components/service_credentials_form_spec.js @@ -0,0 +1,117 @@ +import Vuex from 'vuex'; +import { shallowMount, createLocalVue } from '@vue/test-utils'; + +import ServiceCredentialsForm from '~/create_cluster/eks_cluster/components/service_credentials_form.vue'; +import LoadingButton from '~/vue_shared/components/loading_button.vue'; + +import eksClusterState from '~/create_cluster/eks_cluster/store/state'; + +const localVue = createLocalVue(); +localVue.use(Vuex); + +describe('ServiceCredentialsForm', () => { + let vm; + let state; + let createRoleAction; + const accountId = 'accountId'; + const externalId = 'externalId'; + + beforeEach(() => { + state = Object.assign(eksClusterState(), { + accountId, + externalId, + }); + createRoleAction = jest.fn(); + + const store = new Vuex.Store({ + state, + actions: { + createRole: createRoleAction, + }, + }); + vm = shallowMount(ServiceCredentialsForm, { + propsData: { + accountAndExternalIdsHelpPath: '', + createRoleArnHelpPath: '', + externalLinkIcon: '', + }, + localVue, + store, + }); + }); + afterEach(() => vm.destroy()); + + const findAccountIdInput = () => vm.find('#gitlab-account-id'); + const findCopyAccountIdButton = () => vm.find('.js-copy-account-id-button'); + const findExternalIdInput = () => vm.find('#eks-external-id'); + const findCopyExternalIdButton = () => vm.find('.js-copy-external-id-button'); + const findInvalidCredentials = () => vm.find('.js-invalid-credentials'); + const findSubmitButton = () => vm.find(LoadingButton); + const findForm = () => vm.find('form[name="service-credentials-form"]'); + + it('displays provided account id', () => { + expect(findAccountIdInput().attributes('value')).toBe(accountId); + }); + + it('allows to copy account id', () => { + expect(findCopyAccountIdButton().props('text')).toBe(accountId); + }); + + it('displays provided external id', () => { + expect(findExternalIdInput().attributes('value')).toBe(externalId); + }); + + it('allows to copy external id', () => { + expect(findCopyExternalIdButton().props('text')).toBe(externalId); + }); + + it('disables submit button when role ARN is not provided', () => { + expect(findSubmitButton().attributes('disabled')).toBeTruthy(); + }); + + it('enables submit button when role ARN is not provided', () => { + vm.setData({ roleArn: '123' }); + + expect(findSubmitButton().attributes('disabled')).toBeFalsy(); + }); + + it('dispatches createRole action when form is submitted', () => { + findForm().trigger('submit'); + + expect(createRoleAction).toHaveBeenCalled(); + }); + + describe('when is creating role', () => { + beforeEach(() => { + vm.setData({ roleArn: '123' }); // set role ARN to enable button + + state.isCreatingRole = true; + }); + + it('disables submit button', () => { + expect(findSubmitButton().props('disabled')).toBe(true); + }); + + it('sets submit button as loading', () => { + expect(findSubmitButton().props('loading')).toBe(true); + }); + + it('displays Authenticating label on submit button', () => { + expect(findSubmitButton().props('label')).toBe('Authenticating'); + }); + }); + + describe('when role can’t be created', () => { + beforeEach(() => { + state.createRoleError = 'Invalid credentials'; + }); + + it('displays invalid role warning banner', () => { + expect(findInvalidCredentials().exists()).toBe(true); + }); + + it('displays invalid role error message', () => { + expect(findInvalidCredentials().text()).toContain(state.createRoleError); + }); + }); +}); diff --git a/spec/frontend/create_cluster/eks_cluster/store/actions_spec.js b/spec/frontend/create_cluster/eks_cluster/store/actions_spec.js index 1ed7f806804..99c8cdba296 100644 --- a/spec/frontend/create_cluster/eks_cluster/store/actions_spec.js +++ b/spec/frontend/create_cluster/eks_cluster/store/actions_spec.js @@ -13,7 +13,12 @@ import { SET_ROLE, SET_SECURITY_GROUP, SET_GITLAB_MANAGED_CLUSTER, + REQUEST_CREATE_ROLE, + CREATE_ROLE_SUCCESS, + CREATE_ROLE_ERROR, } from '~/create_cluster/eks_cluster/store/mutation_types'; +import axios from '~/lib/utils/axios_utils'; +import MockAdapter from 'axios-mock-adapter'; describe('EKS Cluster Store Actions', () => { let clusterName; @@ -26,6 +31,8 @@ describe('EKS Cluster Store Actions', () => { let keyPair; let securityGroup; let gitlabManagedCluster; + let mock; + let state; beforeEach(() => { clusterName = 'my cluster'; @@ -38,6 +45,19 @@ describe('EKS Cluster Store Actions', () => { keyPair = { name: 'key-pair-1' }; securityGroup = { name: 'default group' }; gitlabManagedCluster = true; + + state = { + ...createState(), + createRolePath: '/clusters/roles/', + }; + }); + + beforeEach(() => { + mock = new MockAdapter(axios); + }); + + afterEach(() => { + mock.restore(); }); it.each` @@ -55,6 +75,78 @@ describe('EKS Cluster Store Actions', () => { `(`$action commits $mutation with $payloadDescription payload`, data => { const { action, mutation, payload } = data; - testAction(actions[action], payload, createState(), [{ type: mutation, payload }]); + testAction(actions[action], payload, state, [{ type: mutation, payload }]); + }); + + describe('createRole', () => { + const payload = { + roleArn: 'role_arn', + externalId: 'externalId', + }; + + describe('when request succeeds', () => { + beforeEach(() => { + mock + .onPost(state.createRolePath, { + role_arn: payload.roleArn, + role_external_id: payload.externalId, + }) + .reply(201); + }); + + it('dispatches createRoleSuccess action', () => + testAction( + actions.createRole, + payload, + state, + [], + [{ type: 'requestCreateRole' }, { type: 'createRoleSuccess' }], + )); + }); + + describe('when request fails', () => { + let error; + + beforeEach(() => { + error = new Error('Request failed with status code 400'); + mock + .onPost(state.createRolePath, { + role_arn: payload.roleArn, + role_external_id: payload.externalId, + }) + .reply(400, error); + }); + + it('dispatches createRoleError action', () => + testAction( + actions.createRole, + payload, + state, + [], + [{ type: 'requestCreateRole' }, { type: 'createRoleError', payload: { error } }], + )); + }); + }); + + describe('requestCreateRole', () => { + it('commits requestCreaterole mutation', () => { + testAction(actions.requestCreateRole, null, state, [{ type: REQUEST_CREATE_ROLE }]); + }); + }); + + describe('createRoleSuccess', () => { + it('commits createRoleSuccess mutation', () => { + testAction(actions.createRoleSuccess, null, state, [{ type: CREATE_ROLE_SUCCESS }]); + }); + }); + + describe('createRoleError', () => { + it('commits createRoleError mutation', () => { + const payload = { + error: new Error(), + }; + + testAction(actions.createRoleError, payload, state, [{ type: CREATE_ROLE_ERROR, payload }]); + }); }); }); diff --git a/spec/frontend/create_cluster/eks_cluster/store/mutations_spec.js b/spec/frontend/create_cluster/eks_cluster/store/mutations_spec.js index 81b65180fb5..2637b4822a5 100644 --- a/spec/frontend/create_cluster/eks_cluster/store/mutations_spec.js +++ b/spec/frontend/create_cluster/eks_cluster/store/mutations_spec.js @@ -9,6 +9,9 @@ import { SET_ROLE, SET_SECURITY_GROUP, SET_GITLAB_MANAGED_CLUSTER, + REQUEST_CREATE_ROLE, + CREATE_ROLE_SUCCESS, + CREATE_ROLE_ERROR, } from '~/create_cluster/eks_cluster/store/mutation_types'; import createState from '~/create_cluster/eks_cluster/store/state'; import mutations from '~/create_cluster/eks_cluster/store/mutations'; @@ -59,4 +62,60 @@ describe('Create EKS cluster store mutations', () => { mutations[mutation](state, payload); expect(state[mutatedProperty]).toBe(expectedValue); }); + + describe(`mutation ${REQUEST_CREATE_ROLE}`, () => { + beforeEach(() => { + mutations[REQUEST_CREATE_ROLE](state); + }); + + it('sets isCreatingRole to true', () => { + expect(state.isCreatingRole).toBe(true); + }); + + it('sets createRoleError to null', () => { + expect(state.createRoleError).toBe(null); + }); + + it('sets hasCredentials to false', () => { + expect(state.hasCredentials).toBe(false); + }); + }); + + describe(`mutation ${CREATE_ROLE_SUCCESS}`, () => { + beforeEach(() => { + mutations[CREATE_ROLE_SUCCESS](state); + }); + + it('sets isCreatingRole to false', () => { + expect(state.isCreatingRole).toBe(false); + }); + + it('sets createRoleError to null', () => { + expect(state.createRoleError).toBe(null); + }); + + it('sets hasCredentials to false', () => { + expect(state.hasCredentials).toBe(true); + }); + }); + + describe(`mutation ${CREATE_ROLE_ERROR}`, () => { + const error = new Error(); + + beforeEach(() => { + mutations[CREATE_ROLE_ERROR](state, { error }); + }); + + it('sets isCreatingRole to false', () => { + expect(state.isCreatingRole).toBe(false); + }); + + it('sets createRoleError to the error object', () => { + expect(state.createRoleError).toBe(error); + }); + + it('sets hasCredentials to false', () => { + expect(state.hasCredentials).toBe(false); + }); + }); }); diff --git a/spec/frontend/vue_shared/components/slot_switch_spec.js b/spec/frontend/vue_shared/components/slot_switch_spec.js new file mode 100644 index 00000000000..cff955c05b2 --- /dev/null +++ b/spec/frontend/vue_shared/components/slot_switch_spec.js @@ -0,0 +1,56 @@ +import { shallowMount } from '@vue/test-utils'; + +import SlotSwitch from '~/vue_shared/components/slot_switch'; + +describe('SlotSwitch', () => { + const slots = { + first: '<a>AGP</a>', + second: '<p>PCI</p>', + }; + + let wrapper; + + const createComponent = propsData => { + wrapper = shallowMount(SlotSwitch, { + propsData, + slots, + sync: false, + }); + }; + + const getChildrenHtml = () => wrapper.findAll('* *').wrappers.map(c => c.html()); + + afterEach(() => { + if (wrapper) { + wrapper.destroy(); + } + }); + + it('throws an error if activeSlotNames is missing', () => { + expect(createComponent).toThrow('[Vue warn]: Missing required prop: "activeSlotNames"'); + }); + + it('renders no slots if activeSlotNames is empty', () => { + createComponent({ + activeSlotNames: [], + }); + + expect(getChildrenHtml().length).toBe(0); + }); + + it('renders one slot if activeSlotNames contains single slot name', () => { + createComponent({ + activeSlotNames: ['first'], + }); + + expect(getChildrenHtml()).toEqual([slots.first]); + }); + + it('renders multiple slots if activeSlotNames contains multiple slot names', () => { + createComponent({ + activeSlotNames: Object.keys(slots), + }); + + expect(getChildrenHtml()).toEqual(Object.values(slots)); + }); +}); diff --git a/spec/helpers/clusters_helper_spec.rb b/spec/helpers/clusters_helper_spec.rb index 4ea0f76fc28..1ee638ddf04 100644 --- a/spec/helpers/clusters_helper_spec.rb +++ b/spec/helpers/clusters_helper_spec.rb @@ -30,4 +30,60 @@ describe ClustersHelper do end end end + + describe '#create_new_cluster_label' do + subject { helper.create_new_cluster_label(provider: provider) } + + context 'GCP provider' do + let(:provider) { 'gcp' } + + it { is_expected.to eq('Create new Cluster on GKE') } + end + + context 'AWS provider' do + let(:provider) { 'aws' } + + it { is_expected.to eq('Create new Cluster on EKS') } + end + + context 'other provider' do + let(:provider) { 'other' } + + it { is_expected.to eq('Create new Cluster') } + end + + context 'no provider' do + let(:provider) { nil } + + it { is_expected.to eq('Create new Cluster') } + end + end + + describe '#render_new_provider_form' do + subject { helper.new_cluster_partial(provider: provider) } + + context 'GCP provider' do + let(:provider) { 'gcp' } + + it { is_expected.to eq('clusters/clusters/gcp/new') } + end + + context 'AWS provider' do + let(:provider) { 'aws' } + + it { is_expected.to eq('clusters/clusters/aws/new') } + end + + context 'other provider' do + let(:provider) { 'other' } + + it { is_expected.to eq('clusters/clusters/cloud_providers/cloud_provider_selector') } + end + + context 'no provider' do + let(:provider) { nil } + + it { is_expected.to eq('clusters/clusters/cloud_providers/cloud_provider_selector') } + end + end end diff --git a/spec/lib/gitlab/regex_spec.rb b/spec/lib/gitlab/regex_spec.rb index 4678adcd85f..1397add9f5a 100644 --- a/spec/lib/gitlab/regex_spec.rb +++ b/spec/lib/gitlab/regex_spec.rb @@ -66,6 +66,15 @@ describe Gitlab::Regex do end describe '.aws_account_id_regex' do + subject { described_class.aws_account_id_regex } + + it { is_expected.to match('123456789012') } + it { is_expected.not_to match('12345678901') } + it { is_expected.not_to match('1234567890123') } + it { is_expected.not_to match('12345678901a') } + end + + describe '.aws_arn_regex' do subject { described_class.aws_arn_regex } it { is_expected.to match('arn:aws:iam::123456789012:role/role-name') } diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb index b6fc152e478..9d3f5b4b132 100644 --- a/spec/models/application_setting_spec.rb +++ b/spec/models/application_setting_spec.rb @@ -106,6 +106,37 @@ describe ApplicationSetting do it { is_expected.not_to allow_value(nil).for(:lets_encrypt_notification_email) } end + describe 'EKS integration' do + before do + setting.eks_integration_enabled = eks_enabled + end + + context 'integration is disabled' do + let(:eks_enabled) { false } + + it { is_expected.to allow_value(nil).for(:eks_account_id) } + it { is_expected.to allow_value(nil).for(:eks_access_key_id) } + it { is_expected.to allow_value(nil).for(:eks_secret_access_key) } + end + + context 'integration is enabled' do + let(:eks_enabled) { true } + + it { is_expected.to allow_value('123456789012').for(:eks_account_id) } + it { is_expected.not_to allow_value(nil).for(:eks_account_id) } + it { is_expected.not_to allow_value('123').for(:eks_account_id) } + it { is_expected.not_to allow_value('12345678901a').for(:eks_account_id) } + + it { is_expected.to allow_value('access-key-id-12').for(:eks_access_key_id) } + it { is_expected.not_to allow_value('a' * 129).for(:eks_access_key_id) } + it { is_expected.not_to allow_value('short-key').for(:eks_access_key_id) } + it { is_expected.not_to allow_value(nil).for(:eks_access_key_id) } + + it { is_expected.to allow_value('secret-access-key').for(:eks_secret_access_key) } + it { is_expected.not_to allow_value(nil).for(:eks_secret_access_key) } + end + end + describe 'default_artifacts_expire_in' do it 'sets an error if it cannot parse' do setting.update(default_artifacts_expire_in: 'a') diff --git a/spec/models/aws/role_spec.rb b/spec/models/aws/role_spec.rb index c40752e40a6..d4165567146 100644 --- a/spec/models/aws/role_spec.rb +++ b/spec/models/aws/role_spec.rb @@ -31,4 +31,56 @@ describe Aws::Role do end end end + + describe 'callbacks' do + describe '#ensure_role_external_id!' do + subject { role.validate } + + context 'for a new record' do + let(:role) { build(:aws_role, role_external_id: nil) } + + it 'calls #ensure_role_external_id!' do + expect(role).to receive(:ensure_role_external_id!) + + subject + end + end + + context 'for an existing record' do + let(:role) { create(:aws_role) } + + it 'does not call #ensure_role_external_id!' do + expect(role).not_to receive(:ensure_role_external_id!) + + subject + end + end + end + end + + describe '#ensure_role_external_id!' do + let(:role) { build(:aws_role, role_external_id: external_id) } + + subject { role.ensure_role_external_id! } + + context 'role_external_id is blank' do + let(:external_id) { nil } + + it 'generates an external ID and assigns it to the record' do + subject + + expect(role.role_external_id).to be_present + end + end + + context 'role_external_id is already set' do + let(:external_id) { 'external-id' } + + it 'does not change the existing external id' do + subject + + expect(role.role_external_id).to eq external_id + end + end + end end diff --git a/spec/presenters/group_clusterable_presenter_spec.rb b/spec/presenters/group_clusterable_presenter_spec.rb index fa77273f6aa..11a8decc9cc 100644 --- a/spec/presenters/group_clusterable_presenter_spec.rb +++ b/spec/presenters/group_clusterable_presenter_spec.rb @@ -43,6 +43,12 @@ describe GroupClusterablePresenter do it { is_expected.to eq(new_group_cluster_path(group)) } end + describe '#authorize_aws_role_path' do + subject { presenter.authorize_aws_role_path } + + it { is_expected.to eq(authorize_aws_role_group_clusters_path(group)) } + end + describe '#create_user_clusters_path' do subject { presenter.create_user_clusters_path } diff --git a/spec/presenters/project_clusterable_presenter_spec.rb b/spec/presenters/project_clusterable_presenter_spec.rb index 6786a84243f..441c2a50fea 100644 --- a/spec/presenters/project_clusterable_presenter_spec.rb +++ b/spec/presenters/project_clusterable_presenter_spec.rb @@ -43,6 +43,12 @@ describe ProjectClusterablePresenter do it { is_expected.to eq(new_project_cluster_path(project)) } end + describe '#authorize_aws_role_path' do + subject { presenter.authorize_aws_role_path } + + it { is_expected.to eq(authorize_aws_role_project_clusters_path(project)) } + end + describe '#create_user_clusters_path' do subject { presenter.create_user_clusters_path } diff --git a/spec/requests/api/settings_spec.rb b/spec/requests/api/settings_spec.rb index 760eafe33e4..5aba798f2c2 100644 --- a/spec/requests/api/settings_spec.rb +++ b/spec/requests/api/settings_spec.rb @@ -271,6 +271,61 @@ describe API::Settings, 'Settings' do end end + context 'EKS integration settings' do + let(:attribute_names) { settings.keys.map(&:to_s) } + let(:sensitive_attributes) { %w(eks_secret_access_key) } + let(:exposed_attributes) { attribute_names - sensitive_attributes } + + let(:settings) do + { + eks_integration_enabled: true, + eks_account_id: '123456789012', + eks_access_key_id: 'access-key-id-12', + eks_secret_access_key: 'secret-access-key' + } + end + + it 'includes attributes in the API' do + get api("/application/settings", admin) + + expect(response).to have_gitlab_http_status(200) + exposed_attributes.each do |attribute| + expect(json_response.keys).to include(attribute) + end + end + + it 'does not include sensitive attributes in the API' do + get api("/application/settings", admin) + + expect(response).to have_gitlab_http_status(200) + sensitive_attributes.each do |attribute| + expect(json_response.keys).not_to include(attribute) + end + end + + it 'allows updating the settings' do + put api("/application/settings", admin), params: settings + + expect(response).to have_gitlab_http_status(200) + settings.each do |attribute, value| + expect(ApplicationSetting.current.public_send(attribute)).to eq(value) + end + end + + context 'EKS integration is enabled but params are blank' do + let(:settings) { Hash[eks_integration_enabled: true] } + + it 'does not update the settings' do + put api("/application/settings", admin), params: settings + + expect(response).to have_gitlab_http_status(400) + expect(json_response['error']).to include('eks_account_id is missing') + expect(json_response['error']).to include('eks_access_key_id is missing') + expect(json_response['error']).to include('eks_secret_access_key is missing') + end + end + end + context "missing plantuml_url value when plantuml_enabled is true" do it "returns a blank parameter error message" do put api("/application/settings", admin), params: { plantuml_enabled: true } diff --git a/spec/services/clusters/aws/fetch_credentials_service_spec.rb b/spec/services/clusters/aws/fetch_credentials_service_spec.rb index 6476130ab32..09f3be0534d 100644 --- a/spec/services/clusters/aws/fetch_credentials_service_spec.rb +++ b/spec/services/clusters/aws/fetch_credentials_service_spec.rb @@ -13,15 +13,6 @@ describe Clusters::Aws::FetchCredentialsService do let(:sts_client) { Aws::STS::Client.new(credentials: gitlab_credentials, region: provider.region) } let(:assumed_role) { instance_double(Aws::AssumeRoleCredentials, credentials: assumed_role_credentials) } - let(:kubernetes_provisioner_settings) do - { - aws: { - access_key_id: gitlab_access_key_id, - secret_access_key: gitlab_secret_access_key - } - } - end - let(:assumed_role_credentials) { double } subject { described_class.new(provider).execute } @@ -30,7 +21,8 @@ describe Clusters::Aws::FetchCredentialsService do let(:provision_role) { create(:aws_role, user: provider.created_by_user) } before do - stub_config(kubernetes: { provisioners: kubernetes_provisioner_settings }) + stub_application_setting(eks_access_key_id: gitlab_access_key_id) + stub_application_setting(eks_secret_access_key: gitlab_secret_access_key) expect(Aws::Credentials).to receive(:new) .with(gitlab_access_key_id, gitlab_secret_access_key) |