summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/environments
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/environments')
-rw-r--r--app/assets/javascripts/environments/components/canary_ingress.vue109
-rw-r--r--app/assets/javascripts/environments/components/canary_update_modal.vue133
-rw-r--r--app/assets/javascripts/environments/components/container.vue12
-rw-r--r--app/assets/javascripts/environments/components/deploy_board.vue216
-rw-r--r--app/assets/javascripts/environments/components/environment_item.vue2
-rw-r--r--app/assets/javascripts/environments/components/environment_rollback.vue2
-rw-r--r--app/assets/javascripts/environments/components/environments_app.vue16
-rw-r--r--app/assets/javascripts/environments/components/environments_table.vue40
-rw-r--r--app/assets/javascripts/environments/constants.js40
-rw-r--r--app/assets/javascripts/environments/folder/environments_folder_bundle.js3
-rw-r--r--app/assets/javascripts/environments/folder/environments_folder_view.vue12
-rw-r--r--app/assets/javascripts/environments/graphql/mutations/update_canary_ingress.mutation.graphql5
-rw-r--r--app/assets/javascripts/environments/index.js3
-rw-r--r--app/assets/javascripts/environments/mixins/canary_callout_mixin.js26
-rw-r--r--app/assets/javascripts/environments/mixins/environments_mixin.js10
-rw-r--r--app/assets/javascripts/environments/stores/environments_store.js26
-rw-r--r--app/assets/javascripts/environments/stores/helpers.js21
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;
+};