diff options
Diffstat (limited to 'app/assets/javascripts/environments')
17 files changed, 556 insertions, 120 deletions
diff --git a/app/assets/javascripts/environments/components/canary_ingress.vue b/app/assets/javascripts/environments/components/canary_ingress.vue new file mode 100644 index 00000000000..f8cdbb96bc2 --- /dev/null +++ b/app/assets/javascripts/environments/components/canary_ingress.vue @@ -0,0 +1,109 @@ +<script> +import { uniqueId } from 'lodash'; +import { GlDropdown, GlDropdownItem, GlModalDirective as GlModal } from '@gitlab/ui'; +import { s__ } from '~/locale'; +import { CANARY_UPDATE_MODAL } from '../constants'; + +export default { + components: { + GlDropdown, + GlDropdownItem, + }, + directives: { + GlModal, + }, + props: { + canaryIngress: { + required: true, + type: Object, + }, + }, + ingressOptions: Array(100 / 5 + 1) + .fill(0) + .map((_, i) => i * 5), + + translations: { + stableLabel: s__('CanaryIngress|Stable'), + canaryLabel: s__('CanaryIngress|Canary'), + }, + + CANARY_UPDATE_MODAL, + + css: { + label: [ + 'gl-font-base', + 'gl-font-weight-normal', + 'gl-line-height-normal', + 'gl-inset-border-1-gray-200', + 'gl-py-3', + 'gl-px-4', + 'gl-mb-0', + ], + }, + computed: { + stableWeightId() { + return uniqueId('stable-weight-'); + }, + canaryWeightId() { + return uniqueId('canary-weight-'); + }, + stableWeight() { + return (100 - this.canaryIngress.canary_weight).toString(); + }, + canaryWeight() { + return this.canaryIngress.canary_weight.toString(); + }, + }, + methods: { + changeCanary(weight) { + this.$emit('change', weight); + }, + changeStable(weight) { + this.$emit('change', 100 - weight); + }, + }, +}; +</script> +<template> + <section class="gl-display-flex gl-bg-white gl-m-3"> + <div class="gl-display-flex gl-flex-direction-column"> + <label :for="stableWeightId" :class="$options.css.label" class="gl-rounded-top-left-base"> + {{ $options.translations.stableLabel }} + </label> + <gl-dropdown + :id="stableWeightId" + :text="stableWeight" + data-testid="stable-weight" + class="gl-w-full" + toggle-class="gl-rounded-top-left-none! gl-rounded-top-right-none! gl-rounded-bottom-right-none!" + > + <gl-dropdown-item + v-for="option in $options.ingressOptions" + :key="option" + v-gl-modal="$options.CANARY_UPDATE_MODAL" + @click="changeStable(option)" + >{{ option }}</gl-dropdown-item + > + </gl-dropdown> + </div> + <div class="gl-display-flex gl-display-flex gl-flex-direction-column"> + <label :for="canaryWeightId" :class="$options.css.label" class="gl-rounded-top-right-base">{{ + $options.translations.canaryLabel + }}</label> + <gl-dropdown + :id="canaryWeightId" + :text="canaryWeight" + data-testid="canary-weight" + toggle-class="gl-rounded-top-left-none! gl-rounded-top-right-none! gl-rounded-bottom-left-none! gl-border-l-none!" + > + <gl-dropdown-item + v-for="option in $options.ingressOptions" + :key="option" + v-gl-modal="$options.CANARY_UPDATE_MODAL" + @click="changeCanary(option)" + >{{ option }}</gl-dropdown-item + > + </gl-dropdown> + </div> + </section> +</template> diff --git a/app/assets/javascripts/environments/components/canary_update_modal.vue b/app/assets/javascripts/environments/components/canary_update_modal.vue new file mode 100644 index 00000000000..fc63d6272c8 --- /dev/null +++ b/app/assets/javascripts/environments/components/canary_update_modal.vue @@ -0,0 +1,133 @@ +<script> +import { GlAlert, GlModal, GlSprintf } from '@gitlab/ui'; +import { __, s__ } from '~/locale'; +import updateCanaryIngress from '../graphql/mutations/update_canary_ingress.mutation.graphql'; +import { CANARY_UPDATE_MODAL } from '../constants'; + +export default { + components: { + GlAlert, + GlModal, + GlSprintf, + }, + props: { + environment: { + type: Object, + required: false, + default: () => ({}), + }, + weight: { + type: Number, + required: false, + default: 0, + }, + visible: { + type: Boolean, + required: false, + default: false, + }, + }, + translations: { + title: s__('CanaryIngress|Change the ratio of canary deployments?'), + ratioChange: s__( + 'CanaryIngress|You are changing the ratio of the canary rollout for %{environment} compared to the stable deployment to:', + ), + stableWeight: s__('CanaryIngress|%{boldStart}Stable:%{boldEnd} %{stable}'), + canaryWeight: s__('CanaryIngress|%{boldStart}Canary:%{boldEnd} %{canary}'), + deploymentWarning: s__( + 'CanaryIngress|Doing so will set a deployment change in progress. This temporarily blocks any further configuration until the deployment is finished.', + ), + }, + modal: { + modalId: CANARY_UPDATE_MODAL, + actionPrimary: { + text: s__('CanaryIngress|Change ratio'), + attributes: [{ variant: 'info' }], + }, + actionCancel: { text: __('Cancel') }, + static: true, + }, + data() { + return { error: '', dismissed: true }; + }, + computed: { + stableWeight() { + return (100 - this.weight).toString(); + }, + canaryWeight() { + return this.weight.toString(); + }, + hasError() { + return Boolean(this.error); + }, + environmentName() { + return this.environment?.name ?? ''; + }, + }, + methods: { + submitCanaryChange() { + return this.$apollo + .mutate({ + mutation: updateCanaryIngress, + variables: { + input: { + id: this.environment.global_id, + weight: this.weight, + }, + }, + }) + .then( + ({ + data: { + environmentsCanaryIngressUpdate: { + errors: [error], + }, + }, + }) => { + this.error = error; + }, + ) + .catch(() => { + this.error = __('Something went wrong. Please try again later'); + }); + }, + dismiss() { + this.error = ''; + }, + }, +}; +</script> +<template> + <div> + <gl-alert v-if="hasError" variant="danger" @dismiss="dismiss">{{ error }}</gl-alert> + <gl-modal v-bind="$options.modal" :visible="visible" @primary="submitCanaryChange"> + <template #modal-title>{{ $options.translations.title }}</template> + <template #default> + <p> + <gl-sprintf :message="$options.translations.ratioChange"> + <template #environment>{{ environmentName }}</template> + </gl-sprintf> + </p> + <ul class="gl-list-style-none gl-p-0"> + <li> + <gl-sprintf :message="$options.translations.stableWeight"> + <template #bold="{ content }"> + <span class="gl-font-weight-bold">{{ content }}</span> + </template> + <template #stable>{{ stableWeight }}</template> + </gl-sprintf> + </li> + <li> + <gl-sprintf :message="$options.translations.canaryWeight"> + <template #bold="{ content }"> + <span class="gl-font-weight-bold">{{ content }}</span> + </template> + <template #canary>{{ canaryWeight }}</template> + </gl-sprintf> + </li> + </ul> + <p>{{ $options.translations.deploymentWarning }}</p> + </template> + </gl-modal> + </div> +</template> diff --git a/app/assets/javascripts/environments/components/container.vue b/app/assets/javascripts/environments/components/container.vue index e7697f14802..c6b34fecbb7 100644 --- a/app/assets/javascripts/environments/components/container.vue +++ b/app/assets/javascripts/environments/components/container.vue @@ -10,11 +10,6 @@ export default { GlLoadingIcon, }, props: { - canaryDeploymentFeatureId: { - type: String, - required: false, - default: null, - }, isLoading: { type: Boolean, required: true, @@ -46,11 +41,6 @@ export default { required: false, default: '', }, - showCanaryDeploymentCallout: { - type: Boolean, - required: false, - default: false, - }, userCalloutsPath: { type: String, required: false, @@ -75,8 +65,6 @@ export default { <environment-table :environments="environments" :can-read-environment="canReadEnvironment" - :canary-deployment-feature-id="canaryDeploymentFeatureId" - :show-canary-deployment-callout="showCanaryDeploymentCallout" :user-callouts-path="userCalloutsPath" :lock-promotion-svg-path="lockPromotionSvgPath" :help-canary-deployments-path="helpCanaryDeploymentsPath" diff --git a/app/assets/javascripts/environments/components/deploy_board.vue b/app/assets/javascripts/environments/components/deploy_board.vue new file mode 100644 index 00000000000..07cb968d8d3 --- /dev/null +++ b/app/assets/javascripts/environments/components/deploy_board.vue @@ -0,0 +1,216 @@ +<script> +/* eslint-disable @gitlab/vue-require-i18n-strings */ +/** + * Renders a deploy board. + * + * A deploy board is composed by: + * - Information area with percentage of completion. + * - Instances with status. + * - Button Actions. + * [Mockup](https://gitlab.com/gitlab-org/gitlab-foss/uploads/2f655655c0eadf655d0ae7467b53002a/environments__deploy-graphic.png) + */ +import { isEmpty } from 'lodash'; +import { + GlIcon, + GlLoadingIcon, + GlLink, + GlTooltip, + GlTooltipDirective, + GlSafeHtmlDirective as SafeHtml, +} from '@gitlab/ui'; +import deployBoardSvg from '@gitlab/svgs/dist/illustrations/deploy-boards.svg'; +import instanceComponent from '~/vue_shared/components/deployment_instance.vue'; +import { n__ } from '~/locale'; +import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; +import { STATUS_MAP, CANARY_STATUS } from '../constants'; +import CanaryIngress from './canary_ingress.vue'; + +export default { + components: { + instanceComponent, + CanaryIngress, + GlIcon, + GlLoadingIcon, + GlLink, + GlTooltip, + }, + directives: { + GlTooltip: GlTooltipDirective, + SafeHtml, + }, + mixins: [glFeatureFlagsMixin()], + props: { + deployBoardData: { + type: Object, + required: true, + }, + deployBoardsHelpPath: { + type: String, + required: false, + default: '', + }, + isLoading: { + type: Boolean, + required: true, + }, + isEmpty: { + type: Boolean, + required: true, + }, + logsPath: { + type: String, + required: false, + default: '', + }, + }, + computed: { + canRenderDeployBoard() { + return !this.isEmpty && !isEmpty(this.deployBoardData); + }, + canRenderEmptyState() { + return this.isEmpty; + }, + canRenderCanaryWeight() { + return ( + this.glFeatures.canaryIngressWeightControl && !isEmpty(this.deployBoardData.canary_ingress) + ); + }, + instanceCount() { + const { instances } = this.deployBoardData; + + return Array.isArray(instances) ? instances.length : 0; + }, + instanceIsCompletedCount() { + const completionPercentage = this.deployBoardData.completion / 100; + const completionCount = Math.floor(completionPercentage * this.instanceCount); + + return Number.isNaN(completionCount) ? 0 : completionCount; + }, + instanceIsCompletedText() { + const title = n__('instance completed', 'instances completed', this.instanceIsCompletedCount); + + return `${this.instanceIsCompletedCount} ${title}`; + }, + instanceTitle() { + return n__('Instance', 'Instances', this.instanceCount); + }, + deployBoardSvg() { + return deployBoardSvg; + }, + deployBoardActions() { + return this.deployBoardData.rollback_url || this.deployBoardData.abort_url; + }, + statuses() { + // Canary is not a pod status but it needs to be in the legend. + // Hence adding it here. + return { + ...STATUS_MAP, + CANARY_STATUS, + }; + }, + }, + methods: { + changeCanaryWeight(weight) { + this.$emit('changeCanaryWeight', weight); + }, + }, +}; +</script> +<template> + <div class="js-deploy-board deploy-board"> + <gl-loading-icon v-if="isLoading" class="loading-icon" /> + <template v-else> + <div v-if="canRenderDeployBoard" class="deploy-board-information gl-p-5"> + <div class="deploy-board-information gl-w-full"> + <section class="deploy-board-status"> + <span v-gl-tooltip :title="instanceIsCompletedText"> + <span ref="percentage" class="gl-text-center text-plain gl-font-lg" + >{{ deployBoardData.completion }}%</span + > + <span class="text text-center text-secondary">{{ __('Complete') }}</span> + </span> + </section> + + <section class="deploy-board-instances"> + <div class="gl-font-base text-secondary"> + <span class="deploy-board-instances-text" + >{{ instanceTitle }} ({{ instanceCount }})</span + > + <span ref="legend-icon" data-testid="legend-tooltip-target"> + <gl-icon class="gl-text-blue-500 gl-ml-2" name="question" /> + </span> + <gl-tooltip :target="() => $refs['legend-icon']" boundary="#content-body"> + <div class="deploy-board-legend gl-display-flex gl-flex-direction-column"> + <div + v-for="status in statuses" + :key="status.text" + class="gl-display-flex gl-align-items-center" + > + <instance-component :status="status.class" :stable="status.stable" /> + <span class="legend-text gl-ml-3">{{ status.text }}</span> + </div> + </div> + </gl-tooltip> + </div> + + <div class="deploy-board-instances-container d-flex flex-wrap flex-row"> + <template v-for="(instance, i) in deployBoardData.instances"> + <instance-component + :key="i" + :status="instance.status" + :tooltip-text="instance.tooltip" + :pod-name="instance.pod_name" + :logs-path="logsPath" + :stable="instance.stable" + /> + </template> + </div> + </section> + + <canary-ingress + v-if="canRenderCanaryWeight" + class="deploy-board-canary-ingress" + :canary-ingress="deployBoardData.canary_ingress" + @change="changeCanaryWeight" + /> + + <section v-if="deployBoardActions" class="deploy-board-actions"> + <gl-link + v-if="deployBoardData.rollback_url" + :href="deployBoardData.rollback_url" + class="btn" + data-method="post" + rel="nofollow" + >{{ __('Rollback') }}</gl-link + > + <gl-link + v-if="deployBoardData.abort_url" + :href="deployBoardData.abort_url" + class="btn btn-danger btn-inverted" + data-method="post" + rel="nofollow" + >{{ __('Abort') }}</gl-link + > + </section> + </div> + </div> + + <div v-if="canRenderEmptyState" class="deploy-board-empty"> + <section v-safe-html="deployBoardSvg" class="deploy-board-empty-state-svg"></section> + + <section class="deploy-board-empty-state-text"> + <span class="deploy-board-empty-state-title d-flex">{{ + __('Kubernetes deployment not found') + }}</span> + <span> + To see deployment progress for your environments, make sure you are deploying to + <code>$KUBE_NAMESPACE</code> and annotating with + <code>app.gitlab.com/app=$CI_PROJECT_PATH_SLUG</code> + and + <code>app.gitlab.com/env=$CI_ENVIRONMENT_SLUG</code>. + </span> + </section> + </div> + </template> + </div> +</template> diff --git a/app/assets/javascripts/environments/components/environment_item.vue b/app/assets/javascripts/environments/components/environment_item.vue index 347828888dc..1724cc692bd 100644 --- a/app/assets/javascripts/environments/components/environment_item.vue +++ b/app/assets/javascripts/environments/components/environment_item.vue @@ -226,7 +226,7 @@ export default { { deep: true }, ); const combinedActions = (manualActions || []).concat(scheduledActions || []); - return combinedActions.map(action => ({ + return combinedActions.map((action) => ({ ...action, name: action.name, })); diff --git a/app/assets/javascripts/environments/components/environment_rollback.vue b/app/assets/javascripts/environments/components/environment_rollback.vue index 32528e6c6ea..48edde82ce7 100644 --- a/app/assets/javascripts/environments/components/environment_rollback.vue +++ b/app/assets/javascripts/environments/components/environment_rollback.vue @@ -55,7 +55,7 @@ export default { retryUrl: this.retryUrl, isLastDeployment: this.isLastDeployment, }); - eventHub.$on('rollbackEnvironment', environment => { + eventHub.$on('rollbackEnvironment', (environment) => { if (environment.id === this.environment.id) { this.isLoading = true; } diff --git a/app/assets/javascripts/environments/components/environments_app.vue b/app/assets/javascripts/environments/components/environments_app.vue index b6a7cce36e9..6f68c6e864a 100644 --- a/app/assets/javascripts/environments/components/environments_app.vue +++ b/app/assets/javascripts/environments/components/environments_app.vue @@ -39,11 +39,6 @@ export default { type: String, required: true, }, - canaryDeploymentFeatureId: { - type: String, - required: false, - default: '', - }, canCreateEnvironment: { type: Boolean, required: true, @@ -75,11 +70,6 @@ export default { required: false, default: '', }, - showCanaryDeploymentCallout: { - type: Boolean, - required: false, - default: false, - }, userCalloutsPath: { type: String, required: false, @@ -116,7 +106,7 @@ export default { this.service .getFolderContent(folder.folder_path) - .then(response => this.store.setfolderContent(folder, response.data.environments)) + .then((response) => this.store.setfolderContent(folder, response.data.environments)) .then(() => this.store.updateEnvironmentProp(folder, 'isLoadingFolderContent', false)) .catch(() => { Flash(s__('Environments|An error occurred while fetching the environments.')); @@ -130,7 +120,7 @@ export default { // We need to verify if any folder is open to also update it const openFolders = this.store.getOpenFolders(); if (openFolders.length) { - openFolders.forEach(folder => this.fetchChildEnvironments(folder)); + openFolders.forEach((folder) => this.fetchChildEnvironments(folder)); } }, }, @@ -205,8 +195,6 @@ export default { :environments="state.environments" :pagination="state.paginationInformation" :can-read-environment="canReadEnvironment" - :canary-deployment-feature-id="canaryDeploymentFeatureId" - :show-canary-deployment-callout="showCanaryDeploymentCallout" :user-callouts-path="userCalloutsPath" :lock-promotion-svg-path="lockPromotionSvgPath" :help-canary-deployments-path="helpCanaryDeploymentsPath" diff --git a/app/assets/javascripts/environments/components/environments_table.vue b/app/assets/javascripts/environments/components/environments_table.vue index d13c7204285..bbb56ca6f26 100644 --- a/app/assets/javascripts/environments/components/environments_table.vue +++ b/app/assets/javascripts/environments/components/environments_table.vue @@ -6,16 +6,16 @@ import { GlLoadingIcon } from '@gitlab/ui'; import { flow, reverse, sortBy } from 'lodash/fp'; import { s__ } from '~/locale'; import EnvironmentItem from './environment_item.vue'; +import DeployBoard from './deploy_board.vue'; +import CanaryUpdateModal from './canary_update_modal.vue'; export default { components: { EnvironmentItem, GlLoadingIcon, - DeployBoard: () => import('ee_component/environments/components/deploy_board_component.vue'), - CanaryDeploymentCallout: () => - import('ee_component/environments/components/canary_deployment_callout.vue'), + DeployBoard, EnvironmentAlert: () => import('ee_component/environments/components/environment_alert.vue'), - CanaryUpdateModal: () => import('ee_component/environments/components/canary_update_modal.vue'), + CanaryUpdateModal, }, props: { environments: { @@ -33,11 +33,6 @@ export default { required: false, default: false, }, - canaryDeploymentFeatureId: { - type: String, - required: false, - default: '', - }, helpCanaryDeploymentsPath: { type: String, required: false, @@ -48,11 +43,6 @@ export default { required: false, default: '', }, - showCanaryDeploymentCallout: { - type: Boolean, - required: false, - default: false, - }, userCalloutsPath: { type: String, required: false, @@ -67,7 +57,7 @@ export default { }, computed: { sortedEnvironments() { - return this.sortEnvironments(this.environments).map(env => + return this.sortEnvironments(this.environments).map((env) => this.shouldRenderFolderContent(env) ? { ...env, children: this.sortEnvironments(env.children) } : env, @@ -121,9 +111,6 @@ export default { shouldRenderFolderContent(env) { return env.isFolder && env.isOpen && env.children && env.children.length > 0; }, - shouldShowCanaryCallout(env) { - return env.showCanaryCallout && this.showCanaryDeploymentCallout; - }, shouldRenderAlert(env) { return env?.has_opened_alert; }, @@ -144,11 +131,11 @@ export default { * 5. Put folders first. */ return flow( - sortBy(env => (env.isFolder ? env.folderName : env.name)), + sortBy((env) => (env.isFolder ? env.folderName : env.name)), reverse, - sortBy(env => (env.last_deployment ? env.last_deployment.created_at : '0000')), + sortBy((env) => (env.last_deployment ? env.last_deployment.created_at : '0000')), reverse, - sortBy(env => (env.isFolder ? -1 : 1)), + sortBy((env) => (env.isFolder ? -1 : 1)), )(environments); }, changeCanaryWeight(model, weight) { @@ -241,17 +228,6 @@ export default { </div> </template> </template> - - <template v-if="shouldShowCanaryCallout(model)"> - <canary-deployment-callout - :key="`canary-promo-${i}`" - :canary-deployment-feature-id="canaryDeploymentFeatureId" - :user-callouts-path="userCalloutsPath" - :lock-promotion-svg-path="lockPromotionSvgPath" - :help-canary-deployments-path="helpCanaryDeploymentsPath" - :data-js-canary-promo-key="i" - /> - </template> </template> </div> </template> diff --git a/app/assets/javascripts/environments/constants.js b/app/assets/javascripts/environments/constants.js new file mode 100644 index 00000000000..6d427bef4e6 --- /dev/null +++ b/app/assets/javascripts/environments/constants.js @@ -0,0 +1,40 @@ +import { __ } from '~/locale'; + +// These statuses are based on how the backend defines pod phases here +// lib/gitlab/kubernetes/pod.rb + +export const STATUS_MAP = { + succeeded: { + class: 'succeeded', + text: __('Succeeded'), + stable: true, + }, + running: { + class: 'running', + text: __('Running'), + stable: true, + }, + failed: { + class: 'failed', + text: __('Failed'), + stable: true, + }, + pending: { + class: 'pending', + text: __('Pending'), + stable: true, + }, + unknown: { + class: 'unknown', + text: __('Unknown'), + stable: true, + }, +}; + +export const CANARY_STATUS = { + class: 'canary-icon', + text: __('Canary'), + stable: false, +}; + +export const CANARY_UPDATE_MODAL = 'confirm-canary-change'; diff --git a/app/assets/javascripts/environments/folder/environments_folder_bundle.js b/app/assets/javascripts/environments/folder/environments_folder_bundle.js index 6c547c3713a..e4726412f99 100644 --- a/app/assets/javascripts/environments/folder/environments_folder_bundle.js +++ b/app/assets/javascripts/environments/folder/environments_folder_bundle.js @@ -1,6 +1,5 @@ import Vue from 'vue'; import VueApollo from 'vue-apollo'; -import canaryCalloutMixin from '../mixins/canary_callout_mixin'; import environmentsFolderApp from './environments_folder_view.vue'; import { parseBoolean } from '../../lib/utils/common_utils'; import Translate from '../../vue_shared/translate'; @@ -21,7 +20,6 @@ export default () => { components: { environmentsFolderApp, }, - mixins: [canaryCalloutMixin], apolloProvider, provide: { projectPath: el.dataset.projectPath, @@ -43,7 +41,6 @@ export default () => { folderName: this.folderName, cssContainerClass: this.cssContainerClass, canReadEnvironment: this.canReadEnvironment, - ...this.canaryCalloutProps, }, }); }, diff --git a/app/assets/javascripts/environments/folder/environments_folder_view.vue b/app/assets/javascripts/environments/folder/environments_folder_view.vue index 25f5483c58b..dbb60fa4622 100644 --- a/app/assets/javascripts/environments/folder/environments_folder_view.vue +++ b/app/assets/javascripts/environments/folder/environments_folder_view.vue @@ -34,16 +34,6 @@ export default { type: Boolean, required: true, }, - canaryDeploymentFeatureId: { - type: String, - required: false, - default: '', - }, - showCanaryDeploymentCallout: { - type: Boolean, - required: false, - default: false, - }, userCalloutsPath: { type: String, required: false, @@ -98,8 +88,6 @@ export default { :environments="state.environments" :pagination="state.paginationInformation" :can-read-environment="canReadEnvironment" - :canary-deployment-feature-id="canaryDeploymentFeatureId" - :show-canary-deployment-callout="showCanaryDeploymentCallout" :user-callouts-path="userCalloutsPath" :lock-promotion-svg-path="lockPromotionSvgPath" :help-canary-deployments-path="helpCanaryDeploymentsPath" diff --git a/app/assets/javascripts/environments/graphql/mutations/update_canary_ingress.mutation.graphql b/app/assets/javascripts/environments/graphql/mutations/update_canary_ingress.mutation.graphql new file mode 100644 index 00000000000..04ea5cbcaef --- /dev/null +++ b/app/assets/javascripts/environments/graphql/mutations/update_canary_ingress.mutation.graphql @@ -0,0 +1,5 @@ +mutation($input: EnvironmentsCanaryIngressUpdateInput!) { + environmentsCanaryIngressUpdate(input: $input) { + errors + } +} diff --git a/app/assets/javascripts/environments/index.js b/app/assets/javascripts/environments/index.js index 8e8af3f32f7..4d734a457ab 100644 --- a/app/assets/javascripts/environments/index.js +++ b/app/assets/javascripts/environments/index.js @@ -1,6 +1,5 @@ import Vue from 'vue'; import VueApollo from 'vue-apollo'; -import canaryCalloutMixin from './mixins/canary_callout_mixin'; import environmentsComponent from './components/environments_app.vue'; import { parseBoolean } from '../lib/utils/common_utils'; import Translate from '../vue_shared/translate'; @@ -20,7 +19,6 @@ export default () => { components: { environmentsComponent, }, - mixins: [canaryCalloutMixin], apolloProvider, provide: { projectPath: el.dataset.projectPath, @@ -46,7 +44,6 @@ export default () => { deployBoardsHelpPath: this.deployBoardsHelpPath, canCreateEnvironment: this.canCreateEnvironment, canReadEnvironment: this.canReadEnvironment, - ...this.canaryCalloutProps, }, }); }, diff --git a/app/assets/javascripts/environments/mixins/canary_callout_mixin.js b/app/assets/javascripts/environments/mixins/canary_callout_mixin.js deleted file mode 100644 index e9f1a144cb3..00000000000 --- a/app/assets/javascripts/environments/mixins/canary_callout_mixin.js +++ /dev/null @@ -1,26 +0,0 @@ -import { parseBoolean } from '~/lib/utils/common_utils'; - -export default { - data() { - const data = this.$options.el.dataset; - - return { - canaryDeploymentFeatureId: data.environmentsDataCanaryDeploymentFeatureId, - showCanaryDeploymentCallout: parseBoolean(data.environmentsDataShowCanaryDeploymentCallout), - userCalloutsPath: data.environmentsDataUserCalloutsPath, - lockPromotionSvgPath: data.environmentsDataLockPromotionSvgPath, - helpCanaryDeploymentsPath: data.environmentsDataHelpCanaryDeploymentsPath, - }; - }, - computed: { - canaryCalloutProps() { - return { - canaryDeploymentFeatureId: this.canaryDeploymentFeatureId, - showCanaryDeploymentCallout: this.showCanaryDeploymentCallout, - userCalloutsPath: this.userCalloutsPath, - lockPromotionSvgPath: this.lockPromotionSvgPath, - helpCanaryDeploymentsPath: this.helpCanaryDeploymentsPath, - }; - }, - }, -}; diff --git a/app/assets/javascripts/environments/mixins/environments_mixin.js b/app/assets/javascripts/environments/mixins/environments_mixin.js index 9b0301bba07..15a00c11ee6 100644 --- a/app/assets/javascripts/environments/mixins/environments_mixin.js +++ b/app/assets/javascripts/environments/mixins/environments_mixin.js @@ -3,7 +3,7 @@ */ import { isEqual, isFunction, omitBy } from 'lodash'; import Visibility from 'visibilityjs'; -import EnvironmentsStore from 'ee_else_ce/environments/stores/environments_store'; +import EnvironmentsStore from '../stores/environments_store'; import Poll from '../../lib/utils/poll'; import { getParameterByName } from '../../lib/utils/common_utils'; import { s__ } from '../../locale'; @@ -64,7 +64,7 @@ export default { }, filterNilValues(obj) { - return omitBy(obj, value => value === undefined || value === null); + return omitBy(obj, (value) => value === undefined || value === null); }, /** @@ -80,7 +80,7 @@ export default { // fetch new data return this.service .fetchEnvironments(this.requestData) - .then(response => { + .then((response) => { this.successCallback(response); this.poll.enable({ data: this.requestData, response }); }) @@ -107,7 +107,7 @@ export default { this.service .postAction(endpoint) .then(() => this.fetchEnvironments()) - .catch(err => { + .catch((err) => { this.isLoading = false; Flash(isFunction(errorMessage) ? errorMessage(err.response.data) : errorMessage); }); @@ -219,7 +219,7 @@ export default { data: this.requestData, successCallback: this.successCallback, errorCallback: this.errorCallback, - notificationCallback: isMakingRequest => { + notificationCallback: (isMakingRequest) => { this.isMakingRequest = isMakingRequest; }, }); diff --git a/app/assets/javascripts/environments/stores/environments_store.js b/app/assets/javascripts/environments/stores/environments_store.js index 1992e753255..8911885e920 100644 --- a/app/assets/javascripts/environments/stores/environments_store.js +++ b/app/assets/javascripts/environments/stores/environments_store.js @@ -1,4 +1,4 @@ -import { setDeployBoard } from 'ee_else_ce/environments/stores/helpers'; +import { setDeployBoard } from './helpers'; import { parseIntPagination, normalizeHeaders } from '~/lib/utils/common_utils'; /** @@ -46,9 +46,9 @@ export default class EnvironmentsStore { * @returns {Array} */ storeEnvironments(environments = []) { - const filteredEnvironments = environments.map(env => { + const filteredEnvironments = environments.map((env) => { const oldEnvironmentState = - this.state.environments.find(element => { + this.state.environments.find((element) => { if (env.latest) { return element.id === env.latest.id; } @@ -135,12 +135,22 @@ export default class EnvironmentsStore { /** * Toggles deploy board visibility for the provided environment ID. - * Currently only works on EE. * * @param {Object} environment * @return {Array} */ - toggleDeployBoard() { + toggleDeployBoard(environmentID) { + const environments = this.state.environments.slice(); + + this.state.environments = environments.map((env) => { + let updated = { ...env }; + + if (env.id === environmentID) { + updated = { ...updated, isDeployBoardVisible: !env.isDeployBoardVisible }; + } + return updated; + }); + return this.state.environments; } @@ -163,7 +173,7 @@ export default class EnvironmentsStore { * @return {Object} */ setfolderContent(folder, environments) { - const updatedEnvironments = environments.map(env => { + const updatedEnvironments = environments.map((env) => { let updated = env; if (env.latest) { @@ -192,7 +202,7 @@ export default class EnvironmentsStore { updateEnvironmentProp(environment, prop, newValue) { const { environments } = this.state; - const updatedEnvironments = environments.map(env => { + const updatedEnvironments = environments.map((env) => { const updateEnv = { ...env }; if (env.id === environment.id) { updateEnv[prop] = newValue; @@ -207,6 +217,6 @@ export default class EnvironmentsStore { getOpenFolders() { const { environments } = this.state; - return environments.filter(env => env.isFolder && env.isOpen); + return environments.filter((env) => env.isFolder && env.isOpen); } } diff --git a/app/assets/javascripts/environments/stores/helpers.js b/app/assets/javascripts/environments/stores/helpers.js index eb47ba29412..89457da0614 100644 --- a/app/assets/javascripts/environments/stores/helpers.js +++ b/app/assets/javascripts/environments/stores/helpers.js @@ -1,7 +1,22 @@ /** - * Deploy boards are EE only. - * * @param {Object} environment * @returns {Object} */ -export const setDeployBoard = (oldEnvironmentState, environment) => environment; +export const setDeployBoard = (oldEnvironmentState, environment) => { + let parsedEnvironment = environment; + if (environment.size === 1 && environment.rollout_status) { + parsedEnvironment = { + ...environment, + hasDeployBoard: true, + isDeployBoardVisible: + oldEnvironmentState.isDeployBoardVisible === false + ? oldEnvironmentState.isDeployBoardVisible + : true, + deployBoardData: + environment.rollout_status.status === 'found' ? environment.rollout_status : {}, + isLoadingDeployBoard: environment.rollout_status.status === 'loading', + isEmptyDeployBoard: environment.rollout_status.status === 'not_found', + }; + } + return parsedEnvironment; +}; |