Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEnrique Alcantara <ealcantara@gitlab.com>2019-09-10 18:32:23 +0300
committerEnrique Alcantara <ealcantara@gitlab.com>2019-09-12 18:25:31 +0300
commitbe1be5cb1defca6acc862ca87a30b689b79e5c1d (patch)
tree25b57d267c34e711e271d8e2a3007d9a1f84b89a
parent43e44d020479b711a87a00e93ee4906bf5ec2e02 (diff)
Load and display AWS regions
- Create dropdown component to display AWS regions - Include regions dropdown in create EKS cluster form - Create store actions to fetch AWS regions - Add method to fetch aws regions from aws-sdk
-rw-r--r--app/assets/javascripts/create_cluster/eks_cluster/components/eks_cluster_configuration_form.vue23
-rw-r--r--app/assets/javascripts/create_cluster/eks_cluster/components/region_dropdown.vue63
-rw-r--r--app/assets/javascripts/create_cluster/eks_cluster/index.js1
-rw-r--r--app/assets/javascripts/create_cluster/eks_cluster/services/aws_services_facade.js20
-rw-r--r--app/assets/javascripts/create_cluster/eks_cluster/store/actions.js22
-rw-r--r--app/assets/javascripts/create_cluster/eks_cluster/store/index.js2
-rw-r--r--app/assets/javascripts/create_cluster/eks_cluster/store/mutation_types.js3
-rw-r--r--app/assets/javascripts/create_cluster/eks_cluster/store/mutations.js16
-rw-r--r--app/assets/javascripts/create_cluster/eks_cluster/store/state.js8
-rw-r--r--locale/gitlab.pot22
-rw-r--r--spec/frontend/create_cluster/eks_cluster/components/eks_cluster_configuration_form_spec.js62
-rw-r--r--spec/frontend/create_cluster/eks_cluster/components/region_dropdown_spec.js64
-rw-r--r--spec/frontend/create_cluster/eks_cluster/store/actions_spec.js88
-rw-r--r--spec/frontend/create_cluster/eks_cluster/store/mutations_spec.js36
14 files changed, 426 insertions, 4 deletions
diff --git a/app/assets/javascripts/create_cluster/eks_cluster/components/eks_cluster_configuration_form.vue b/app/assets/javascripts/create_cluster/eks_cluster/components/eks_cluster_configuration_form.vue
index 6e74963dcb0..984c587b45a 100644
--- a/app/assets/javascripts/create_cluster/eks_cluster/components/eks_cluster_configuration_form.vue
+++ b/app/assets/javascripts/create_cluster/eks_cluster/components/eks_cluster_configuration_form.vue
@@ -1,4 +1,7 @@
<script>
+import { mapActions, mapState } from 'vuex';
+
+import RegionDropdown from './region_dropdown.vue';
import RoleNameDropdown from './role_name_dropdown.vue';
import SecurityGroupDropdown from './security_group_dropdown.vue';
import SubnetDropdown from './subnet_dropdown.vue';
@@ -6,11 +9,21 @@ import VPCDropdown from './vpc_dropdown.vue';
export default {
components: {
+ RegionDropdown,
RoleNameDropdown,
SecurityGroupDropdown,
SubnetDropdown,
VPCDropdown,
},
+ computed: {
+ ...mapState(['isLoadingRegions', 'loadingRegionsError', 'regions']),
+ },
+ mounted() {
+ this.fetchRegions();
+ },
+ methods: {
+ ...mapActions(['fetchRegions']),
+ },
};
</script>
<template>
@@ -21,5 +34,15 @@ export default {
</label>
<role-name-dropdown />
</div>
+ <div class="form-group">
+ <label class="label-bold" name="role" for="eks-role">
+ {{ s__('ClusterIntegration|Region') }}
+ </label>
+ <region-dropdown
+ :regions="regions"
+ :error="loadingRegionsError"
+ :loading="isLoadingRegions"
+ />
+ </div>
</form>
</template>
diff --git a/app/assets/javascripts/create_cluster/eks_cluster/components/region_dropdown.vue b/app/assets/javascripts/create_cluster/eks_cluster/components/region_dropdown.vue
new file mode 100644
index 00000000000..6e6e5b665a7
--- /dev/null
+++ b/app/assets/javascripts/create_cluster/eks_cluster/components/region_dropdown.vue
@@ -0,0 +1,63 @@
+<script>
+import { sprintf, s__ } from '~/locale';
+
+import ClusterFormDropdown from './cluster_form_dropdown.vue';
+
+export default {
+ components: {
+ ClusterFormDropdown,
+ },
+ props: {
+ regions: {
+ type: Array,
+ required: false,
+ default() {
+ return [];
+ },
+ },
+ loading: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ error: {
+ type: Object,
+ required: false,
+ default: null,
+ },
+ },
+ computed: {
+ hasErrors() {
+ return Boolean(this.error);
+ },
+ helpText() {
+ return sprintf(
+ s__('ClusterIntegration|Learn more about %{startLink}Regions%{endLink}.'),
+ {
+ startLink:
+ '<a href="https://aws.amazon.com/about-aws/global-infrastructure/regional-product-services/" target="_blank" rel="noopener noreferrer">',
+ endLink: '</a>',
+ },
+ false,
+ );
+ },
+ },
+};
+</script>
+<template>
+ <div>
+ <cluster-form-dropdown
+ field-id="eks-region"
+ field-name="eks-region"
+ :items="regions"
+ :loading="loading"
+ :loading-text="s__('ClusterIntegration|Loading Regions')"
+ :placeholder="s__('ClusterIntergation|Select a region')"
+ :search-field-placeholder="s__('ClusterIntegration|Search regions')"
+ :empty-text="s__('ClusterIntegration|No region found')"
+ :has-errors="hasErrors"
+ :error-message="s__('ClusterIntegration|Could not load regions from your AWS account')"
+ />
+ <p class="form-text text-muted" v-html="helpText"></p>
+ </div>
+</template>
diff --git a/app/assets/javascripts/create_cluster/eks_cluster/index.js b/app/assets/javascripts/create_cluster/eks_cluster/index.js
index c62e5ec101d..9365fc41f4c 100644
--- a/app/assets/javascripts/create_cluster/eks_cluster/index.js
+++ b/app/assets/javascripts/create_cluster/eks_cluster/index.js
@@ -12,7 +12,6 @@ export default () =>
components: {
CreateEksCluster,
},
- data() {},
render(createElement) {
return createElement('create-eks-cluster');
},
diff --git a/app/assets/javascripts/create_cluster/eks_cluster/services/aws_services_facade.js b/app/assets/javascripts/create_cluster/eks_cluster/services/aws_services_facade.js
index e69de29bb2d..5a13d32e0d2 100644
--- a/app/assets/javascripts/create_cluster/eks_cluster/services/aws_services_facade.js
+++ b/app/assets/javascripts/create_cluster/eks_cluster/services/aws_services_facade.js
@@ -0,0 +1,20 @@
+import EC2 from 'aws-sdk/clients/ec2';
+
+export const fetchRegions = () =>
+ new Promise((resolve, reject) => {
+ const ec2 = new EC2();
+
+ ec2
+ .describeRegions()
+ .on('success', ({ data: { Regions: regions } }) => {
+ const transformedRegions = regions.map(({ RegionName: name }) => ({ name }));
+
+ resolve(transformedRegions);
+ })
+ .on('error', error => {
+ reject(error);
+ })
+ .send();
+ });
+
+export default () => {};
diff --git a/app/assets/javascripts/create_cluster/eks_cluster/store/actions.js b/app/assets/javascripts/create_cluster/eks_cluster/store/actions.js
index 861bcddfcc7..5563c2bf61e 100644
--- a/app/assets/javascripts/create_cluster/eks_cluster/store/actions.js
+++ b/app/assets/javascripts/create_cluster/eks_cluster/store/actions.js
@@ -1,3 +1,23 @@
-// import awsServices from '../services/aws_services_facade';
+import * as awsServices from '../services/aws_services_facade';
+import * as types from './mutation_types';
+
+export const requestRegions = ({ commit }) => commit(types.REQUEST_REGIONS);
+
+export const receiveRegionsSuccess = ({ commit }, payload) => {
+ commit(types.RECEIVE_REGIONS_SUCCESS, payload);
+};
+
+export const receiveRegionsError = ({ commit }, payload) => {
+ commit(types.RECEIVE_REGIONS_ERROR, payload);
+};
+
+export const fetchRegions = ({ dispatch }) => {
+ dispatch('requestRegions');
+
+ return awsServices
+ .fetchRegions()
+ .then(regions => dispatch('receiveRegionsSuccess', { regions }))
+ .catch(error => dispatch('receiveRegionsError', { error }));
+};
export default () => {};
diff --git a/app/assets/javascripts/create_cluster/eks_cluster/store/index.js b/app/assets/javascripts/create_cluster/eks_cluster/store/index.js
index 99e9e35fd1a..8f5ad109c2f 100644
--- a/app/assets/javascripts/create_cluster/eks_cluster/store/index.js
+++ b/app/assets/javascripts/create_cluster/eks_cluster/store/index.js
@@ -9,7 +9,7 @@ const createStore = () =>
actions,
getters,
mutations,
- state,
+ state: state(),
});
export default createStore;
diff --git a/app/assets/javascripts/create_cluster/eks_cluster/store/mutation_types.js b/app/assets/javascripts/create_cluster/eks_cluster/store/mutation_types.js
index e69de29bb2d..006a2578bee 100644
--- a/app/assets/javascripts/create_cluster/eks_cluster/store/mutation_types.js
+++ b/app/assets/javascripts/create_cluster/eks_cluster/store/mutation_types.js
@@ -0,0 +1,3 @@
+export const REQUEST_REGIONS = 'REQUEST_REGIONS';
+export const RECEIVE_REGIONS_SUCCESS = 'REQUEST_REGIONS_SUCCESS';
+export const RECEIVE_REGIONS_ERROR = 'RECEIVE_REGIONS_ERROR';
diff --git a/app/assets/javascripts/create_cluster/eks_cluster/store/mutations.js b/app/assets/javascripts/create_cluster/eks_cluster/store/mutations.js
index e69de29bb2d..9a15b902b99 100644
--- a/app/assets/javascripts/create_cluster/eks_cluster/store/mutations.js
+++ b/app/assets/javascripts/create_cluster/eks_cluster/store/mutations.js
@@ -0,0 +1,16 @@
+import * as types from './mutation_types';
+
+export default {
+ [types.REQUEST_REGIONS](state) {
+ state.isLoadingRegions = true;
+ state.loadingRegionsError = null;
+ },
+ [types.RECEIVE_REGIONS_SUCCESS](state, { regions }) {
+ state.isLoadingRegions = false;
+ state.regions = regions;
+ },
+ [types.RECEIVE_REGIONS_ERROR](state, { error }) {
+ state.isLoadingRegions = false;
+ state.loadingRegionsError = error;
+ },
+};
diff --git a/app/assets/javascripts/create_cluster/eks_cluster/store/state.js b/app/assets/javascripts/create_cluster/eks_cluster/store/state.js
index 9754ccfeeaf..86b8da153a1 100644
--- a/app/assets/javascripts/create_cluster/eks_cluster/store/state.js
+++ b/app/assets/javascripts/create_cluster/eks_cluster/store/state.js
@@ -2,16 +2,24 @@ export default () => ({
isValidatingCredentials: false,
validCredentials: false,
+ isLoadingRegions: false,
isLoadingRoles: false,
isLoadingVPCs: false,
isLoadingSubnets: false,
isLoadingSecurityGroups: false,
+ regions: [],
roles: [],
vpcs: [],
subnets: [],
securityGroups: [],
+ loadingRegionsError: null,
+ loadingRolesError: null,
+ loadingVPCsError: null,
+ loadingSubnetsError: null,
+ loadingSecurityGroupsError: null,
+
selectedRole: '',
selectedVPC: '',
selectedSubnet: '',
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 8d529d397ba..fffc04e3d38 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -2611,6 +2611,9 @@ msgstr ""
msgid "ClusterIntegration|Copy Service Token"
msgstr ""
+msgid "ClusterIntegration|Could not load regions from your AWS account"
+msgstr ""
+
msgid "ClusterIntegration|Create Kubernetes cluster"
msgstr ""
@@ -2767,6 +2770,9 @@ msgstr ""
msgid "ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}."
msgstr ""
+msgid "ClusterIntegration|Learn more about %{startLink}Regions%{endLink}."
+msgstr ""
+
msgid "ClusterIntegration|Learn more about Kubernetes"
msgstr ""
@@ -2782,6 +2788,9 @@ msgstr ""
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
+msgid "ClusterIntegration|Loading Regions"
+msgstr ""
+
msgid "ClusterIntegration|Machine type"
msgstr ""
@@ -2803,6 +2812,9 @@ msgstr ""
msgid "ClusterIntegration|No projects matched your search"
msgstr ""
+msgid "ClusterIntegration|No region found"
+msgstr ""
+
msgid "ClusterIntegration|No zones matched your search"
msgstr ""
@@ -2842,6 +2854,9 @@ msgstr ""
msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
msgstr ""
+msgid "ClusterIntegration|Region"
+msgstr ""
+
msgid "ClusterIntegration|Remove Kubernetes cluster integration"
msgstr ""
@@ -2875,6 +2890,9 @@ msgstr ""
msgid "ClusterIntegration|Search projects"
msgstr ""
+msgid "ClusterIntegration|Search regions"
+msgstr ""
+
msgid "ClusterIntegration|Search zones"
msgstr ""
@@ -3019,6 +3037,9 @@ msgstr ""
msgid "ClusterIntegration|sign up"
msgstr ""
+msgid "ClusterIntergation|Select a region"
+msgstr ""
+
msgid "ClusterIntergation|Select role name"
msgstr ""
@@ -5457,7 +5478,6 @@ msgstr ""
msgid "Go to file (MRs only)"
msgstr ""
-
msgid "Go to file permalink (while viewing a file)"
msgstr ""
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
new file mode 100644
index 00000000000..57f7cbab2c2
--- /dev/null
+++ b/spec/frontend/create_cluster/eks_cluster/components/eks_cluster_configuration_form_spec.js
@@ -0,0 +1,62 @@
+import { shallowMount, createLocalVue } from '@vue/test-utils';
+import Vuex from 'vuex';
+import EksClusterConfigurationForm from '~/create_cluster/eks_cluster/components/eks_cluster_configuration_form.vue';
+import RegionDropdown from '~/create_cluster/eks_cluster/components/region_dropdown.vue';
+
+const localVue = createLocalVue();
+localVue.use(Vuex);
+
+describe('EksClusterConfigurationForm', () => {
+ let store;
+ let actions;
+ let state;
+
+ beforeEach(() => {
+ actions = {
+ fetchRegions: jest.fn(),
+ };
+ state = {
+ regions: [{ name: 'region 1' }],
+ isLoadingRegions: false,
+ loadingRegionsError: { message: '' },
+ };
+ store = new Vuex.Store({
+ state,
+ actions,
+ });
+ });
+
+ const buildVM = (props = {}) =>
+ shallowMount(EksClusterConfigurationForm, {
+ propsData: props,
+ localVue,
+ store,
+ });
+
+ describe('when mounted', () => {
+ it('fetches available regions', () => {
+ buildVM();
+ expect(actions.fetchRegions).toHaveBeenCalled();
+ });
+ });
+
+ it('sets isLoadingRegions to RegionDropdown loading property', () => {
+ state.isLoadingRegions = true;
+
+ const vm = buildVM();
+
+ expect(vm.find(RegionDropdown).props('loading')).toEqual(state.isLoadingRegions);
+ });
+
+ it('sets regions to RegionDropdown regions property', () => {
+ const vm = buildVM();
+
+ expect(vm.find(RegionDropdown).props('regions')).toEqual(state.regions);
+ });
+
+ it('sets loadingRegionsError to RegionDropdown error property', () => {
+ const vm = buildVM();
+
+ expect(vm.find(RegionDropdown).props('error')).toEqual(state.loadingRegionsError);
+ });
+});
diff --git a/spec/frontend/create_cluster/eks_cluster/components/region_dropdown_spec.js b/spec/frontend/create_cluster/eks_cluster/components/region_dropdown_spec.js
new file mode 100644
index 00000000000..1d553159628
--- /dev/null
+++ b/spec/frontend/create_cluster/eks_cluster/components/region_dropdown_spec.js
@@ -0,0 +1,64 @@
+import { shallowMount } from '@vue/test-utils';
+
+import ClusterFormDropdown from '~/create_cluster/eks_cluster/components/cluster_form_dropdown.vue';
+import RegionDropdown from '~/create_cluster/eks_cluster/components/region_dropdown.vue';
+
+describe('RegionDropdown', () => {
+ const buildVM = (props = {}) =>
+ shallowMount(RegionDropdown, {
+ propsData: props,
+ });
+
+ it('renders a cluster-form-dropdown', () => {
+ expect(
+ buildVM()
+ .find(ClusterFormDropdown)
+ .exists(),
+ ).toBe(true);
+ });
+
+ it('sets regions to cluster-form-dropdown items property', () => {
+ const regions = [{ name: 'basic' }];
+ const vm = buildVM({ regions });
+
+ expect(vm.find(ClusterFormDropdown).props('items')).toEqual(regions);
+ });
+
+ it('sets a loading text', () => {
+ const vm = buildVM();
+
+ expect(vm.find(ClusterFormDropdown).props('loadingText')).toEqual('Loading Regions');
+ });
+
+ it('sets a placeholder', () => {
+ const vm = buildVM();
+
+ expect(vm.find(ClusterFormDropdown).props('placeholder')).toEqual('Select a region');
+ });
+
+ it('sets an empty results text', () => {
+ const vm = buildVM();
+
+ expect(vm.find(ClusterFormDropdown).props('emptyText')).toEqual('No region found');
+ });
+
+ it('sets a search field placeholder', () => {
+ const vm = buildVM();
+
+ expect(vm.find(ClusterFormDropdown).props('searchFieldPlaceholder')).toEqual('Search regions');
+ });
+
+ it('sets hasErrors property', () => {
+ const vm = buildVM({ error: {} });
+
+ expect(vm.find(ClusterFormDropdown).props('hasErrors')).toEqual(true);
+ });
+
+ it('sets an error message', () => {
+ const vm = buildVM();
+
+ expect(vm.find(ClusterFormDropdown).props('errorMessage')).toEqual(
+ 'Could not load regions from your AWS account',
+ );
+ });
+});
diff --git a/spec/frontend/create_cluster/eks_cluster/store/actions_spec.js b/spec/frontend/create_cluster/eks_cluster/store/actions_spec.js
new file mode 100644
index 00000000000..a575fd0d8be
--- /dev/null
+++ b/spec/frontend/create_cluster/eks_cluster/store/actions_spec.js
@@ -0,0 +1,88 @@
+import testAction from 'helpers/vuex_action_helper';
+
+import * as awsServicesFacade from '~/create_cluster/eks_cluster/services/aws_services_facade';
+import createState from '~/create_cluster/eks_cluster/store/state';
+import * as types from '~/create_cluster/eks_cluster/store/mutation_types';
+import * as actions from '~/create_cluster/eks_cluster/store/actions';
+
+describe('EKS Cluster Store Actions', () => {
+ const regions = [{ name: 'region 1' }];
+
+ describe('fetchRegions', () => {
+ describe('on success', () => {
+ beforeEach(() => {
+ jest.spyOn(awsServicesFacade, 'fetchRegions').mockResolvedValueOnce(regions);
+ });
+
+ it('dispatches success with received regions', () =>
+ testAction(
+ actions.fetchRegions,
+ null,
+ createState(),
+ [],
+ [
+ { type: 'requestRegions' },
+ {
+ type: 'receiveRegionsSuccess',
+ payload: { regions },
+ },
+ ],
+ ));
+ });
+
+ describe('on failure', () => {
+ const error = new Error('Could not fetch regions');
+
+ beforeEach(() => {
+ jest.spyOn(awsServicesFacade, 'fetchRegions').mockRejectedValueOnce(error);
+ });
+
+ it('dispatches success with received regions', () =>
+ testAction(
+ actions.fetchRegions,
+ null,
+ createState(),
+ [],
+ [
+ { type: 'requestRegions' },
+ {
+ type: 'receiveRegionsError',
+ payload: { error },
+ },
+ ],
+ ));
+ });
+ });
+
+ describe('requestRegions', () => {
+ it(`commits ${types.REQUEST_REGIONS} mutation`, () =>
+ testAction(actions.requestRegions, null, createState(), [{ type: types.REQUEST_REGIONS }]));
+ });
+
+ describe('receiveRegionsSuccess', () => {
+ it(`commits ${types.RECEIVE_REGIONS_SUCCESS} mutation`, () =>
+ testAction(actions.receiveRegionsSuccess, { regions }, createState(), [
+ {
+ type: types.RECEIVE_REGIONS_SUCCESS,
+ payload: {
+ regions,
+ },
+ },
+ ]));
+ });
+
+ describe('receiveRegionsError', () => {
+ it(`commits ${types.RECEIVE_REGIONS_ERROR} mutation`, () => {
+ const error = new Error('Error fetching regions');
+
+ testAction(actions.receiveRegionsError, { error }, createState(), [
+ {
+ type: types.RECEIVE_REGIONS_ERROR,
+ payload: {
+ error,
+ },
+ },
+ ]);
+ });
+ });
+});
diff --git a/spec/frontend/create_cluster/eks_cluster/store/mutations_spec.js b/spec/frontend/create_cluster/eks_cluster/store/mutations_spec.js
new file mode 100644
index 00000000000..e9121f0c449
--- /dev/null
+++ b/spec/frontend/create_cluster/eks_cluster/store/mutations_spec.js
@@ -0,0 +1,36 @@
+import {
+ REQUEST_REGIONS,
+ RECEIVE_REGIONS_ERROR,
+ RECEIVE_REGIONS_SUCCESS,
+} 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';
+
+describe('Create EKS cluster store mutations', () => {
+ let state;
+ let emptyPayload;
+ let regions;
+ let error;
+
+ beforeEach(() => {
+ emptyPayload = {};
+ regions = [{ name: 'regions-1' }];
+ error = new Error('could not load error');
+ state = createState();
+ });
+
+ it.each`
+ mutation | mutatedProperty | payload | expectedValue | expectedValueDescription
+ ${REQUEST_REGIONS} | ${'isLoadingRegions'} | ${emptyPayload} | ${true} | ${true}
+ ${REQUEST_REGIONS} | ${'loadingRegionsError'} | ${emptyPayload} | ${null} | ${null}
+ ${RECEIVE_REGIONS_SUCCESS} | ${'isLoadingRegions'} | ${{ regions }} | ${false} | ${false}
+ ${RECEIVE_REGIONS_SUCCESS} | ${'regions'} | ${{ regions }} | ${regions} | ${'regions payload'}
+ ${RECEIVE_REGIONS_ERROR} | ${'isLoadingRegions'} | ${{ error }} | ${false} | ${false}
+ ${RECEIVE_REGIONS_ERROR} | ${'error'} | ${{ error }} | ${error} | ${'received error object'}
+ `(`$mutation sets $mutatedProperty to $expectedValueDescription`, data => {
+ const { mutation, mutatedProperty, payload, expectedValue } = data;
+
+ mutations[mutation](state, payload);
+ expect(state[mutatedProperty]).toBe(expectedValue);
+ });
+});