Commit be0e2f6e authored by Phil Hughes's avatar Phil Hughes

Merge branch '60852-update-or-upgrade' into 'master'

Use "update" instead of "upgrade" in Clusters -> Managed Apps

Closes #60852

See merge request gitlab-org/gitlab-ce!29207
parents 431f33fe 940c812d
......@@ -142,8 +142,7 @@ export default class Clusters {
addListeners() {
if (this.showTokenButton) this.showTokenButton.addEventListener('click', this.showToken);
eventHub.$on('installApplication', this.installApplication);
eventHub.$on('upgradeApplication', data => this.upgradeApplication(data));
eventHub.$on('dismissUpgradeSuccess', appId => this.dismissUpgradeSuccess(appId));
eventHub.$on('updateApplication', data => this.updateApplication(data));
eventHub.$on('saveKnativeDomain', data => this.saveKnativeDomain(data));
eventHub.$on('setKnativeHostname', data => this.setKnativeHostname(data));
eventHub.$on('uninstallApplication', data => this.uninstallApplication(data));
......@@ -155,8 +154,7 @@ export default class Clusters {
removeListeners() {
if (this.showTokenButton) this.showTokenButton.removeEventListener('click', this.showToken);
eventHub.$off('installApplication', this.installApplication);
eventHub.$off('upgradeApplication', this.upgradeApplication);
eventHub.$off('dismissUpgradeSuccess', this.dismissUpgradeSuccess);
eventHub.$off('updateApplication', this.updateApplication);
eventHub.$off('saveKnativeDomain');
eventHub.$off('setKnativeHostname');
eventHub.$off('uninstallApplication');
......@@ -331,19 +329,13 @@ export default class Clusters {
});
}
upgradeApplication(data) {
const appId = data.id;
updateApplication({ id: appId, params }) {
this.store.updateApplication(appId);
this.service.installApplication(appId, data.params).catch(() => {
this.service.installApplication(appId, params).catch(() => {
this.store.notifyUpdateFailure(appId);
});
}
dismissUpgradeSuccess(appId) {
this.store.acknowledgeSuccessfulUpdate(appId);
}
toggleIngressDomainHelpText({ externalIp }, { externalIp: newExternalIp }) {
if (externalIp !== newExternalIp) {
this.ingressDomainHelpText.classList.toggle('hide', !newExternalIp);
......
......@@ -2,7 +2,7 @@
/* eslint-disable vue/require-default-prop */
import { GlLink, GlModalDirective } from '@gitlab/ui';
import TimeagoTooltip from '../../vue_shared/components/time_ago_tooltip.vue';
import { s__, sprintf } from '../../locale';
import { s__, __, sprintf } from '~/locale';
import eventHub from '../event_hub';
import identicon from '../../vue_shared/components/identicon.vue';
import loadingButton from '../../vue_shared/components/loading_button.vue';
......@@ -85,7 +85,7 @@ export default {
type: String,
required: false,
},
upgradeAvailable: {
updateAvailable: {
type: Boolean,
required: false,
},
......@@ -113,11 +113,6 @@ export default {
required: false,
default: false,
},
updateAcknowledged: {
type: Boolean,
required: false,
default: true,
},
installApplicationRequestParams: {
type: Object,
required: false,
......@@ -174,11 +169,11 @@ export default {
installButtonLabel() {
let label;
if (this.canInstall) {
label = s__('ClusterIntegration|Install');
label = __('Install');
} else if (this.isInstalling) {
label = s__('ClusterIntegration|Installing');
label = __('Installing');
} else if (this.installed) {
label = s__('ClusterIntegration|Installed');
label = __('Installed');
}
return label;
......@@ -187,7 +182,7 @@ export default {
return this.manageLink && this.status === APPLICATION_STATUS.INSTALLED;
},
manageButtonLabel() {
return s__('ClusterIntegration|Manage');
return __('Manage');
},
hasError() {
return this.installFailed || this.uninstallFailed;
......@@ -207,42 +202,42 @@ export default {
},
versionLabel() {
if (this.updateFailed) {
return s__('ClusterIntegration|Upgrade failed');
} else if (this.isUpgrading) {
return s__('ClusterIntegration|Upgrading');
return __('Update failed');
} else if (this.isUpdating) {
return __('Updating');
}
return s__('ClusterIntegration|Upgraded');
return __('Updated');
},
upgradeFailureDescription() {
updateFailureDescription() {
return s__('ClusterIntegration|Update failed. Please check the logs and try again.');
},
upgradeSuccessDescription() {
return sprintf(s__('ClusterIntegration|%{title} upgraded successfully.'), {
updateSuccessDescription() {
return sprintf(s__('ClusterIntegration|%{title} updated successfully.'), {
title: this.title,
});
},
upgradeButtonLabel() {
updateButtonLabel() {
let label;
if (this.upgradeAvailable && !this.updateFailed && !this.isUpgrading) {
label = s__('ClusterIntegration|Upgrade');
} else if (this.isUpgrading) {
label = s__('ClusterIntegration|Updating');
if (this.updateAvailable && !this.updateFailed && !this.isUpdating) {
label = __('Update');
} else if (this.isUpdating) {
label = __('Updating');
} else if (this.updateFailed) {
label = s__('ClusterIntegration|Retry update');
label = __('Retry update');
}
return label;
},
isUpgrading() {
isUpdating() {
// Since upgrading is handled asynchronously on the backend we need this check to prevent any delay on the frontend
return this.status === APPLICATION_STATUS.UPDATING;
},
shouldShowUpgradeDetails() {
shouldShowUpdateDetails() {
// This method only returns true when;
// Upgrade was successful OR Upgrade failed
// AND new upgrade is unavailable AND version information is present.
return (this.updateSuccessful || this.updateFailed) && !this.upgradeAvailable && this.version;
// Update was successful OR Update failed
// AND new update is unavailable AND version information is present.
return (this.updateSuccessful || this.updateFailed) && !this.updateAvailable && this.version;
},
uninstallSuccessDescription() {
return sprintf(s__('ClusterIntegration|%{title} uninstalled successfully.'), {
......@@ -253,7 +248,7 @@ export default {
watch: {
updateSuccessful(updateSuccessful) {
if (updateSuccessful) {
this.$toast.show(this.upgradeSuccessDescription);
this.$toast.show(this.updateSuccessDescription);
}
},
uninstallSuccessful(uninstallSuccessful) {
......@@ -269,8 +264,8 @@ export default {
params: this.installApplicationRequestParams,
});
},
upgradeClicked() {
eventHub.$emit('upgradeApplication', {
updateClicked() {
eventHub.$emit('updateApplication', {
id: this.id,
params: this.installApplicationRequestParams,
});
......@@ -332,8 +327,8 @@ export default {
<div v-if="updateable">
<div
v-if="shouldShowUpgradeDetails"
class="form-text text-muted label p-0 js-cluster-application-upgrade-details"
v-if="shouldShowUpdateDetails"
class="form-text text-muted label p-0 js-cluster-application-update-details"
>
{{ versionLabel }}
<span v-if="updateSuccessful">to</span>
......@@ -342,24 +337,24 @@ export default {
v-if="updateSuccessful"
:href="chartRepo"
target="_blank"
class="js-cluster-application-upgrade-version"
class="js-cluster-application-update-version"
>chart v{{ version }}</gl-link
>
</div>
<div
v-if="updateFailed && !isUpgrading"
class="bs-callout bs-callout-danger cluster-application-banner mt-2 mb-0 js-cluster-application-upgrade-failure-message"
v-if="updateFailed && !isUpdating"
class="bs-callout bs-callout-danger cluster-application-banner mt-2 mb-0 js-cluster-application-update-details"
>
{{ upgradeFailureDescription }}
{{ updateFailureDescription }}
</div>
<loading-button
v-if="upgradeAvailable || updateFailed || isUpgrading"
class="btn btn-primary js-cluster-application-upgrade-button mt-2"
:loading="isUpgrading"
:disabled="isUpgrading"
:label="upgradeButtonLabel"
@click="upgradeClicked"
v-if="updateAvailable || updateFailed || isUpdating"
class="btn btn-primary js-cluster-application-update-button mt-2"
:loading="isUpdating"
:disabled="isUpdating"
:label="updateButtonLabel"
@click="updateClicked"
/>
</div>
</div>
......
......@@ -376,7 +376,7 @@ export default {
:request-reason="applications.runner.requestReason"
:version="applications.runner.version"
:chart-repo="applications.runner.chartRepo"
:upgrade-available="applications.runner.upgradeAvailable"
:update-available="applications.runner.updateAvailable"
:installed="applications.runner.installed"
:install-failed="applications.runner.installFailed"
:update-successful="applications.runner.updateSuccessful"
......
......@@ -123,7 +123,6 @@ const applicationStateMachine = {
target: INSTALLED,
effects: {
updateSuccessful: true,
updateAcknowledged: false,
},
},
[UPDATE_ERRORED]: {
......
......@@ -56,8 +56,7 @@ export default class ClusterStore {
title: s__('ClusterIntegration|GitLab Runner'),
version: null,
chartRepo: 'https://gitlab.com/charts/gitlab-runner',
upgradeAvailable: null,
updateAcknowledged: true,
updateAvailable: null,
updateSuccessful: false,
updateFailed: false,
},
......@@ -136,10 +135,6 @@ export default class ClusterStore {
this.state.applications[appId] = transitionApplicationState(currentAppState, event);
}
acknowledgeSuccessfulUpdate(appId) {
this.state.applications[appId].updateAcknowledged = true;
}
updateAppProperty(appId, prop, value) {
this.state.applications[appId][prop] = value;
}
......@@ -154,7 +149,7 @@ export default class ClusterStore {
status,
status_reason: statusReason,
version,
update_available: upgradeAvailable,
update_available: updateAvailable,
can_uninstall: uninstallable,
} = serverAppEntry;
const currentApplicationState = this.state.applications[appId] || {};
......@@ -191,7 +186,7 @@ export default class ClusterStore {
serverAppEntry.external_hostname || this.state.applications.knative.externalHostname;
} else if (appId === RUNNER) {
this.state.applications.runner.version = version;
this.state.applications.runner.upgradeAvailable = upgradeAvailable;
this.state.applications.runner.updateAvailable = updateAvailable;
}
});
}
......
......@@ -2136,7 +2136,7 @@ msgstr ""
msgid "ClusterIntegration|%{title} uninstalled successfully."
msgstr ""
msgid "ClusterIntegration|%{title} upgraded successfully."
msgid "ClusterIntegration|%{title} updated successfully."
msgstr ""
msgid "ClusterIntegration|A service token scoped to %{code}kube-system%{end_code} with %{code}cluster-admin%{end_code} privileges."
......@@ -2331,15 +2331,6 @@ msgstr ""
msgid "ClusterIntegration|Ingress gives you a way to route requests to services based on the request host or path, centralizing a number of services into a single entrypoint."
msgstr ""
msgid "ClusterIntegration|Install"
msgstr ""
msgid "ClusterIntegration|Installed"
msgstr ""
msgid "ClusterIntegration|Installing"
msgstr ""
msgid "ClusterIntegration|Installing Ingress may incur additional costs. Learn more about %{pricingLink}."
msgstr ""
......@@ -2427,9 +2418,6 @@ msgstr ""
msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create Kubernetes clusters"
msgstr ""
msgid "ClusterIntegration|Manage"
msgstr ""
msgid "ClusterIntegration|Manage your Kubernetes cluster by visiting %{link_gke}"
msgstr ""
......@@ -2496,9 +2484,6 @@ msgstr ""
msgid "ClusterIntegration|Request to begin uninstalling failed"
msgstr ""
msgid "ClusterIntegration|Retry update"
msgstr ""
msgid "ClusterIntegration|Save changes"
msgstr ""
......@@ -2598,21 +2583,6 @@ msgstr ""
msgid "ClusterIntegration|Update failed. Please check the logs and try again."
msgstr ""
msgid "ClusterIntegration|Updating"
msgstr ""
msgid "ClusterIntegration|Upgrade"
msgstr ""
msgid "ClusterIntegration|Upgrade failed"
msgstr ""
msgid "ClusterIntegration|Upgraded"
msgstr ""
msgid "ClusterIntegration|Upgrading"
msgstr ""
msgid "ClusterIntegration|Validating project billing status"
msgstr ""
......@@ -5339,6 +5309,9 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
msgid "Install"
msgstr ""
msgid "Install GitLab Runner"
msgstr ""
......@@ -5348,6 +5321,12 @@ msgstr ""
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
msgid "Installed"
msgstr ""
msgid "Installing"
msgstr ""
msgid "Instance Statistics"
msgstr ""
......@@ -5940,6 +5919,9 @@ msgstr ""
msgid "Makes this issue confidential"
msgstr ""
msgid "Manage"
msgstr ""
msgid "Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki."
msgstr ""
......@@ -8535,6 +8517,9 @@ msgstr ""
msgid "Retry this job in order to create the necessary resources."
msgstr ""
msgid "Retry update"
msgstr ""
msgid "Retry verification"
msgstr ""
......@@ -11011,6 +10996,9 @@ msgstr ""
msgid "UpdateProject|Project could not be updated!"
msgstr ""
msgid "Updated"
msgstr ""
msgid "Updating"
msgstr ""
......
......@@ -8,8 +8,8 @@ module QA
class Show < Page::Base
view 'app/assets/javascripts/clusters/components/application_row.vue' do
element :application_row, 'js-cluster-application-row-${this.id}' # rubocop:disable QA/ElementWithPattern
element :install_button, "s__('ClusterIntegration|Install')" # rubocop:disable QA/ElementWithPattern
element :installed_button, "s__('ClusterIntegration|Installed')" # rubocop:disable QA/ElementWithPattern
element :install_button, "__('Install')" # rubocop:disable QA/ElementWithPattern
element :installed_button, "__('Installed')" # rubocop:disable QA/ElementWithPattern
end
view 'app/assets/javascripts/clusters/components/applications.vue' do
......
import Clusters from '~/clusters/clusters_bundle';
import { APPLICATION_STATUS, INGRESS_DOMAIN_SUFFIX, APPLICATIONS } from '~/clusters/constants';
import {
APPLICATION_STATUS,
INGRESS_DOMAIN_SUFFIX,
APPLICATIONS,
RUNNER,
} from '~/clusters/constants';
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import { loadHTMLFixture } from 'helpers/fixtures';
......@@ -353,4 +358,30 @@ describe('Clusters', () => {
});
});
});
describe('updateApplication', () => {
const params = { version: '1.0.0' };
let storeUpdateApplication;
let installApplication;
beforeEach(() => {
storeUpdateApplication = jest.spyOn(cluster.store, 'updateApplication');
installApplication = jest.spyOn(cluster.service, 'installApplication');
cluster.updateApplication({ id: RUNNER, params });
});
afterEach(() => {
storeUpdateApplication.mockRestore();
installApplication.mockRestore();
});
it('calls store updateApplication method', () => {
expect(storeUpdateApplication).toHaveBeenCalledWith(RUNNER);
});
it('sends installApplication request', () => {
expect(installApplication).toHaveBeenCalledWith(RUNNER, params);
});
});
});
......@@ -245,26 +245,26 @@ describe('Application Row', () => {
});
});
describe('Upgrade button', () => {
describe('Update button', () => {
it('has indeterminate state on page load', () => {
vm = mountComponent(ApplicationRow, {
...DEFAULT_APPLICATION_STATE,
status: null,
});
const upgradeBtn = vm.$el.querySelector('.js-cluster-application-upgrade-button');
const updateBtn = vm.$el.querySelector('.js-cluster-application-update-button');
expect(upgradeBtn).toBe(null);
expect(updateBtn).toBe(null);
});
it('has enabled "Upgrade" when "upgradeAvailable" is true', () => {
it('has enabled "Update" when "updateAvailable" is true', () => {
vm = mountComponent(ApplicationRow, {
...DEFAULT_APPLICATION_STATE,
upgradeAvailable: true,
updateAvailable: true,
});
const upgradeBtn = vm.$el.querySelector('.js-cluster-application-upgrade-button');
const updateBtn = vm.$el.querySelector('.js-cluster-application-update-button');
expect(upgradeBtn).not.toBe(null);
expect(upgradeBtn.innerHTML).toContain('Upgrade');
expect(updateBtn).not.toBe(null);
expect(updateBtn.innerHTML).toContain('Update');
});
it('has enabled "Retry update" when update process fails', () => {
......@@ -273,10 +273,10 @@ describe('Application Row', () => {
status: APPLICATION_STATUS.INSTALLED,
updateFailed: true,
});
const upgradeBtn = vm.$el.querySelector('.js-cluster-application-upgrade-button');
const updateBtn = vm.$el.querySelector('.js-cluster-application-update-button');
expect(upgradeBtn).not.toBe(null);
expect(upgradeBtn.innerHTML).toContain('Retry update');
expect(updateBtn).not.toBe(null);
expect(updateBtn.innerHTML).toContain('Retry update');
});
it('has disabled "Updating" when APPLICATION_STATUS.UPDATING', () => {
......@@ -284,53 +284,51 @@ describe('Application Row', () => {
...DEFAULT_APPLICATION_STATE,
status: APPLICATION_STATUS.UPDATING,
});
const upgradeBtn = vm.$el.querySelector('.js-cluster-application-upgrade-button');
const updateBtn = vm.$el.querySelector('.js-cluster-application-update-button');
expect(upgradeBtn).not.toBe(null);
expect(vm.isUpgrading).toBe(true);
expect(upgradeBtn.innerHTML).toContain('Updating');
expect(updateBtn).not.toBe(null);
expect(vm.isUpdating).toBe(true);
expect(updateBtn.innerHTML).toContain('Updating');
});
it('clicking upgrade button emits event', () => {
it('clicking update button emits event', () => {
jest.spyOn(eventHub, '$emit');
vm = mountComponent(ApplicationRow, {
...DEFAULT_APPLICATION_STATE,
status: APPLICATION_STATUS.INSTALLED,
upgradeAvailable: true,
updateAvailable: true,
});
const upgradeBtn = vm.$el.querySelector('.js-cluster-application-upgrade-button');
const updateBtn = vm.$el.querySelector('.js-cluster-application-update-button');
upgradeBtn.click();
updateBtn.click();
expect(eventHub.$emit).toHaveBeenCalledWith('upgradeApplication', {
expect(eventHub.$emit).toHaveBeenCalledWith('updateApplication', {
id: DEFAULT_APPLICATION_STATE.id,
params: {},
});
});
it('clicking disabled upgrade button emits nothing', () => {
it('clicking disabled update button emits nothing', () => {
jest.spyOn(eventHub, '$emit');
vm = mountComponent(ApplicationRow, {
...DEFAULT_APPLICATION_STATE,
status: APPLICATION_STATUS.UPDATING,
});
const upgradeBtn = vm.$el.querySelector('.js-cluster-application-upgrade-button');
const updateBtn = vm.$el.querySelector('.js-cluster-application-update-button');
upgradeBtn.click();
updateBtn.click();
expect(eventHub.$emit).not.toHaveBeenCalled();
});
it('displays an error message if application upgrade failed', () => {
it('displays an error message if application update failed', () => {
vm = mountComponent(ApplicationRow, {
...DEFAULT_APPLICATION_STATE,
title: 'GitLab Runner',
status: APPLICATION_STATUS.INSTALLED,
updateFailed: true,
});
const failureMessage = vm.$el.querySelector(
'.js-cluster-application-upgrade-failure-message',
);
const failureMessage = vm.$el.querySelector('.js-cluster-application-update-details');
expect(failureMessage).not.toBe(null);
expect(failureMessage.innerHTML).toContain(
......@@ -338,7 +336,7 @@ describe('Application Row', () => {
);
});
it('displays a success toast message if application upgrade was successful', () => {
it('displays a success toast message if application update was successful', () => {
vm = mountComponent(ApplicationRow, {
...DEFAULT_APPLICATION_STATE,
title: 'GitLab Runner',
......@@ -349,13 +347,13 @@ describe('Application Row', () => {
vm.updateSuccessful = true;
return vm.$nextTick(() => {
expect(vm.$toast.show).toHaveBeenCalledWith('GitLab Runner upgraded successfully.');
expect(vm.$toast.show).toHaveBeenCalledWith('GitLab Runner updated successfully.');
});
});
});
describe('Version', () => {
it('displays a version number if application has been upgraded', () => {
it('displays a version number if application has been updated', () => {
const version = '0.1.45';
vm = mountComponent(ApplicationRow, {
...DEFAULT_APPLICATION_STATE,
......@@ -363,15 +361,15 @@ describe('Application Row', () => {
updateSuccessful: true,
version,
});
const upgradeDetails = vm.$el.querySelector('.js-cluster-application-upgrade-details');
const versionEl = vm.$el.querySelector('.js-cluster-application-upgrade-version');
const updateDetails = vm.$el.querySelector('.js-cluster-application-update-details');
const versionEl = vm.$el.querySelector('.js-cluster-application-update-version');
expect(upgradeDetails.innerHTML).toContain('Upgraded');
expect(updateDetails.innerHTML).toContain('Updated');
expect(versionEl).not.toBe(null);
expect(versionEl.innerHTML).toContain(version);
});
it('contains a link to the chart repo if application has been upgraded', () => {
it('contains a link to the chart repo if application has been updated', () => {
const version = '0.1.45';
const chartRepo = 'https://gitlab.com/charts/gitlab-runner';
vm = mountComponent(ApplicationRow, {
......@@ -381,13 +379,13 @@ describe('Application Row', () => {
chartRepo,
version,
});
const versionEl = vm.$el.querySelector('.js-cluster-application-upgrade-version');
const versionEl = vm.$el.querySelector('.js-cluster-application-update-version');
expect(versionEl.href).toEqual(chartRepo);
expect(versionEl.target).toEqual('_blank');
});
it('does not display a version number if application upgrade failed', () => {
it('does not display a version number if application update failed', () => {
const version = '0.1.45';
vm = mountComponent(ApplicationRow, {
...DEFAULT_APPLICATION_STATE,
......@@ -395,10 +393,10 @@ describe('Application Row', () => {
updateFailed: true,
version,
});
const upgradeDetails = vm.$el.querySelector('.js-cluster-application-upgrade-details');
const versionEl = vm.$el.querySelector('.js-cluster-application-upgrade-version');
const updateDetails = vm.$el.querySelector('.js-cluster-application-update-details');
const versionEl = vm.$el.querySelector('.js-cluster-application-update-version');
expect(upgradeDetails.innerHTML).toContain('failed');
expect(updateDetails.innerHTML).toContain('failed');
expect(versionEl).toBe(null);
});
});
......
......@@ -127,7 +127,7 @@ describe('applicationStateMachine', () => {
describe(`current state is ${UPDATING}`, () => {
it.each`
expectedState | event | effects
${INSTALLED} | ${UPDATED} | ${{ updateSuccessful: true, updateAcknowledged: false }}
${INSTALLED} | ${UPDATED} | ${{ updateSuccessful: true }}
${INSTALLED} | ${UPDATE_ERRORED} | ${{ updateFailed: true }}
`(`transitions to $expectedState on $event event and applies $effects`, data => {
const { expectedState, event, effects } = data;
......
......@@ -85,11 +85,10 @@ describe('Clusters Store', () => {
statusReason: mockResponseData.applications[2].status_reason,
requestReason: null,
version: mockResponseData.applications[2].version,
upgradeAvailable: mockResponseData.applications[2].update_available,
updateAvailable: mockResponseData.applications[2].update_available,
chartRepo: 'https://gitlab.com/charts/gitlab-runner',
installed: false,
installFailed: false,
updateAcknowledged: true,
updateFailed: false,
updateSuccessful: false,
uninstallable: false,
......
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