diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2019-12-12 00:07:43 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2019-12-12 00:07:43 +0000 |
commit | 2e3cbf7d89815e2915f77677388c49b48f8d20c3 (patch) | |
tree | 03bdbc99e829295e8077b2ec4032300c15b48e37 /spec | |
parent | e44bb86539a8fb4cfb06dfe281632b6f206bd0a7 (diff) | |
download | gitlab-ce-2e3cbf7d89815e2915f77677388c49b48f8d20c3.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
19 files changed, 467 insertions, 448 deletions
diff --git a/spec/controllers/admin/clusters_controller_spec.rb b/spec/controllers/admin/clusters_controller_spec.rb index 4184c7e611a..f27519496df 100644 --- a/spec/controllers/admin/clusters_controller_spec.rb +++ b/spec/controllers/admin/clusters_controller_spec.rb @@ -381,10 +381,15 @@ describe Admin::ClustersController do post :authorize_aws_role, params: params end + before do + allow(Clusters::Aws::FetchCredentialsService).to receive(:new) + .and_return(double(execute: double)) + end + it 'creates an Aws::Role record' do expect { go }.to change { Aws::Role.count } - expect(response.status).to eq 201 + expect(response.status).to eq 200 role = Aws::Role.last expect(role.user).to eq admin @@ -409,27 +414,6 @@ describe Admin::ClustersController do end end - describe 'DELETE revoke AWS role for EKS cluster' do - let!(:role) { create(:aws_role, user: admin) } - - def go - delete :revoke_aws_role - end - - it 'deletes the Aws::Role record' do - expect { go }.to change { Aws::Role.count } - - expect(response.status).to eq 204 - expect(admin.reload_aws_role).to be_nil - 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 'DELETE clear cluster cache' do let(:cluster) { create(:cluster, :instance) } let!(:kubernetes_namespace) do diff --git a/spec/controllers/groups/clusters_controller_spec.rb b/spec/controllers/groups/clusters_controller_spec.rb index d47122f051e..cf90d388a61 100644 --- a/spec/controllers/groups/clusters_controller_spec.rb +++ b/spec/controllers/groups/clusters_controller_spec.rb @@ -443,10 +443,15 @@ describe Groups::ClustersController do post :authorize_aws_role, params: params.merge(group_id: group) end + before do + allow(Clusters::Aws::FetchCredentialsService).to receive(:new) + .and_return(double(execute: double)) + end + it 'creates an Aws::Role record' do expect { go }.to change { Aws::Role.count } - expect(response.status).to eq 201 + expect(response.status).to eq 200 role = Aws::Role.last expect(role.user).to eq user @@ -476,32 +481,6 @@ describe Groups::ClustersController do end end - describe 'DELETE revoke AWS role for EKS cluster' do - let!(:role) { create(:aws_role, user: user) } - - def go - delete :revoke_aws_role, params: { group_id: group } - end - - it 'deletes the Aws::Role record' do - expect { go }.to change { Aws::Role.count } - - expect(response.status).to eq 204 - expect(user.reload_aws_role).to be_nil - 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 'DELETE clear cluster cache' do let(:cluster) { create(:cluster, :group, groups: [group]) } let!(:kubernetes_namespace) do diff --git a/spec/controllers/projects/clusters_controller_spec.rb b/spec/controllers/projects/clusters_controller_spec.rb index 5efac44615f..ab8bfc0cabe 100644 --- a/spec/controllers/projects/clusters_controller_spec.rb +++ b/spec/controllers/projects/clusters_controller_spec.rb @@ -444,10 +444,15 @@ describe Projects::ClustersController do post :authorize_aws_role, params: params.merge(namespace_id: project.namespace, project_id: project) end + before do + allow(Clusters::Aws::FetchCredentialsService).to receive(:new) + .and_return(double(execute: double)) + end + it 'creates an Aws::Role record' do expect { go }.to change { Aws::Role.count } - expect(response.status).to eq 201 + expect(response.status).to eq 200 role = Aws::Role.last expect(role.user).to eq user @@ -477,32 +482,6 @@ describe Projects::ClustersController do end end - describe 'DELETE revoke AWS role for EKS cluster' do - let!(:role) { create(:aws_role, user: user) } - - def go - delete :revoke_aws_role, params: { namespace_id: project.namespace, project_id: project } - end - - it 'deletes the Aws::Role record' do - expect { go }.to change { Aws::Role.count } - - expect(response.status).to eq 204 - expect(user.reload_aws_role).to be_nil - 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 'DELETE clear cluster cache' do let(:cluster) { create(:cluster, :project, projects: [project]) } let!(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, cluster: cluster) } diff --git a/spec/controllers/projects/deploy_keys_controller_spec.rb b/spec/controllers/projects/deploy_keys_controller_spec.rb index 8b1ca2efab2..2c7c99eabf6 100644 --- a/spec/controllers/projects/deploy_keys_controller_spec.rb +++ b/spec/controllers/projects/deploy_keys_controller_spec.rb @@ -46,17 +46,27 @@ describe Projects::DeployKeysController do create(:deploy_keys_project, project: project_private, deploy_key: create(:another_deploy_key)) end - before do - project2.add_developer(user) + context 'when user has access to all projects where deploy keys are used' do + before do + project2.add_developer(user) + end + + it 'returns json in a correct format' do + get :index, params: params.merge(format: :json) + + expect(json_response.keys).to match_array(%w(enabled_keys available_project_keys public_keys)) + expect(json_response['enabled_keys'].count).to eq(1) + expect(json_response['available_project_keys'].count).to eq(1) + expect(json_response['public_keys'].count).to eq(1) + end end - it 'returns json in a correct format' do - get :index, params: params.merge(format: :json) + context 'when user has no access to all projects where deploy keys are used' do + it 'returns json in a correct format' do + get :index, params: params.merge(format: :json) - expect(json_response.keys).to match_array(%w(enabled_keys available_project_keys public_keys)) - expect(json_response['enabled_keys'].count).to eq(1) - expect(json_response['available_project_keys'].count).to eq(1) - expect(json_response['public_keys'].count).to eq(1) + expect(json_response['available_project_keys'].count).to eq(0) + end end end end diff --git a/spec/controllers/projects/error_tracking_controller_spec.rb b/spec/controllers/projects/error_tracking_controller_spec.rb index ab99e44e4ca..e5585d7b52d 100644 --- a/spec/controllers/projects/error_tracking_controller_spec.rb +++ b/spec/controllers/projects/error_tracking_controller_spec.rb @@ -384,6 +384,10 @@ describe Projects::ErrorTrackingController do ).permit! end + subject(:get_stack_trace) do + get :stack_trace, params: issue_params(issue_id: issue_id, format: :json) + end + before do expect(ErrorTracking::IssueLatestEventService) .to receive(:new).with(project, user, permitted_params) @@ -398,7 +402,7 @@ describe Projects::ErrorTrackingController do end it 'returns no data' do - get :stack_trace, params: issue_params(issue_id: issue_id, format: :json) + get_stack_trace expect(response).to have_gitlab_http_status(:no_content) end @@ -408,16 +412,21 @@ describe Projects::ErrorTrackingController do before do expect(issue_stack_trace_service).to receive(:execute) .and_return(status: :success, latest_event: error_event) + + get_stack_trace end let(:error_event) { build(:error_tracking_error_event) } it 'returns an error' do - get :stack_trace, params: issue_params(issue_id: issue_id, format: :json) - expect(response).to have_gitlab_http_status(:ok) expect(response).to match_response_schema('error_tracking/issue_stack_trace') - expect(json_response['error']).to eq(error_event.as_json) + end + + it 'highlights stack trace source code' do + expect(json_response['error']).to eq( + Gitlab::ErrorTracking::StackTraceHighlightDecorator.decorate(error_event).as_json + ) end end @@ -431,7 +440,7 @@ describe Projects::ErrorTrackingController do end it 'returns 400 with message' do - get :stack_trace, params: issue_params(issue_id: issue_id, format: :json) + get_stack_trace expect(response).to have_gitlab_http_status(:bad_request) expect(json_response['message']).to eq(error_message) @@ -450,7 +459,7 @@ describe Projects::ErrorTrackingController do end it 'returns http_status with message' do - get :stack_trace, params: issue_params(issue_id: issue_id, format: :json) + get_stack_trace expect(response).to have_gitlab_http_status(http_status) expect(json_response['message']).to eq(error_message) diff --git a/spec/factories/error_tracking/error_event.rb b/spec/factories/error_tracking/error_event.rb index 44c127e7bf5..c4dcd67bc9f 100644 --- a/spec/factories/error_tracking/error_event.rb +++ b/spec/factories/error_tracking/error_event.rb @@ -5,12 +5,40 @@ FactoryBot.define do issue_id { 'id' } date_received { Time.now.iso8601 } stack_trace_entries do - { - 'stacktrace' => - { - 'frames' => [{ 'file' => 'test.rb' }] - } - } + [ + { + 'function' => 'puts', + 'lineNo' => 14, + 'filename' => 'hello_world.rb', + 'context' => [ + [10, "# Ruby example\n"], + [11, "class HelloWorld\n"], + [12, " def self.message\n"], + [13, " @name = 'World'\n"], + [14, " puts \"Hello \#{@name}\"\n"], + [15, " end\n"], + [16, "end\n"] + ] + }, + { + 'function' => 'print', + 'lineNo' => 6, + 'filename' => 'HelloWorld.swift', + 'context' => [ + [1, "// Swift example\n"], + [2, "struct HelloWorld {\n"], + [3, " let name = \"World\"\n"], + [4, "\n"], + [5, " static func message() {\n"], + [6, " print(\"Hello, \\(self.name)\")\n"], + [7, " }\n"], + [8, "}\n"] + ] + }, + { + 'filename' => 'blank.txt' + } + ] end skip_create diff --git a/spec/fixtures/api/schemas/error_tracking/error_stack_trace.json b/spec/fixtures/api/schemas/error_tracking/error_stack_trace.json index a684dd0496a..e2eeefcdd53 100644 --- a/spec/fixtures/api/schemas/error_tracking/error_stack_trace.json +++ b/spec/fixtures/api/schemas/error_tracking/error_stack_trace.json @@ -7,7 +7,7 @@ ], "properties": { "issue_id": { "type": ["string", "integer"] }, - "stack_trace_entries": { "type": "object" }, + "stack_trace_entries": { "type": "array" }, "date_received": { "type": "string" } }, "additionalProperties": false diff --git a/spec/frontend/create_cluster/eks_cluster/components/eks_cluster_configuration_form_spec.js b/spec/frontend/create_cluster/eks_cluster/components/eks_cluster_configuration_form_spec.js index 25d613d64ed..d3992c6751c 100644 --- a/spec/frontend/create_cluster/eks_cluster/components/eks_cluster_configuration_form_spec.js +++ b/spec/frontend/create_cluster/eks_cluster/components/eks_cluster_configuration_form_spec.js @@ -27,13 +27,11 @@ describe('EksClusterConfigurationForm', () => { let subnetsActions; let keyPairsActions; let securityGroupsActions; - let instanceTypesActions; let vm; beforeEach(() => { state = eksClusterFormState(); actions = { - signOut: jest.fn(), createCluster: jest.fn(), setClusterName: jest.fn(), setEnvironmentScope: jest.fn(), @@ -66,9 +64,6 @@ describe('EksClusterConfigurationForm', () => { securityGroupsActions = { fetchItems: jest.fn(), }; - instanceTypesActions = { - fetchItems: jest.fn(), - }; rolesState = { ...clusterDropdownStoreState(), }; @@ -127,7 +122,6 @@ describe('EksClusterConfigurationForm', () => { instanceTypes: { namespaced: true, state: instanceTypesState, - actions: instanceTypesActions, }, }, }); @@ -164,7 +158,6 @@ describe('EksClusterConfigurationForm', () => { }); }; - const findSignOutButton = () => vm.find('.js-sign-out'); const findCreateClusterButton = () => vm.find('.js-create-cluster'); const findClusterNameInput = () => vm.find('[id=eks-cluster-name]'); const findEnvironmentScopeInput = () => vm.find('[id=eks-environment-scope]'); @@ -187,15 +180,6 @@ describe('EksClusterConfigurationForm', () => { it('fetches available roles', () => { expect(rolesActions.fetchItems).toHaveBeenCalled(); }); - - it('fetches available instance types', () => { - expect(instanceTypesActions.fetchItems).toHaveBeenCalled(); - }); - }); - - it('dispatches signOut action when sign out button is clicked', () => { - findSignOutButton().trigger('click'); - expect(actions.signOut).toHaveBeenCalled(); }); it('sets isLoadingRoles to RoleDropdown loading property', () => { diff --git a/spec/frontend/create_cluster/eks_cluster/services/aws_services_facade_spec.js b/spec/frontend/create_cluster/eks_cluster/services/aws_services_facade_spec.js index 146bcc04569..490a2775b67 100644 --- a/spec/frontend/create_cluster/eks_cluster/services/aws_services_facade_spec.js +++ b/spec/frontend/create_cluster/eks_cluster/services/aws_services_facade_spec.js @@ -1,42 +1,62 @@ -import AxiosMockAdapter from 'axios-mock-adapter'; -import awsServicesFacadeFactory from '~/create_cluster/eks_cluster/services/aws_services_facade'; -import axios from '~/lib/utils/axios_utils'; +import AWS from 'aws-sdk/global'; +import EC2 from 'aws-sdk/clients/ec2'; +import { + setAWSConfig, + fetchRoles, + fetchRegions, + fetchKeyPairs, + fetchVpcs, + fetchSubnets, + fetchSecurityGroups, + DEFAULT_REGION, +} from '~/create_cluster/eks_cluster/services/aws_services_facade'; + +const mockListRolesPromise = jest.fn(); +const mockDescribeRegionsPromise = jest.fn(); +const mockDescribeKeyPairsPromise = jest.fn(); +const mockDescribeVpcsPromise = jest.fn(); +const mockDescribeSubnetsPromise = jest.fn(); +const mockDescribeSecurityGroupsPromise = jest.fn(); + +jest.mock('aws-sdk/clients/iam', () => + jest.fn().mockImplementation(() => ({ + listRoles: jest.fn().mockReturnValue({ promise: mockListRolesPromise }), + })), +); + +jest.mock('aws-sdk/clients/ec2', () => + jest.fn().mockImplementation(() => ({ + describeRegions: jest.fn().mockReturnValue({ promise: mockDescribeRegionsPromise }), + describeKeyPairs: jest.fn().mockReturnValue({ promise: mockDescribeKeyPairsPromise }), + describeVpcs: jest.fn().mockReturnValue({ promise: mockDescribeVpcsPromise }), + describeSubnets: jest.fn().mockReturnValue({ promise: mockDescribeSubnetsPromise }), + describeSecurityGroups: jest + .fn() + .mockReturnValue({ promise: mockDescribeSecurityGroupsPromise }), + })), +); describe('awsServicesFacade', () => { - let apiPaths; - let axiosMock; - let awsServices; let region; let vpc; beforeEach(() => { - apiPaths = { - getKeyPairsPath: '/clusters/aws/api/key_pairs', - getRegionsPath: '/clusters/aws/api/regions', - getRolesPath: '/clusters/aws/api/roles', - getSecurityGroupsPath: '/clusters/aws/api/security_groups', - getSubnetsPath: '/clusters/aws/api/subnets', - getVpcsPath: '/clusters/aws/api/vpcs', - getInstanceTypesPath: '/clusters/aws/api/instance_types', - }; region = 'west-1'; vpc = 'vpc-2'; - awsServices = awsServicesFacadeFactory(apiPaths); - axiosMock = new AxiosMockAdapter(axios); }); - describe('when fetchRegions succeeds', () => { - let regions; - let regionsOutput; + it('setAWSConfig configures AWS SDK with provided credentials and default region', () => { + const awsCredentials = { + accessKeyId: 'access-key', + secretAccessKey: 'secret-key', + sessionToken: 'session-token', + }; - beforeEach(() => { - regions = [{ region_name: 'east-1' }, { region_name: 'west-2' }]; - regionsOutput = regions.map(({ region_name: name }) => ({ name, value: name })); - axiosMock.onGet(apiPaths.getRegionsPath).reply(200, { regions }); - }); + setAWSConfig({ awsCredentials }); - it('return list of roles where each item has a name and value', () => { - expect(awsServices.fetchRegions()).resolves.toEqual(regionsOutput); + expect(AWS.config).toEqual({ + ...awsCredentials, + region: DEFAULT_REGION, }); }); @@ -46,15 +66,32 @@ describe('awsServicesFacade', () => { beforeEach(() => { roles = [ - { role_name: 'admin', arn: 'aws::admin' }, - { role_name: 'read-only', arn: 'aws::read-only' }, + { RoleName: 'admin', Arn: 'aws::admin' }, + { RoleName: 'read-only', Arn: 'aws::read-only' }, ]; - rolesOutput = roles.map(({ role_name: name, arn: value }) => ({ name, value })); - axiosMock.onGet(apiPaths.getRolesPath).reply(200, { roles }); + rolesOutput = roles.map(({ RoleName: name, Arn: value }) => ({ name, value })); + + mockListRolesPromise.mockResolvedValueOnce({ Roles: roles }); }); it('return list of regions where each item has a name and value', () => { - expect(awsServices.fetchRoles()).resolves.toEqual(rolesOutput); + expect(fetchRoles()).resolves.toEqual(rolesOutput); + }); + }); + + describe('when fetchRegions succeeds', () => { + let regions; + let regionsOutput; + + beforeEach(() => { + regions = [{ RegionName: 'east-1' }, { RegionName: 'west-2' }]; + regionsOutput = regions.map(({ RegionName: name }) => ({ name, value: name })); + + mockDescribeRegionsPromise.mockResolvedValueOnce({ Regions: regions }); + }); + + it('return list of roles where each item has a name and value', () => { + expect(fetchRegions()).resolves.toEqual(regionsOutput); }); }); @@ -63,15 +100,19 @@ describe('awsServicesFacade', () => { let keyPairsOutput; beforeEach(() => { - keyPairs = [{ key_pair: 'key-pair' }, { key_pair: 'key-pair-2' }]; - keyPairsOutput = keyPairs.map(({ key_name: name }) => ({ name, value: name })); - axiosMock - .onGet(apiPaths.getKeyPairsPath, { params: { region } }) - .reply(200, { key_pairs: keyPairs }); + keyPairs = [{ KeyName: 'key-pair' }, { KeyName: 'key-pair-2' }]; + keyPairsOutput = keyPairs.map(({ KeyName: name }) => ({ name, value: name })); + + mockDescribeKeyPairsPromise.mockResolvedValueOnce({ KeyPairs: keyPairs }); + }); + + it('instantatiates ec2 service with provided region', () => { + fetchKeyPairs({ region }); + expect(EC2).toHaveBeenCalledWith({ region }); }); it('return list of key pairs where each item has a name and value', () => { - expect(awsServices.fetchKeyPairs({ region })).resolves.toEqual(keyPairsOutput); + expect(fetchKeyPairs({ region })).resolves.toEqual(keyPairsOutput); }); }); @@ -80,13 +121,37 @@ describe('awsServicesFacade', () => { let vpcsOutput; beforeEach(() => { - vpcs = [{ vpc_id: 'vpc-1' }, { vpc_id: 'vpc-2' }]; - vpcsOutput = vpcs.map(({ vpc_id: name }) => ({ name, value: name })); - axiosMock.onGet(apiPaths.getVpcsPath, { params: { region } }).reply(200, { vpcs }); + vpcs = [{ VpcId: 'vpc-1', Tags: [] }, { VpcId: 'vpc-2', Tags: [] }]; + vpcsOutput = vpcs.map(({ VpcId: vpcId }) => ({ name: vpcId, value: vpcId })); + + mockDescribeVpcsPromise.mockResolvedValueOnce({ Vpcs: vpcs }); + }); + + it('instantatiates ec2 service with provided region', () => { + fetchVpcs({ region }); + expect(EC2).toHaveBeenCalledWith({ region }); }); it('return list of vpcs where each item has a name and value', () => { - expect(awsServices.fetchVpcs({ region })).resolves.toEqual(vpcsOutput); + expect(fetchVpcs({ region })).resolves.toEqual(vpcsOutput); + }); + }); + + describe('when vpcs has a Name tag', () => { + const vpcName = 'vpc name'; + const vpcId = 'vpc id'; + let vpcs; + let vpcsOutput; + + beforeEach(() => { + vpcs = [{ VpcId: vpcId, Tags: [{ Key: 'Name', Value: vpcName }] }]; + vpcsOutput = [{ name: vpcName, value: vpcId }]; + + mockDescribeVpcsPromise.mockResolvedValueOnce({ Vpcs: vpcs }); + }); + + it('uses name tag value as the vpc name', () => { + expect(fetchVpcs({ region })).resolves.toEqual(vpcsOutput); }); }); @@ -95,15 +160,14 @@ describe('awsServicesFacade', () => { let subnetsOutput; beforeEach(() => { - subnets = [{ subnet_id: 'vpc-1' }, { subnet_id: 'vpc-2' }]; - subnetsOutput = subnets.map(({ subnet_id }) => ({ name: subnet_id, value: subnet_id })); - axiosMock - .onGet(apiPaths.getSubnetsPath, { params: { region, vpc_id: vpc } }) - .reply(200, { subnets }); + subnets = [{ SubnetId: 'subnet-1' }, { SubnetId: 'subnet-2' }]; + subnetsOutput = subnets.map(({ SubnetId }) => ({ name: SubnetId, value: SubnetId })); + + mockDescribeSubnetsPromise.mockResolvedValueOnce({ Subnets: subnets }); }); it('return list of subnets where each item has a name and value', () => { - expect(awsServices.fetchSubnets({ region, vpc })).resolves.toEqual(subnetsOutput); + expect(fetchSubnets({ region, vpc })).resolves.toEqual(subnetsOutput); }); }); @@ -113,40 +177,19 @@ describe('awsServicesFacade', () => { beforeEach(() => { securityGroups = [ - { group_name: 'admin group', group_id: 'group-1' }, - { group_name: 'basic group', group_id: 'group-2' }, + { GroupName: 'admin group', GroupId: 'group-1' }, + { GroupName: 'basic group', GroupId: 'group-2' }, ]; - securityGroupsOutput = securityGroups.map(({ group_id: value, group_name: name }) => ({ + securityGroupsOutput = securityGroups.map(({ GroupId: value, GroupName: name }) => ({ name, value, })); - axiosMock - .onGet(apiPaths.getSecurityGroupsPath, { params: { region, vpc_id: vpc } }) - .reply(200, { security_groups: securityGroups }); - }); - it('return list of security groups where each item has a name and value', () => { - expect(awsServices.fetchSecurityGroups({ region, vpc })).resolves.toEqual( - securityGroupsOutput, - ); + mockDescribeSecurityGroupsPromise.mockResolvedValueOnce({ SecurityGroups: securityGroups }); }); - }); - - describe('when fetchInstanceTypes succeeds', () => { - let instanceTypes; - let instanceTypesOutput; - beforeEach(() => { - instanceTypes = [{ instance_type_name: 't2.small' }, { instance_type_name: 't2.medium' }]; - instanceTypesOutput = instanceTypes.map(({ instance_type_name }) => ({ - name: instance_type_name, - value: instance_type_name, - })); - axiosMock.onGet(apiPaths.getInstanceTypesPath).reply(200, { instance_types: instanceTypes }); - }); - - it('return list of instance types where each item has a name and value', () => { - expect(awsServices.fetchInstanceTypes()).resolves.toEqual(instanceTypesOutput); + it('return list of security groups where each item has a name and value', () => { + expect(fetchSecurityGroups({ region, vpc })).resolves.toEqual(securityGroupsOutput); }); }); }); 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 578d82cfc26..fda1f71b1f9 100644 --- a/spec/frontend/create_cluster/eks_cluster/store/actions_spec.js +++ b/spec/frontend/create_cluster/eks_cluster/store/actions_spec.js @@ -1,5 +1,4 @@ import testAction from 'helpers/vuex_action_helper'; - import MockAdapter from 'axios-mock-adapter'; import createState from '~/create_cluster/eks_cluster/store/state'; import * as actions from '~/create_cluster/eks_cluster/store/actions'; @@ -21,7 +20,6 @@ import { CREATE_ROLE_ERROR, REQUEST_CREATE_CLUSTER, CREATE_CLUSTER_ERROR, - SIGN_OUT, } from '~/create_cluster/eks_cluster/store/mutation_types'; import axios from '~/lib/utils/axios_utils'; import createFlash from '~/flash'; @@ -64,7 +62,6 @@ describe('EKS Cluster Store Actions', () => { state = { ...createState(), createRolePath: '/clusters/roles/', - signOutPath: '/aws/signout', createClusterPath: '/clusters/', }; }); @@ -102,6 +99,10 @@ describe('EKS Cluster Store Actions', () => { roleArn: 'role_arn', externalId: 'externalId', }; + const response = { + accessKeyId: 'access-key-id', + secretAccessKey: 'secret-key-id', + }; describe('when request succeeds', () => { beforeEach(() => { @@ -110,7 +111,7 @@ describe('EKS Cluster Store Actions', () => { role_arn: payload.roleArn, role_external_id: payload.externalId, }) - .reply(201); + .reply(201, response); }); it('dispatches createRoleSuccess action', () => @@ -119,7 +120,7 @@ describe('EKS Cluster Store Actions', () => { payload, state, [], - [{ type: 'requestCreateRole' }, { type: 'createRoleSuccess' }], + [{ type: 'requestCreateRole' }, { type: 'createRoleSuccess', payload: response }], )); }); @@ -281,14 +282,4 @@ describe('EKS Cluster Store Actions', () => { expect(createFlash).toHaveBeenCalledWith(payload.name[0]); }); }); - - describe('signOut', () => { - beforeEach(() => { - mock.onDelete(state.signOutPath).reply(200, null); - }); - - it('commits signOut mutation', () => { - testAction(actions.signOut, null, state, [{ type: SIGN_OUT }]); - }); - }); }); 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 0fb392f5eea..8bb014d4758 100644 --- a/spec/frontend/create_cluster/eks_cluster/store/mutations_spec.js +++ b/spec/frontend/create_cluster/eks_cluster/store/mutations_spec.js @@ -16,7 +16,6 @@ import { CREATE_ROLE_ERROR, REQUEST_CREATE_CLUSTER, CREATE_CLUSTER_ERROR, - SIGN_OUT, } 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'; @@ -159,15 +158,4 @@ describe('Create EKS cluster store mutations', () => { expect(state.createClusterError).toBe(error); }); }); - - describe(`mutation ${SIGN_OUT}`, () => { - beforeEach(() => { - state.hasCredentials = true; - mutations[SIGN_OUT](state); - }); - - it('sets hasCredentials to false', () => { - expect(state.hasCredentials).toBe(false); - }); - }); }); diff --git a/spec/lib/gitlab/error_tracking/stack_trace_highlight_decorator_spec.rb b/spec/lib/gitlab/error_tracking/stack_trace_highlight_decorator_spec.rb new file mode 100644 index 00000000000..04ef5ba516e --- /dev/null +++ b/spec/lib/gitlab/error_tracking/stack_trace_highlight_decorator_spec.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::ErrorTracking::StackTraceHighlightDecorator do + let(:error_event) { build(:error_tracking_error_event) } + + describe '.decorate' do + subject(:decorate) { described_class.decorate(error_event) } + + it 'does not change issue_id' do + expect(decorate.issue_id).to eq(error_event.issue_id) + end + + it 'does not change date_received' do + expect(decorate.date_received).to eq(error_event.date_received) + end + + it 'decorates the stack trace context' do + expect(decorate.stack_trace_entries).to eq( + [ + { + 'function' => 'puts', + 'lineNo' => 14, + 'filename' => 'hello_world.rb', + 'context' => [ + [10, '<span id="LC1" class="line" lang="ruby"><span class="c1"># Ruby example</span></span>'], + [11, '<span id="LC1" class="line" lang="ruby"><span class="k">class</span> <span class="nc">HelloWorld</span></span>'], + [12, '<span id="LC1" class="line" lang="ruby"> <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">message</span></span>'], + [13, '<span id="LC1" class="line" lang="ruby"> <span class="vi">@name</span> <span class="o">=</span> <span class="s1">\'World\'</span></span>'], + [14, %Q[<span id="LC1" class="line" lang="ruby"> <span class="nb">puts</span> <span class="s2">"Hello </span><span class="si">\#{</span><span class="vi">@name</span><span class="si">}</span><span class="s2">"</span></span>]], + [15, '<span id="LC1" class="line" lang="ruby"> <span class="k">end</span></span>'], + [16, '<span id="LC1" class="line" lang="ruby"><span class="k">end</span></span>'] + ] + }, + { + 'function' => 'print', + 'lineNo' => 6, + 'filename' => 'HelloWorld.swift', + 'context' => [ + [1, '<span id="LC1" class="line" lang="swift"><span class="c1">// Swift example</span></span>'], + [2, '<span id="LC1" class="line" lang="swift"><span class="kd">struct</span> <span class="kt">HelloWorld</span> <span class="p">{</span></span>'], + [3, '<span id="LC1" class="line" lang="swift"> <span class="k">let</span> <span class="nv">name</span> <span class="o">=</span> <span class="s">"World"</span></span>'], + [4, '<span id="LC1" class="line" lang="swift"></span>'], + [5, '<span id="LC1" class="line" lang="swift"> <span class="kd">static</span> <span class="kd">func</span> <span class="nf">message</span><span class="p">()</span> <span class="p">{</span></span>'], + [6, '<span id="LC1" class="line" lang="swift"> <span class="nf">print</span><span class="p">(</span><span class="s">"Hello, </span><span class="se">\\(</span><span class="k">self</span><span class="o">.</span><span class="n">name</span><span class="se">)</span><span class="s">"</span><span class="p">)</span></span>'], + [7, '<span id="LC1" class="line" lang="swift"> <span class="p">}</span></span>'], + [8, '<span id="LC1" class="line" lang="swift"><span class="p">}</span></span>'] + ] + }, + { + 'filename' => 'blank.txt' + } + ] + ) + end + end +end diff --git a/spec/presenters/instance_clusterable_presenter_spec.rb b/spec/presenters/instance_clusterable_presenter_spec.rb index 3e7ee7a0ff6..4265e2fcb69 100644 --- a/spec/presenters/instance_clusterable_presenter_spec.rb +++ b/spec/presenters/instance_clusterable_presenter_spec.rb @@ -21,20 +21,6 @@ describe InstanceClusterablePresenter do it { is_expected.to eq(authorize_aws_role_admin_clusters_path) } end - describe '#revoke_aws_role_path' do - subject { described_class.new(instance).revoke_aws_role_path } - - it { is_expected.to eq(revoke_aws_role_admin_clusters_path) } - end - - describe '#aws_api_proxy_path' do - let(:resource) { 'resource' } - - subject { described_class.new(instance).aws_api_proxy_path(resource) } - - it { is_expected.to eq(aws_proxy_admin_clusters_path(resource: resource)) } - end - describe '#clear_cluster_cache_path' do subject { presenter.clear_cluster_cache_path(cluster) } diff --git a/spec/presenters/projects/settings/deploy_keys_presenter_spec.rb b/spec/presenters/projects/settings/deploy_keys_presenter_spec.rb index de58733c8ea..b9cb60e414f 100644 --- a/spec/presenters/projects/settings/deploy_keys_presenter_spec.rb +++ b/spec/presenters/projects/settings/deploy_keys_presenter_spec.rb @@ -5,11 +5,6 @@ require 'spec_helper' describe Projects::Settings::DeployKeysPresenter do let(:project) { create(:project) } let(:user) { create(:user) } - let(:deploy_key) { create(:deploy_key, public: true) } - - let!(:deploy_keys_project) do - create(:deploy_keys_project, project: project, deploy_key: deploy_key) - end subject(:presenter) do described_class.new(project, current_user: user) @@ -20,6 +15,12 @@ describe Projects::Settings::DeployKeysPresenter do end describe '#enabled_keys' do + let!(:deploy_key) { create(:deploy_key, public: true) } + + let!(:deploy_keys_project) do + create(:deploy_keys_project, project: project, deploy_key: deploy_key) + end + it 'returns currently enabled keys' do expect(presenter.enabled_keys).to eq [deploy_keys_project.deploy_key] end @@ -53,4 +54,54 @@ describe Projects::Settings::DeployKeysPresenter do expect(presenter.available_project_keys_size).to eq(1) end end + + context 'prevent N + 1 queries' do + before do + create_records + + project.add_maintainer(user) + end + + def create_records + other_project = create(:project) + other_project.add_maintainer(user) + + create(:deploy_keys_project, project: project, deploy_key: create(:deploy_key)) + create(:deploy_keys_project, project: other_project, deploy_key: create(:deploy_key)) + create(:deploy_key, public: true) + end + + def execute_with_query_count + ActiveRecord::QueryRecorder.new { execute_presenter }.count + end + + def execute_presenter + described_class.new(project, current_user: user).as_json + end + + it 'returns correct counts' do + result = execute_presenter + + expect(result[:enabled_keys].size).to eq(1) + expect(result[:available_project_keys].size).to eq(1) + expect(result[:public_keys].size).to eq(1) + end + + it 'does not increase the query count' do + execute_presenter # make sure everything is cached + + count_before = execute_with_query_count + + 3.times { create_records } + + count_after = execute_with_query_count + + expect(count_after).to eq(count_before) + + result = execute_presenter + expect(result[:enabled_keys].size).to eq(4) + expect(result[:available_project_keys].size).to eq(4) + expect(result[:public_keys].size).to eq(4) + end + end end diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb index 1a1e80f1ce3..29088a42fbe 100644 --- a/spec/requests/api/users_spec.rb +++ b/spec/requests/api/users_spec.rb @@ -1261,6 +1261,25 @@ describe API::Users do expect { Namespace.find(namespace.id) }.to raise_error ActiveRecord::RecordNotFound end + context "sole owner of a group" do + let!(:group) { create(:group).tap { |group| group.add_owner(user) } } + + context "hard delete disabled" do + it "does not delete user" do + perform_enqueued_jobs { delete api("/users/#{user.id}", admin)} + expect(response).to have_gitlab_http_status(409) + end + end + + context "hard delete enabled" do + it "delete user and group", :sidekiq_might_not_need_inline do + perform_enqueued_jobs { delete api("/users/#{user.id}?hard_delete=true", admin)} + expect(response).to have_gitlab_http_status(204) + expect(Group.exists?(group.id)).to be_falsy + end + end + end + it_behaves_like '412 response' do let(:request) { api("/users/#{user.id}", admin) } end diff --git a/spec/services/clusters/aws/authorize_role_service_spec.rb b/spec/services/clusters/aws/authorize_role_service_spec.rb new file mode 100644 index 00000000000..3ef332558a2 --- /dev/null +++ b/spec/services/clusters/aws/authorize_role_service_spec.rb @@ -0,0 +1,91 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Clusters::Aws::AuthorizeRoleService do + let(:user) { create(:user) } + let(:credentials) { instance_double(Aws::Credentials) } + let(:credentials_service) { instance_double(Clusters::Aws::FetchCredentialsService, execute: credentials) } + + let(:params) do + params = ActionController::Parameters.new({ + cluster: { + role_arn: 'arn:my-role', + role_external_id: 'external-id' + } + }) + + params.require(:cluster).permit(:role_arn, :role_external_id) + end + + subject { described_class.new(user, params: params).execute } + + before do + allow(Clusters::Aws::FetchCredentialsService).to receive(:new) + .with(instance_of(Aws::Role)).and_return(credentials_service) + end + + context 'role does not exist' do + it 'creates an Aws::Role record and returns a set of credentials' do + expect(user).to receive(:create_aws_role!) + .with(params).and_call_original + + expect(subject.status).to eq(:ok) + expect(subject.body).to eq(credentials) + end + end + + context 'role already exists' do + let(:role) { create(:aws_role, user: user) } + + it 'updates the existing Aws::Role record and returns a set of credentials' do + expect(role).to receive(:update!) + .with(params).and_call_original + + expect(subject.status).to eq(:ok) + expect(subject.body).to eq(credentials) + end + end + + context 'errors' do + shared_examples 'bad request' do + it 'returns an empty hash' do + expect(subject.status).to eq(:unprocessable_entity) + expect(subject.body).to eq({}) + end + end + + context 'cannot create role' do + before do + allow(user).to receive(:create_aws_role!) + .and_raise(ActiveRecord::RecordInvalid.new(user)) + end + + include_examples 'bad request' + end + + context 'client errors' do + before do + allow(credentials_service).to receive(:execute).and_raise(error) + end + + context 'error fetching credentials' do + let(:error) { Aws::STS::Errors::ServiceError.new(nil, 'error message') } + + include_examples 'bad request' + end + + context 'credentials not configured' do + let(:error) { Aws::Errors::MissingCredentialsError.new('error message') } + + include_examples 'bad request' + end + + context 'role not configured' do + let(:error) { Clusters::Aws::FetchCredentialsService::MissingRoleError.new('error message') } + + include_examples 'bad request' + end + end + end +end diff --git a/spec/services/clusters/aws/fetch_credentials_service_spec.rb b/spec/services/clusters/aws/fetch_credentials_service_spec.rb index 726d1c30603..9194947c67f 100644 --- a/spec/services/clusters/aws/fetch_credentials_service_spec.rb +++ b/spec/services/clusters/aws/fetch_credentials_service_spec.rb @@ -5,19 +5,18 @@ require 'spec_helper' describe Clusters::Aws::FetchCredentialsService do describe '#execute' do let(:user) { create(:user) } - let(:provider) { create(:cluster_provider_aws) } + let(:provider) { create(:cluster_provider_aws, region: 'ap-southeast-2') } let(:gitlab_access_key_id) { 'gitlab-access-key-id' } let(:gitlab_secret_access_key) { 'gitlab-secret-access-key' } - let(:region) { 'us-east-1' } let(:gitlab_credentials) { Aws::Credentials.new(gitlab_access_key_id, gitlab_secret_access_key) } let(:sts_client) { Aws::STS::Client.new(credentials: gitlab_credentials, region: region) } let(:assumed_role) { instance_double(Aws::AssumeRoleCredentials, credentials: assumed_role_credentials) } let(:assumed_role_credentials) { double } - subject { described_class.new(provision_role, region: region, provider: provider).execute } + subject { described_class.new(provision_role, provider: provider).execute } context 'provision role is configured' do let(:provision_role) { create(:aws_role, user: user) } @@ -39,19 +38,30 @@ describe Clusters::Aws::FetchCredentialsService do client: sts_client, role_arn: provision_role.role_arn, role_session_name: session_name, - external_id: provision_role.role_external_id + external_id: provision_role.role_external_id, + policy: session_policy ).and_return(assumed_role) end context 'provider is specified' do + let(:region) { provider.region } let(:session_name) { "gitlab-eks-cluster-#{provider.cluster_id}-user-#{user.id}" } + let(:session_policy) { nil } it { is_expected.to eq assumed_role_credentials } end context 'provider is not specifed' do let(:provider) { nil } + let(:region) { Clusters::Providers::Aws::DEFAULT_REGION } let(:session_name) { "gitlab-eks-autofill-user-#{user.id}" } + let(:session_policy) { 'policy-document' } + + before do + allow(File).to receive(:read) + .with(Rails.root.join('vendor', 'aws', 'iam', 'eks_cluster_read_only_policy.json')) + .and_return(session_policy) + end it { is_expected.to eq assumed_role_credentials } end diff --git a/spec/services/clusters/aws/proxy_service_spec.rb b/spec/services/clusters/aws/proxy_service_spec.rb deleted file mode 100644 index 7b0e0512b95..00000000000 --- a/spec/services/clusters/aws/proxy_service_spec.rb +++ /dev/null @@ -1,210 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -describe Clusters::Aws::ProxyService do - let(:role) { create(:aws_role) } - let(:credentials) { instance_double(Aws::Credentials) } - let(:client_instance) { instance_double(client) } - - let(:region) { 'region' } - let(:vpc_id) { } - let(:params) do - ActionController::Parameters.new({ - resource: resource, - region: region, - vpc_id: vpc_id - }) - end - - subject { described_class.new(role, params: params).execute } - - context 'external resources' do - before do - allow(Clusters::Aws::FetchCredentialsService).to receive(:new) do - double(execute: credentials) - end - - allow(client).to receive(:new) - .with( - credentials: credentials, region: region, - http_open_timeout: 5, http_read_timeout: 10) - .and_return(client_instance) - end - - shared_examples 'bad request' do - it 'returns an empty hash' do - expect(subject.status).to eq :bad_request - expect(subject.body).to eq({}) - end - end - - describe 'key_pairs' do - let(:client) { Aws::EC2::Client } - let(:resource) { 'key_pairs' } - let(:response) { double(to_hash: :key_pairs) } - - it 'requests a list of key pairs' do - expect(client_instance).to receive(:describe_key_pairs).once.and_return(response) - expect(subject.status).to eq :ok - expect(subject.body).to eq :key_pairs - end - end - - describe 'roles' do - let(:client) { Aws::IAM::Client } - let(:resource) { 'roles' } - let(:response) { double(to_hash: :roles) } - - it 'requests a list of roles' do - expect(client_instance).to receive(:list_roles).once.and_return(response) - expect(subject.status).to eq :ok - expect(subject.body).to eq :roles - end - end - - describe 'regions' do - let(:client) { Aws::EC2::Client } - let(:resource) { 'regions' } - let(:response) { double(to_hash: :regions) } - - it 'requests a list of regions' do - expect(client_instance).to receive(:describe_regions).once.and_return(response) - expect(subject.status).to eq :ok - expect(subject.body).to eq :regions - end - end - - describe 'security_groups' do - let(:client) { Aws::EC2::Client } - let(:resource) { 'security_groups' } - let(:response) { double(to_hash: :security_groups) } - - include_examples 'bad request' - - context 'VPC is specified' do - let(:vpc_id) { 'vpc-1' } - - it 'requests a list of security groups for a VPC' do - expect(client_instance).to receive(:describe_security_groups).once - .with(filters: [{ name: 'vpc-id', values: [vpc_id] }]) - .and_return(response) - expect(subject.status).to eq :ok - expect(subject.body).to eq :security_groups - end - end - end - - describe 'subnets' do - let(:client) { Aws::EC2::Client } - let(:resource) { 'subnets' } - let(:response) { double(to_hash: :subnets) } - - include_examples 'bad request' - - context 'VPC is specified' do - let(:vpc_id) { 'vpc-1' } - - it 'requests a list of subnets for a VPC' do - expect(client_instance).to receive(:describe_subnets).once - .with(filters: [{ name: 'vpc-id', values: [vpc_id] }]) - .and_return(response) - expect(subject.status).to eq :ok - expect(subject.body).to eq :subnets - end - end - end - - describe 'vpcs' do - let(:client) { Aws::EC2::Client } - let(:resource) { 'vpcs' } - let(:response) { double(to_hash: :vpcs) } - - it 'requests a list of VPCs' do - expect(client_instance).to receive(:describe_vpcs).once.and_return(response) - expect(subject.status).to eq :ok - expect(subject.body).to eq :vpcs - end - end - - context 'errors' do - let(:client) { Aws::EC2::Client } - - context 'unknown resource' do - let(:resource) { 'instances' } - - include_examples 'bad request' - end - - context 'client and configuration errors' do - let(:resource) { 'vpcs' } - - before do - allow(client_instance).to receive(:describe_vpcs).and_raise(error) - end - - context 'error fetching credentials' do - let(:error) { Aws::STS::Errors::ServiceError.new(nil, 'error message') } - - include_examples 'bad request' - end - - context 'credentials not configured' do - let(:error) { Aws::Errors::MissingCredentialsError.new('error message') } - - include_examples 'bad request' - end - - context 'role not configured' do - let(:error) { Clusters::Aws::FetchCredentialsService::MissingRoleError.new('error message') } - - include_examples 'bad request' - end - - context 'EC2 error' do - let(:error) { Aws::EC2::Errors::ServiceError.new(nil, 'error message') } - - include_examples 'bad request' - end - - context 'IAM error' do - let(:error) { Aws::IAM::Errors::ServiceError.new(nil, 'error message') } - - include_examples 'bad request' - end - - context 'STS error' do - let(:error) { Aws::STS::Errors::ServiceError.new(nil, 'error message') } - - include_examples 'bad request' - end - end - end - end - - context 'local resources' do - describe 'instance_types' do - let(:resource) { 'instance_types' } - let(:cloudformation_template) { double } - let(:instance_types) { double(dig: %w(t3.small)) } - - before do - allow(File).to receive(:read) - .with(Rails.root.join('vendor', 'aws', 'cloudformation', 'eks_cluster.yaml')) - .and_return(cloudformation_template) - - allow(YAML).to receive(:safe_load) - .with(cloudformation_template) - .and_return(instance_types) - end - - it 'returns a list of instance types' do - expect(subject.status).to eq :ok - expect(subject.body).to have_key(:instance_types) - expect(subject.body[:instance_types]).to match_array([ - instance_type_name: 't3.small' - ]) - end - end - end -end diff --git a/spec/services/clusters/kubernetes_spec.rb b/spec/services/clusters/kubernetes_spec.rb new file mode 100644 index 00000000000..7f2c5e0461d --- /dev/null +++ b/spec/services/clusters/kubernetes_spec.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Clusters::Kubernetes do + it { is_expected.to be_const_defined(:GITLAB_SERVICE_ACCOUNT_NAME) } + it { is_expected.to be_const_defined(:GITLAB_SERVICE_ACCOUNT_NAMESPACE) } + it { is_expected.to be_const_defined(:GITLAB_ADMIN_TOKEN_NAME) } + it { is_expected.to be_const_defined(:GITLAB_CLUSTER_ROLE_BINDING_NAME) } + it { is_expected.to be_const_defined(:GITLAB_CLUSTER_ROLE_NAME) } + it { is_expected.to be_const_defined(:PROJECT_CLUSTER_ROLE_NAME) } + it { is_expected.to be_const_defined(:GITLAB_KNATIVE_SERVING_ROLE_NAME) } + it { is_expected.to be_const_defined(:GITLAB_KNATIVE_SERVING_ROLE_BINDING_NAME) } + it { is_expected.to be_const_defined(:GITLAB_CROSSPLANE_DATABASE_ROLE_NAME) } + it { is_expected.to be_const_defined(:GITLAB_CROSSPLANE_DATABASE_ROLE_BINDING_NAME) } + it { is_expected.to be_const_defined(:GITLAB_KNATIVE_VERSION_ROLE_NAME) } + it { is_expected.to be_const_defined(:GITLAB_KNATIVE_VERSION_ROLE_BINDING_NAME) } + it { is_expected.to be_const_defined(:KNATIVE_SERVING_NAMESPACE) } +end |