Commit 855a72e0 authored by Andrew Fontaine's avatar Andrew Fontaine Committed by Nicolò Maria Mezzopera

Allow Users to Set Canary Ingress via UI

This lets users set the canary ingress percentage via the UI when using
deploy boards.
parent 429f4a23
......@@ -143,13 +143,7 @@ export default {
<confirm-rollback-modal :environment="environmentInRollbackModal" />
<div class="gl-w-full">
<div
class="
gl-display-flex
gl-flex-direction-column
gl-mt-3
gl-display-md-none!"
>
<div class="gl-display-flex gl-flex-direction-column gl-mt-3 gl-display-md-none!">
<gl-button
v-if="state.reviewAppDetails.can_setup_review_app"
v-gl-modal="$options.modal.id"
......@@ -158,18 +152,16 @@ export default {
category="secondary"
type="button"
class="gl-mb-3 gl-flex-fill-1"
>{{ $options.i18n.reviewAppButtonLabel }}</gl-button
>
{{ $options.i18n.reviewAppButtonLabel }}
</gl-button>
<gl-button
v-if="canCreateEnvironment"
:href="newEnvironmentPath"
data-testid="new-environment"
category="primary"
variant="success"
>{{ $options.i18n.newEnvironmentButtonLabel }}</gl-button
>
{{ $options.i18n.newEnvironmentButtonLabel }}
</gl-button>
</div>
<gl-tabs content-class="gl-display-none">
<gl-tab
......@@ -185,14 +177,7 @@ export default {
</gl-tab>
<template #tabs-end>
<div
class="
gl-display-none
gl-display-md-flex
gl-lg-align-items-center
gl-lg-flex-direction-row
gl-lg-flex-fill-1
gl-lg-justify-content-end
gl-lg-mt-0"
class="gl-display-none gl-display-md-flex gl-lg-align-items-center gl-lg-flex-direction-row gl-lg-flex-fill-1 gl-lg-justify-content-end gl-lg-mt-0"
>
<gl-button
v-if="state.reviewAppDetails.can_setup_review_app"
......@@ -202,18 +187,16 @@ export default {
category="secondary"
type="button"
class="gl-mb-3 gl-lg-mr-3 gl-lg-mb-0"
>{{ $options.i18n.reviewAppButtonLabel }}</gl-button
>
{{ $options.i18n.reviewAppButtonLabel }}
</gl-button>
<gl-button
v-if="canCreateEnvironment"
:href="newEnvironmentPath"
data-testid="new-environment"
category="primary"
variant="success"
>{{ $options.i18n.newEnvironmentButtonLabel }}</gl-button
>
{{ $options.i18n.newEnvironmentButtonLabel }}
</gl-button>
</div>
</template>
</gl-tabs>
......
......@@ -15,6 +15,7 @@ export default {
CanaryDeploymentCallout: () =>
import('ee_component/environments/components/canary_deployment_callout.vue'),
EnvironmentAlert: () => import('ee_component/environments/components/environment_alert.vue'),
CanaryUpdateModal: () => import('ee_component/environments/components/canary_update_modal.vue'),
},
props: {
environments: {
......@@ -58,6 +59,12 @@ export default {
default: '',
},
},
data() {
return {
canaryWeight: 0,
environmentToChange: null,
};
},
computed: {
sortedEnvironments() {
return this.sortEnvironments(this.environments).map(env =>
......@@ -144,11 +151,16 @@ export default {
sortBy(env => (env.isFolder ? -1 : 1)),
)(environments);
},
changeCanaryWeight(model, weight) {
this.environmentToChange = model;
this.canaryWeight = weight;
},
},
};
</script>
<template>
<div class="ci-table" role="grid">
<canary-update-modal :environment="environmentToChange" :weight="canaryWeight" />
<div class="gl-responsive-table-row table-row-header" role="row">
<div class="table-section" :class="tableData.name.spacing" role="columnheader">
{{ tableData.name.title }}
......@@ -179,6 +191,7 @@ export default {
:model="model"
:can-read-environment="canReadEnvironment"
:table-data="tableData"
data-qa-selector="environment_item"
/>
<div
......@@ -193,6 +206,7 @@ export default {
:is-loading="model.isLoadingDeployBoard"
:is-empty="model.isEmptyDeployBoard"
:logs-path="model.logs_path"
@changeCanaryWeight="changeCanaryWeight(model, $event)"
/>
</div>
</div>
......@@ -215,6 +229,7 @@ export default {
:model="children"
:can-read-environment="canReadEnvironment"
:table-data="tableData"
data-qa-selector="environment_item"
/>
<div :key="`sub-div-${i}`">
......
......@@ -103,21 +103,22 @@ Here's an example setup flow from scratch:
#### How to check the current traffic weight on a Canary Ingress
1. Visit [Deploy Board](../../user/project/deploy_boards.md).
1. Open your browser's inspection tool and examine a response from the `environments.json` endpoint.
You can find the current weight under `rollout_status`.
1. Visit the [Deploy Board](../../user/project/deploy_boards.md).
1. View the current weights on the right.
![Rollout Status Canary Ingress](img/rollout_status_canary_ingress.png)
Note that we have [a plan](https://gitlab.com/gitlab-org/gitlab/-/issues/218139)
to visualize this information in a [Deploy Board](../../user/project/deploy_boards.md)
without needing a browser's inspection tool.
![Rollout Status Canary Ingress](img/canary_weight.png)
#### How to change the traffic weight on a Canary Ingress
You can change the traffic weight by using [GraphiQL](../../api/graphql/getting_started.md#graphiql)
You can change the traffic weight within your environment's Deploy Board by using [GraphiQL](../../api/graphql/getting_started.md#graphiql),
or by sending requests to the [GraphQL API](../../api/graphql/getting_started.md#command-line).
To use your [Deploy Board](../../user/project/deploy_boards.md):
1. Navigate to **Operations > Environments** for your project.
1. Set the new weight with the dropdown on the right side.
1. Confirm your selection.
Here's an example using [GraphiQL](../../api/graphql/getting_started.md#graphiql):
1. Visit [GraphiQL Explorer](https://gitlab.com/-/graphql-explorer).
......@@ -136,6 +137,3 @@ Here's an example using [GraphiQL](../../api/graphql/getting_started.md#graphiql
1. If the request succeeds, the `errors` response contains an empty array. GitLab sends a `PATCH`
request to your Kubernetes cluster for updating the weight parameter on a Canary Ingress.
Note that there's [a plan](https://gitlab.com/gitlab-org/gitlab/-/issues/218139)
to control the weight from a [Deploy Board](../../user/project/deploy_boards.md).
<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>
<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>
......@@ -20,11 +20,14 @@ import {
} from '@gitlab/ui';
import deployBoardSvg from 'ee_empty_states/icons/_deploy_board.svg';
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: () => import('ee_component/vue_shared/components/deployment_instance.vue'),
CanaryIngress,
GlIcon,
GlLoadingIcon,
GlLink,
......@@ -34,6 +37,7 @@ export default {
GlTooltip: GlTooltipDirective,
SafeHtml,
},
mixins: [glFeatureFlagsMixin()],
props: {
deployBoardData: {
type: Object,
......@@ -65,6 +69,11 @@ export default {
canRenderEmptyState() {
return this.isEmpty;
},
canRenderCanaryWeight() {
return (
this.glFeatures.canaryIngressWeightControl && !isEmpty(this.deployBoardData.canary_ingress)
);
},
instanceCount() {
const { instances } = this.deployBoardData;
......@@ -99,17 +108,22 @@ export default {
};
},
},
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 p-3">
<div class="deploy-board-information">
<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="text-center text-plain gl-font-lg"
<span ref="percentage" class="gl-text-center text-plain gl-font-lg"
>{{ deployBoardData.completion }}%</span
>
<span class="text text-center text-secondary">{{ __('Complete') }}</span>
......@@ -152,6 +166,13 @@ export default {
</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"
......@@ -177,9 +198,9 @@ export default {
<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 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
......
......@@ -36,3 +36,5 @@ export const CANARY_STATUS = {
text: __('Canary'),
stable: false,
};
export const CANARY_UPDATE_MODAL = 'confirm-canary-change';
mutation($input: EnvironmentsCanaryIngressUpdateInput!) {
environmentsCanaryIngressUpdate(input: $input) {
errors
}
}
......@@ -32,6 +32,10 @@
width: 100%;
}
.deploy-board-canary-ingress {
order: 7;
}
.deploy-board-actions {
order: 3;
align-self: center;
......@@ -53,7 +57,6 @@
order: 2;
flex-wrap: wrap;
margin: auto auto 15px 0;
}
.deploy-board-empty-state-title {
......
......@@ -7,6 +7,9 @@ module EE
prepended do
before_action :authorize_create_environment_terminal!, only: [:terminal]
before_action do
push_frontend_feature_flag(:canary_ingress_weight_control, default_enabled: true)
end
end
private
......
---
title: Allow Users to Set Canary Ingress via UI
merge_request: 49516
author:
type: added
import { mount } from '@vue/test-utils';
import { GlDropdownItem } from '@gitlab/ui';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import { CANARY_UPDATE_MODAL } from 'ee/environments/constants';
import CanaryIngress from 'ee/environments/components/canary_ingress.vue';
describe('ee/environments/components/canary_ingress.vue', () => {
let wrapper;
const setWeightTo = (weightWrapper, x) =>
weightWrapper
.findAll(GlDropdownItem)
.at(x / 5)
.vm.$emit('click');
const createComponent = () => {
wrapper = mount(CanaryIngress, {
propsData: {
canaryIngress: {
canary_weight: 60,
},
},
directives: {
GlModal: createMockDirective(),
},
});
};
beforeEach(() => {
createComponent();
});
afterEach(() => {
if (wrapper) {
wrapper.destroy();
}
wrapper = null;
});
describe('stable weight', () => {
let stableWeightDropdown;
beforeEach(() => {
stableWeightDropdown = wrapper.find('[data-testid="stable-weight"]');
});
it('displays the current stable weight', () => {
expect(stableWeightDropdown.props('text')).toBe('40');
});
it('emits a change with the new canary weight', () => {
setWeightTo(stableWeightDropdown, 15);
expect(wrapper.emitted('change')).toContainEqual([85]);
});
it('lists options from 0 to 100 in increments of 5', () => {
const options = stableWeightDropdown.findAll(GlDropdownItem);
expect(options).toHaveLength(21);
options.wrappers.forEach((w, i) => expect(w.text()).toBe((i * 5).toString()));
});
it('is set to open the change modal', () => {
stableWeightDropdown
.findAll(GlDropdownItem)
.wrappers.forEach(w =>
expect(getBinding(w.element, 'gl-modal')).toMatchObject({ value: CANARY_UPDATE_MODAL }),
);
});
});
describe('canary weight', () => {
let canaryWeightDropdown;
beforeEach(() => {
canaryWeightDropdown = wrapper.find('[data-testid="canary-weight"]');
});
it('displays the current canary weight', () => {
expect(canaryWeightDropdown.props('text')).toBe('60');
});
it('emits a change with the new canary weight', () => {
setWeightTo(canaryWeightDropdown, 15);
expect(wrapper.emitted('change')).toContainEqual([15]);
});
it('lists options from 0 to 100 in increments of 5', () => {
canaryWeightDropdown
.findAll(GlDropdownItem)
.wrappers.forEach((w, i) => expect(w.text()).toBe((i * 5).toString()));
});
it('is set to open the change modal', () => {
const options = canaryWeightDropdown.findAll(GlDropdownItem);
expect(options).toHaveLength(21);
options.wrappers.forEach((w, i) => expect(w.text()).toBe((i * 5).toString()));
});
});
});
import { mount } from '@vue/test-utils';
import { GlAlert, GlModal } from '@gitlab/ui';
import waitForPromises from 'helpers/wait_for_promises';
import CanaryUpdateModal from 'ee/environments/components/canary_update_modal.vue';
import updateCanaryIngress from 'ee/environments/graphql/mutations/update_canary_ingress.mutation.graphql';
describe('ee/environments/components/canary_update_modal.vue', () => {
let wrapper;
let modal;
let mutate;
const findAlert = () => wrapper.find(GlAlert);
const createComponent = () => {
mutate = jest.fn().mockResolvedValue();
wrapper = mount(CanaryUpdateModal, {
propsData: {
environment: {
name: 'staging',
global_id: 'gid://environments/staging',
},
weight: 60,
visible: true,
},
mocks: {
$apollo: { mutate },
},
});
modal = wrapper.find(GlModal);
};
afterEach(() => {
if (wrapper) {
wrapper.destroy();
}
wrapper = null;
});
beforeEach(() => {
createComponent();
});
it('should bind the modal props', () => {
expect(modal.props()).toMatchObject({
modalId: 'confirm-canary-change',
actionPrimary: {
text: 'Change ratio',
attributes: [{ variant: 'info' }],
},
actionCancel: { text: 'Cancel' },
});
});
it('should display the new weights', () => {
expect(modal.text()).toContain('Stable: 40');
expect(modal.text()).toContain('Canary: 60');
});
it('should display the affected environment', () => {
expect(modal.text()).toContain(
'You are changing the ratio of the canary rollout for staging compared to the stable deployment to:',
);
});
it('should update the weight on primary action', () => {
modal.vm.$emit('primary');
expect(mutate).toHaveBeenCalledWith({
mutation: updateCanaryIngress,
variables: {
input: {
id: 'gid://environments/staging',
weight: 60,
},
},
});
});
it('should do nothing on cancel', () => {
modal.vm.$emit('secondary');
expect(mutate).not.toHaveBeenCalled();
});
it('should not display an error if there was not one', async () => {
mutate.mockResolvedValue({ data: { environmentsCanaryIngressUpdate: { errors: [] } } });
modal.vm.$emit('primary');
await wrapper.vm.$nextTick();
expect(findAlert().exists()).toBe(false);
});
it('should display an error if there was one', async () => {
mutate.mockResolvedValue({ data: { environmentsCanaryIngressUpdate: { errors: ['error'] } } });
modal.vm.$emit('primary');
await wrapper.vm.$nextTick();
expect(findAlert().text()).toBe('error');
});
it('should display a generic error if there was a top-level one', async () => {
mutate.mockRejectedValue();
modal.vm.$emit('primary');
await waitForPromises();
await wrapper.vm.$nextTick();
expect(findAlert().text()).toBe('Something went wrong. Please try again later');
});
it('hides teh alert on dismiss', async () => {
mutate.mockResolvedValue({ data: { environmentsCanaryIngressUpdate: { errors: ['error'] } } });
modal.vm.$emit('primary');
await wrapper.vm.$nextTick();
const alert = findAlert();
alert.vm.$emit('dismiss');
await wrapper.vm.$nextTick();
expect(alert.exists()).toBe(false);
});
});
......@@ -2,6 +2,7 @@ import { GlTooltip, GlIcon, GlLoadingIcon } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import Vue from 'vue';
import DeployBoard from 'ee/environments/components/deploy_board_component.vue';
import CanaryIngress from 'ee/environments/components/canary_ingress.vue';
import { deployBoardMockData, environment } from './mock_data';
const logsPath = `gitlab-org/gitlab-test/-/logs?environment_name=${environment.name}`;
......@@ -11,6 +12,7 @@ describe('Deploy Board', () => {
const createComponent = (props = {}) =>
mount(Vue.extend(DeployBoard), {
provide: { glFeatures: { canaryIngressWeightControl: true } },
propsData: {
deployBoardData: deployBoardMockData,
isLoading: false,
......@@ -62,6 +64,12 @@ describe('Deploy Board', () => {
expect(tooltip.props('target')()).toBe(iconSpan.element);
expect(icon.props('name')).toBe('question');
});
it('renders the canary weight selector', () => {
const canary = wrapper.find(CanaryIngress);
expect(canary.exists()).toBe(true);
expect(canary.props('canaryIngress')).toEqual({ canary_weight: 50 });
});
});
describe('with empty state', () => {
......
import { mount, shallowMount } from '@vue/test-utils';
import EnvironmentAlert from 'ee/environments/components/environment_alert.vue';
import DeployBoard from 'ee/environments/components/deploy_board_component.vue';
import CanaryUpdateModal from 'ee/environments/components/canary_update_modal.vue';
import EnvironmentTable from '~/environments/components/environments_table.vue';
import eventHub from '~/environments/event_hub';
import { deployBoardMockData } from './mock_data';
......@@ -91,6 +93,7 @@ describe('Environment table', () => {
rollback_url: 'url',
completion: 100,
is_completed: true,
canary_ingress: { canary_weight: 60 },
},
isDeployBoardVisible: false,
};
......@@ -169,4 +172,40 @@ describe('Environment table', () => {
expect(wrapper.find(EnvironmentAlert).exists()).toBe(true);
});
it('should set the enviornment to change and weight when a change canary weight event is recevied', async () => {
const mockItem = {
name: 'review',
size: 1,
environment_path: 'url',
logs_path: 'url',
id: 1,
hasDeployBoard: true,
deployBoardData: deployBoardMockData,
isDeployBoardVisible: true,
isLoadingDeployBoard: false,
isEmptyDeployBoard: false,
};
await factory({
propsData: {
environments: [mockItem],
canCreateDeployment: false,
canReadEnvironment: true,
canaryDeploymentFeatureId: 'canary_deployment',
showCanaryDeploymentCallout: true,
userCalloutsPath: '/callouts',
lockPromotionSvgPath: '/assets/illustrations/lock-promotion.svg',
helpCanaryDeploymentsPath: 'help/canary-deployments',
},
});
wrapper.find(DeployBoard).vm.$emit('changeCanaryWeight', 40);
await wrapper.vm.$nextTick();
expect(wrapper.find(CanaryUpdateModal).props()).toMatchObject({
weight: 40,
environment: mockItem,
});
});
});
......@@ -32,6 +32,9 @@ export const deployBoardMockData = {
rollback_url: 'url',
completion: 100,
status: 'found',
canary_ingress: {
canary_weight: 50,
},
};
export const environment = {
......
......@@ -4996,6 +4996,30 @@ msgstr ""
msgid "Canary weight must be specified and valid range (0..100)."
msgstr ""
msgid "CanaryIngress|%{boldStart}Canary:%{boldEnd} %{canary}"
msgstr ""
msgid "CanaryIngress|%{boldStart}Stable:%{boldEnd} %{stable}"
msgstr ""
msgid "CanaryIngress|Canary"
msgstr ""
msgid "CanaryIngress|Change ratio"
msgstr ""
msgid "CanaryIngress|Change the ratio of canary deployments?"
msgstr ""
msgid "CanaryIngress|Doing so will set a deployment change in progress. This temporarily blocks any further configuration until the deployment is finished."
msgstr ""
msgid "CanaryIngress|Stable"
msgstr ""
msgid "CanaryIngress|You are changing the ratio of the canary rollout for %{environment} compared to the stable deployment to:"
msgstr ""
msgid "Cancel"
msgstr ""
......@@ -25937,6 +25961,9 @@ msgstr ""
msgid "Something went wrong, unable to search projects"
msgstr ""
msgid "Something went wrong. Please try again later"
msgstr ""
msgid "Something went wrong. Please try again."
msgstr ""
......
......@@ -454,10 +454,10 @@ RSpec.describe 'Environments page', :js do
expect(page).to have_content 'review-1'
expect(page).to have_content 'review-2'
within('.ci-table') do
within('.gl-responsive-table-row:nth-child(3)') do
within('[data-qa-selector="environment_item"]', text: 'review-1') do
expect(find('.js-auto-stop').text).not_to be_empty
end
within('.gl-responsive-table-row:nth-child(4)') do
within('[data-qa-selector="environment_item"]', text: 'review-2') do
expect(find('.js-auto-stop').text).not_to be_empty
end
end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment