diff options
author | Dennis Tang <dtang@gitlab.com> | 2018-05-23 12:16:14 +0200 |
---|---|---|
committer | Dennis Tang <dtang@gitlab.com> | 2018-05-23 12:16:14 +0200 |
commit | 247b20972b51fce323939bfa43340ad32f7e9a30 (patch) | |
tree | 7ddbad70cf7440a808056c6c9188fbea6d7f8c2e | |
parent | 2b903eceda1253e60fa98ba00d2fe12f6fa0d9ba (diff) | |
download | gitlab-ce-247b20972b51fce323939bfa43340ad32f7e9a30.tar.gz |
validate project billing status after selection
9 files changed, 110 insertions, 32 deletions
diff --git a/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_machine_type_dropdown.vue b/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_machine_type_dropdown.vue index cc4d863a989..5cb1ae670dc 100644 --- a/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_machine_type_dropdown.vue +++ b/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_machine_type_dropdown.vue @@ -8,14 +8,14 @@ export default { name: 'GkeMachineTypeDropdown', mixins: [gkeDropdownMixin], computed: { - ...mapState(['selectedProject', 'selectedZone', 'selectedMachineType']), + ...mapState(['projectHasBillingEnabled', 'selectedZone', 'selectedMachineType']), ...mapState({ items: 'machineTypes' }), - ...mapGetters(['hasProject', 'hasZone', 'hasMachineType']), + ...mapGetters(['hasZone', 'hasMachineType']), allDropdownsSelected() { - return this.hasProject && this.hasZone && this.hasMachineType; + return this.projectHasBillingEnabled && this.hasZone && this.hasMachineType; }, isDisabled() { - return !this.selectedProject || !this.selectedZone; + return !this.projectHasBillingEnabled || !this.selectedZone; }, toggleText() { if (this.isLoading) { @@ -26,7 +26,7 @@ export default { return this.selectedMachineType; } - if (!this.hasProject && !this.hasZone) { + if (!this.projectHasBillingEnabled && !this.hasZone) { return s__('ClusterIntegration|Select project and zone to choose machine type'); } diff --git a/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_project_id_dropdown.vue b/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_project_id_dropdown.vue index a322b7c375e..265c66f8ad1 100644 --- a/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_project_id_dropdown.vue +++ b/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_project_id_dropdown.vue @@ -15,7 +15,7 @@ export default { }, }, computed: { - ...mapState(['selectedProject']), + ...mapState(['selectedProject', 'projectHasBillingEnabled']), ...mapState({ items: 'projects' }), ...mapGetters(['hasProject']), hasOneProject() { @@ -25,6 +25,10 @@ export default { return this.items && this.items.length < 2; }, toggleText() { + if (this.isValidatingProjectBilling) { + return s__('ClusterIntegration|Validating project billing status'); + } + if (this.isLoading) { return s__('ClusterIntegration|Fetching projects'); } @@ -42,7 +46,7 @@ export default { helpText() { let message; if (this.hasErrors) { - message = this.gapiError; + return this.errorMessage; } if (!this.items) { @@ -67,12 +71,50 @@ export default { ); }, errorMessage() { + if (!this.projectHasBillingEnabled) { + if (this.gapiError) { + return s__( + 'ClusterIntegration|We could not verify that one of your projects on GCP has billing enabled. Please try again.', + ); + } + + return sprintf( + s__( + 'Please <a href=%{linkToBilling} target="_blank" rel="noopener noreferrer">enable billing for one of your projects to be able to create a Kubernetes cluster</a>, then try again.', + ), + { + linkToBilling: + 'https://console.cloud.google.com/freetrial?utm_campaign=2018_cpanel&utm_source=gitlab&utm_medium=referral', + }, + false, + ); + } + return sprintf( s__('ClusterIntegration|An error occured while trying to fetch your projects: %{error}'), { error: this.gapiError }, ); }, }, + watch: { + selectedProject() { + this.isLoading = true; + this.isValidatingProjectBilling = true; + + this.validateProjectBilling() + .then(this.validateProjectBillingSuccessHandler) + .catch(this.validateProjectBillingFailureHandler); + }, + projectHasBillingEnabled(billingEnabled) { + this.hasErrors = !billingEnabled; + this.isValidatingProjectBilling = false; + }, + }, + data() { + return { + isValidatingProjectBilling: false, + }; + }, created() { this.isLoading = true; @@ -81,7 +123,7 @@ export default { .catch(this.fetchFailureHandler); }, methods: { - ...mapActions(['fetchProjects']), + ...mapActions(['fetchProjects', 'validateProjectBilling']), ...mapActions({ setItem: 'setProject' }), fetchSuccessHandler() { if (this.defaultValue) { @@ -97,6 +139,15 @@ export default { this.isLoading = false; this.hasErrors = false; }, + validateProjectBillingSuccessHandler() { + this.isLoading = false; + }, + validateProjectBillingFailureHandler(resp) { + this.isLoading = false; + this.hasErrors = true; + + this.gapiError = resp.result ? resp.result.error.message : resp; + }, }, }; </script> diff --git a/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_zone_dropdown.vue b/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_zone_dropdown.vue index 46d84bbb751..43531813407 100644 --- a/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_zone_dropdown.vue +++ b/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_zone_dropdown.vue @@ -1,6 +1,6 @@ <script> import { sprintf, s__ } from '~/locale'; -import { mapState, mapGetters, mapActions } from 'vuex'; +import { mapState, mapActions } from 'vuex'; import gkeDropdownMixin from './gke_dropdown_mixin'; @@ -8,11 +8,10 @@ export default { name: 'GkeZoneDropdown', mixins: [gkeDropdownMixin], computed: { - ...mapState(['selectedProject', 'selectedZone', 'projects']), + ...mapState(['selectedProject', 'selectedZone', 'projects', 'projectHasBillingEnabled']), ...mapState({ items: 'zones' }), - ...mapGetters(['hasProject']), isDisabled() { - return !this.hasProject; + return !this.projectHasBillingEnabled; }, toggleText() { if (this.isLoading) { @@ -23,7 +22,7 @@ export default { return this.selectedZone; } - return !this.hasProject + return !this.projectHasBillingEnabled ? s__('ClusterIntegration|Select project to choose zone') : s__('ClusterIntegration|Select zone'); }, @@ -35,10 +34,11 @@ export default { }, }, watch: { - selectedProject() { + projectHasBillingEnabled(billingEnabled) { + if (!billingEnabled) return false; this.isLoading = true; - this.fetchZones() + return this.fetchZones() .then(this.fetchSuccessHandler) .catch(this.fetchFailureHandler); }, diff --git a/app/assets/javascripts/projects/gke_cluster_dropdowns/constants.js b/app/assets/javascripts/projects/gke_cluster_dropdowns/constants.js index 100331df819..2a1c0819916 100644 --- a/app/assets/javascripts/projects/gke_cluster_dropdowns/constants.js +++ b/app/assets/javascripts/projects/gke_cluster_dropdowns/constants.js @@ -3,6 +3,8 @@ import { s__ } from '~/locale'; export const GCP_API_ERROR = s__( 'ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later.', ); +export const GCP_API_CLOUD_BILLING_ENDPOINT = + 'https://www.googleapis.com/discovery/v1/apis/cloudbilling/v1/rest'; export const GCP_API_CLOUD_RESOURCE_MANAGER_ENDPOINT = 'https://www.googleapis.com/discovery/v1/apis/cloudresourcemanager/v1/rest'; export const GCP_API_COMPUTE_ENDPOINT = diff --git a/app/assets/javascripts/projects/gke_cluster_dropdowns/index.js b/app/assets/javascripts/projects/gke_cluster_dropdowns/index.js index 79255e654f2..7a0b515b1a8 100644 --- a/app/assets/javascripts/projects/gke_cluster_dropdowns/index.js +++ b/app/assets/javascripts/projects/gke_cluster_dropdowns/index.js @@ -56,19 +56,20 @@ const gkeDropdownErrorHandler = () => { const initializeGapiClient = () => { const el = document.querySelector('.js-gke-cluster-creation'); - - gapi.client.setToken({ access_token: el.dataset.token }); + if (!el) return false; gapi.client - .load(CONSTANTS.GCP_API_CLOUD_RESOURCE_MANAGER_ENDPOINT) - .then(() => { - mountGkeProjectIdDropdown(); + .init({ + discoveryDocs: [ + CONSTANTS.GCP_API_CLOUD_BILLING_ENDPOINT, + CONSTANTS.GCP_API_CLOUD_RESOURCE_MANAGER_ENDPOINT, + CONSTANTS.GCP_API_COMPUTE_ENDPOINT, + ], }) - .catch(gkeDropdownErrorHandler); - - gapi.client - .load(CONSTANTS.GCP_API_COMPUTE_ENDPOINT) .then(() => { + gapi.client.setToken({ access_token: el.dataset.token }); + + mountGkeProjectIdDropdown(); mountGkeZoneDropdown(); mountGkeMachineTypeDropdown(); }) diff --git a/app/assets/javascripts/projects/gke_cluster_dropdowns/store/actions.js b/app/assets/javascripts/projects/gke_cluster_dropdowns/store/actions.js index 11bd04f5b01..409265175a4 100644 --- a/app/assets/javascripts/projects/gke_cluster_dropdowns/store/actions.js +++ b/app/assets/javascripts/projects/gke_cluster_dropdowns/store/actions.js @@ -1,9 +1,9 @@ /* global gapi */ import * as types from './mutation_types'; -const gapiRequest = ({ service, params, commit, mutation, payloadKey }) => +const gapiResourceListRequest = ({ resource, params, commit, mutation, payloadKey }) => new Promise((resolve, reject) => { - const request = service.list(params); + const request = resource.list(params); return request.then( resp => { @@ -32,17 +32,36 @@ export const setMachineType = ({ commit }, selectedMachineType) => { }; export const fetchProjects = ({ commit }) => - gapiRequest({ - service: gapi.client.cloudresourcemanager.projects, + gapiResourceListRequest({ + resource: gapi.client.cloudresourcemanager.projects, params: {}, commit, mutation: types.SET_PROJECTS, payloadKey: 'projects', }); +export const validateProjectBilling = ({ commit, state }) => + new Promise((resolve, reject) => { + const request = gapi.client.cloudbilling.projects.getBillingInfo({ + name: `projects/${state.selectedProject.projectId}`, + }); + + return request.then( + resp => { + const { billingEnabled } = resp.result; + + commit(types.SET_PROJECT_BILLING_STATUS, !!billingEnabled); + resolve(); + }, + resp => { + reject(resp); + }, + ); + }); + export const fetchZones = ({ commit, state }) => - gapiRequest({ - service: gapi.client.compute.zones, + gapiResourceListRequest({ + resource: gapi.client.compute.zones, params: { project: state.selectedProject.projectId, }, @@ -52,8 +71,8 @@ export const fetchZones = ({ commit, state }) => }); export const fetchMachineTypes = ({ commit, state }) => - gapiRequest({ - service: gapi.client.compute.machineTypes, + gapiResourceListRequest({ + resource: gapi.client.compute.machineTypes, params: { project: state.selectedProject.projectId, zone: state.selectedZone, diff --git a/app/assets/javascripts/projects/gke_cluster_dropdowns/store/mutation_types.js b/app/assets/javascripts/projects/gke_cluster_dropdowns/store/mutation_types.js index 6577612b4cd..98574289bc4 100644 --- a/app/assets/javascripts/projects/gke_cluster_dropdowns/store/mutation_types.js +++ b/app/assets/javascripts/projects/gke_cluster_dropdowns/store/mutation_types.js @@ -1,4 +1,5 @@ export const SET_PROJECT = 'SET_PROJECT'; +export const SET_PROJECT_BILLING_STATUS = 'SET_PROJECT_BILLING_STATUS'; export const SET_ZONE = 'SET_ZONE'; export const SET_MACHINE_TYPE = 'SET_MACHINE_TYPE'; export const SET_PROJECTS = 'SET_PROJECTS'; diff --git a/app/assets/javascripts/projects/gke_cluster_dropdowns/store/mutations.js b/app/assets/javascripts/projects/gke_cluster_dropdowns/store/mutations.js index 95b7ed68053..a9ff3b503f4 100644 --- a/app/assets/javascripts/projects/gke_cluster_dropdowns/store/mutations.js +++ b/app/assets/javascripts/projects/gke_cluster_dropdowns/store/mutations.js @@ -4,6 +4,9 @@ export default { [types.SET_PROJECT](state, selectedProject) { Object.assign(state, { selectedProject }); }, + [types.SET_PROJECT_BILLING_STATUS](state, projectHasBillingEnabled) { + Object.assign(state, { projectHasBillingEnabled }); + }, [types.SET_ZONE](state, selectedZone) { Object.assign(state, { selectedZone }); }, diff --git a/app/assets/javascripts/projects/gke_cluster_dropdowns/store/state.js b/app/assets/javascripts/projects/gke_cluster_dropdowns/store/state.js index 5b1ed85bdb2..5ed8a8da7b5 100644 --- a/app/assets/javascripts/projects/gke_cluster_dropdowns/store/state.js +++ b/app/assets/javascripts/projects/gke_cluster_dropdowns/store/state.js @@ -5,6 +5,7 @@ export default { }, selectedZone: '', selectedMachineType: '', + projectHasBillingEnabled: null, projects: [], zones: [], machineTypes: [], |