@rancher/shell 3.0.10 → 3.0.12-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/styles/base/_mixins.scss +31 -0
- package/assets/styles/base/_variables.scss +2 -0
- package/assets/styles/themes/_modern.scss +6 -5
- package/assets/translations/en-us.yaml +12 -9
- package/assets/translations/zh-hans.yaml +0 -3
- package/chart/__tests__/rancher-backup-index.test.ts +248 -0
- package/chart/rancher-backup/index.vue +41 -2
- package/components/BrandImage.vue +6 -5
- package/components/ConsumptionGauge.vue +12 -4
- package/components/DynamicContent/DynamicContentIcon.vue +3 -2
- package/components/EmptyProductPage.vue +76 -0
- package/components/ExplorerProjectsNamespaces.vue +1 -4
- package/components/LazyImage.vue +2 -1
- package/components/Resource/Detail/Card/Scaler.vue +4 -4
- package/components/Resource/Detail/CopyToClipboard.vue +1 -2
- package/components/Resource/Detail/Metadata/KeyValueRow.vue +9 -3
- package/components/Resource/Detail/TitleBar/__tests__/__snapshots__/index.test.ts.snap +31 -0
- package/components/Resource/Detail/TitleBar/__tests__/index.test.ts +45 -1
- package/components/Resource/Detail/TitleBar/index.vue +1 -1
- package/components/Resource/Detail/ViewOptions/__tests__/__snapshots__/index.test.ts.snap +9 -0
- package/components/Resource/Detail/ViewOptions/__tests__/index.test.ts +62 -0
- package/components/Resource/Detail/ViewOptions/index.vue +2 -1
- package/components/ResourceList/Masthead.vue +25 -2
- package/components/SideNav.vue +13 -0
- package/components/Tabbed/index.vue +6 -0
- package/components/__tests__/ConsumptionGauge.test.ts +31 -0
- package/components/__tests__/PromptModal.test.ts +2 -0
- package/components/fleet/FleetClusters.vue +1 -0
- package/components/fleet/__tests__/FleetClusters.test.ts +71 -0
- package/components/form/NodeScheduling.vue +17 -3
- package/components/form/PrivateRegistry.vue +69 -0
- package/components/form/ProjectMemberEditor.vue +0 -10
- package/components/form/__tests__/PrivateRegistry.test.ts +133 -0
- package/components/formatter/WorkloadHealthScale.vue +3 -1
- package/components/nav/Group.vue +26 -3
- package/components/nav/Header.vue +32 -7
- package/components/nav/TopLevelMenu.helper.ts +7 -79
- package/components/nav/TopLevelMenu.vue +15 -1
- package/components/nav/__tests__/TopLevelMenu.helper.test.ts +2 -53
- package/config/pagination-table-headers.js +8 -1
- package/config/private-label.js +2 -1
- package/config/product/apps.js +3 -1
- package/config/product/auth.js +1 -0
- package/config/product/backup.js +1 -0
- package/config/product/compliance.js +1 -1
- package/config/product/explorer.js +25 -6
- package/config/product/fleet.js +1 -0
- package/config/product/gatekeeper.js +1 -0
- package/config/product/istio.js +1 -0
- package/config/product/logging.js +1 -0
- package/config/product/longhorn.js +2 -1
- package/config/product/manager.js +1 -0
- package/config/product/monitoring.js +1 -0
- package/config/product/navlinks.js +1 -0
- package/config/product/neuvector.js +2 -1
- package/config/product/settings.js +1 -0
- package/config/product/uiplugins.js +1 -0
- package/core/__tests__/extension-manager-impl.test.js +187 -2
- package/core/__tests__/plugin-products-helpers.test.ts +454 -0
- package/core/__tests__/plugin-products.test.ts +3219 -0
- package/core/extension-manager-impl.js +34 -3
- package/core/plugin-helpers.ts +31 -0
- package/core/plugin-products-base.ts +375 -0
- package/core/plugin-products-extending.ts +44 -0
- package/core/plugin-products-helpers.ts +262 -0
- package/core/plugin-products-top-level.ts +66 -0
- package/core/plugin-products-type-guards.ts +33 -0
- package/core/plugin-products.ts +50 -0
- package/core/plugin-types.ts +222 -0
- package/core/plugin.ts +45 -10
- package/core/productDebugger.js +48 -0
- package/core/types.ts +95 -11
- package/detail/__tests__/__snapshots__/fleet.cattle.io.bundle.test.ts.snap +52 -0
- package/detail/__tests__/fleet.cattle.io.bundle.test.ts +171 -0
- package/detail/__tests__/node.test.ts +83 -0
- package/detail/fleet.cattle.io.bundle.vue +21 -34
- package/detail/management.cattle.io.oidcclient.vue +2 -1
- package/detail/node.vue +1 -0
- package/dialog/ExtensionCatalogInstallDialog.vue +1 -1
- package/dialog/InstallExtensionDialog.vue +6 -27
- package/dialog/UninstallExistingExtensionDialog.vue +141 -0
- package/dialog/UninstallExtensionDialog.vue +4 -26
- package/dialog/__tests__/UninstallExistingExtensionDialog.test.ts +114 -0
- package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +1 -0
- package/edit/catalog.cattle.io.clusterrepo.vue +17 -3
- package/edit/cloudcredential.vue +2 -1
- package/edit/monitoring.coreos.com.alertmanagerconfig/receiverConfig.vue +11 -6
- package/edit/provisioning.cattle.io.cluster/__tests__/Ingress.test.ts +176 -0
- package/edit/provisioning.cattle.io.cluster/index.vue +5 -4
- package/edit/provisioning.cattle.io.cluster/rke2.vue +4 -1
- package/edit/provisioning.cattle.io.cluster/shared.ts +4 -2
- package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +6 -0
- package/edit/provisioning.cattle.io.cluster/tabs/Ingress.vue +7 -2
- package/edit/secret/generic.vue +1 -0
- package/edit/secret/index.vue +2 -1
- package/edit/service.vue +2 -14
- package/list/management.cattle.io.feature.vue +7 -1
- package/list/provisioning.cattle.io.cluster.vue +0 -50
- package/list/workload.vue +11 -4
- package/mixins/brand.js +2 -1
- package/mixins/resource-fetch.js +12 -3
- package/models/catalog.cattle.io.clusterrepo.js +9 -0
- package/models/cluster.x-k8s.io.machinedeployment.js +8 -3
- package/models/management.cattle.io.authconfig.js +2 -1
- package/models/management.cattle.io.cluster.js +4 -3
- package/models/monitoring.coreos.com.receiver.js +11 -6
- package/models/pod.js +18 -0
- package/models/provisioning.cattle.io.cluster.js +2 -2
- package/models/workload.js +20 -2
- package/package.json +5 -6
- package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +0 -1
- package/pages/c/_cluster/apps/charts/index.vue +3 -8
- package/pages/c/_cluster/apps/charts/install.vue +8 -9
- package/pages/c/_cluster/istio/index.vue +4 -2
- package/pages/c/_cluster/longhorn/index.vue +2 -1
- package/pages/c/_cluster/monitoring/index.vue +2 -2
- package/pages/c/_cluster/neuvector/index.vue +2 -1
- package/pages/c/_cluster/settings/brand.vue +4 -4
- package/pages/c/_cluster/settings/performance.vue +0 -5
- package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +2 -1
- package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +231 -13
- package/pages/c/_cluster/uiplugins/index.vue +145 -38
- package/plugins/dashboard-store/__tests__/resource-class.test.ts +1 -0
- package/plugins/dashboard-store/actions.js +3 -2
- package/plugins/dashboard-store/resource-class.js +62 -6
- package/plugins/plugin.js +16 -0
- package/plugins/steve/steve-pagination-utils.ts +8 -2
- package/plugins/steve/subscribe.js +29 -4
- package/rancher-components/RcButton/RcButton.vue +3 -3
- package/rancher-components/RcButtonSplit/RcButtonSplit.test.ts +253 -0
- package/rancher-components/RcButtonSplit/RcButtonSplit.vue +158 -0
- package/rancher-components/RcButtonSplit/index.ts +1 -0
- package/scripts/test-plugins-build.sh +4 -4
- package/scripts/typegen.sh +13 -1
- package/store/__tests__/type-map.test.ts +84 -24
- package/store/type-map.js +42 -3
- package/tsconfig.paths.json +1 -0
- package/types/resources/pod.ts +18 -0
- package/types/shell/index.d.ts +8506 -2908
- package/types/store/dashboard-store.types.ts +5 -0
- package/types/store/pagination.types.ts +6 -0
- package/utils/__tests__/require-asset.test.ts +98 -0
- package/utils/async.ts +1 -5
- package/utils/axios.js +1 -4
- package/utils/brand.ts +3 -1
- package/utils/dynamic-importer.js +3 -2
- package/utils/favicon.js +4 -3
- package/utils/pagination-utils.ts +1 -1
- package/utils/require-asset.ts +95 -0
- package/utils/uiplugins.ts +12 -16
- package/utils/validators/__tests__/private-registry.test.ts +76 -0
- package/utils/validators/private-registry.ts +28 -0
- package/vue.config.js +4 -3
- package/components/HarvesterServiceAddOnConfig.vue +0 -207
|
@@ -5,12 +5,16 @@ import FleetUtils from '@shell/utils/fleet';
|
|
|
5
5
|
import { checkSchemasForFindAllHash } from '@shell/utils/auth';
|
|
6
6
|
import Loading from '@shell/components/Loading.vue';
|
|
7
7
|
import { FLEET as FLEET_ANNOTATIONS } from '@shell/config/labels-annotations';
|
|
8
|
+
import ResourceTabs from '@shell/components/form/ResourceTabs';
|
|
9
|
+
import Tab from '@shell/components/Tabbed/Tab';
|
|
8
10
|
|
|
9
11
|
export default {
|
|
10
12
|
name: 'FleetBundleDetail',
|
|
11
13
|
|
|
12
|
-
components: {
|
|
13
|
-
|
|
14
|
+
components: {
|
|
15
|
+
Loading, FleetResources, ResourceTabs, Tab
|
|
16
|
+
},
|
|
17
|
+
props: {
|
|
14
18
|
value: {
|
|
15
19
|
type: Object,
|
|
16
20
|
required: true,
|
|
@@ -82,42 +86,25 @@ export default {
|
|
|
82
86
|
return res;
|
|
83
87
|
}, []);
|
|
84
88
|
},
|
|
85
|
-
resourceCount() {
|
|
86
|
-
return this.bundleResources.length;
|
|
87
|
-
},
|
|
88
89
|
}
|
|
89
90
|
};
|
|
90
91
|
|
|
91
92
|
</script>
|
|
92
93
|
|
|
93
94
|
<template>
|
|
94
|
-
<
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
95
|
+
<Loading v-if="$fetchState.pending" />
|
|
96
|
+
<ResourceTabs
|
|
97
|
+
v-else
|
|
98
|
+
:value="value"
|
|
99
|
+
mode="view"
|
|
100
|
+
:need-related="false"
|
|
101
|
+
>
|
|
102
|
+
<Tab
|
|
103
|
+
label="Resources"
|
|
104
|
+
name="resources"
|
|
105
|
+
:weight="20"
|
|
106
|
+
>
|
|
107
|
+
<FleetResources :rows="bundleResources" />
|
|
108
|
+
</Tab>
|
|
109
|
+
</ResourceTabs>
|
|
105
110
|
</template>
|
|
106
|
-
|
|
107
|
-
<style lang="scss" scoped>
|
|
108
|
-
.bundle-title {
|
|
109
|
-
display: flex;
|
|
110
|
-
align-items: center;
|
|
111
|
-
|
|
112
|
-
h2 {
|
|
113
|
-
margin: 0 10px 0 0;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
span {
|
|
117
|
-
background-color: var(--darker);
|
|
118
|
-
color: var(--default);
|
|
119
|
-
padding: 5px 10px;
|
|
120
|
-
border-radius: 15px;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
</style>
|
|
@@ -9,6 +9,7 @@ import DateComponent from '@shell/components/formatter/Date.vue';
|
|
|
9
9
|
import { RcItemCard } from '@components/RcItemCard';
|
|
10
10
|
import ActionMenu, { type ActionMenuSelection } from '@shell/components/ActionMenuShell.vue';
|
|
11
11
|
import { Banner } from '@components/Banner';
|
|
12
|
+
import keySvg from '~shell/assets/images/key.svg';
|
|
12
13
|
|
|
13
14
|
type SecretActionType = 'create-secret' | 'regen-secret' | 'remove-secret'
|
|
14
15
|
interface ClientSecretData { createdAt: string, lastUsedAt: string, lastFiveCharacters: string }
|
|
@@ -228,7 +229,7 @@ export default defineComponent({
|
|
|
228
229
|
clientSecrets.push({
|
|
229
230
|
id: oidcSecretDataKey,
|
|
230
231
|
header: { title: { text: oidcSecretDataKey } },
|
|
231
|
-
image: { src:
|
|
232
|
+
image: { src: keySvg },
|
|
232
233
|
createdAt,
|
|
233
234
|
lastFiveCharacters: oidcSecretData.lastFiveCharacters,
|
|
234
235
|
lastUsedAt,
|
package/detail/node.vue
CHANGED
|
@@ -234,6 +234,7 @@ export default {
|
|
|
234
234
|
:resource-name="t('node.detail.glance.consumptionGauge.pods')"
|
|
235
235
|
:capacity="value.podCapacity"
|
|
236
236
|
:used="value.podConsumed"
|
|
237
|
+
:used-label="t('node.detail.glance.consumptionGauge.running')"
|
|
237
238
|
/>
|
|
238
239
|
</div>
|
|
239
240
|
<div class="spacer" />
|
|
@@ -302,7 +302,7 @@ export default {
|
|
|
302
302
|
});
|
|
303
303
|
|
|
304
304
|
if (this.extensionSvc) {
|
|
305
|
-
this.extensionUrl = `http://${ this.extensionSvc.
|
|
305
|
+
this.extensionUrl = `http://${ this.extensionSvc.metadata.name }.${ this.extensionSvc.metadata.namespace }.svc:${ this.extensionSvc.spec.ports[0].port }`;
|
|
306
306
|
} else {
|
|
307
307
|
throw new Error('Error fetching extension service');
|
|
308
308
|
}
|
|
@@ -223,7 +223,7 @@ export default {
|
|
|
223
223
|
|
|
224
224
|
const plugin = this.plugin;
|
|
225
225
|
|
|
226
|
-
this.updateStatus(plugin.
|
|
226
|
+
this.updateStatus(plugin.id, this.action);
|
|
227
227
|
|
|
228
228
|
// Find the version that the user wants to install
|
|
229
229
|
const version = plugin.versions?.find((v) => v.version === this.version);
|
|
@@ -370,31 +370,21 @@ export default {
|
|
|
370
370
|
</template>
|
|
371
371
|
|
|
372
372
|
<style lang="scss" scoped>
|
|
373
|
-
.
|
|
374
|
-
padding: 10px;
|
|
373
|
+
@import '@shell/assets/styles/base/_mixins.scss';
|
|
375
374
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
}
|
|
375
|
+
.plugin-install-dialog {
|
|
376
|
+
@include extension-dialog;
|
|
379
377
|
|
|
380
378
|
.dialog-panel {
|
|
381
|
-
display: flex;
|
|
382
|
-
flex-direction: column;
|
|
383
|
-
min-height: 100px;
|
|
384
|
-
|
|
385
379
|
p {
|
|
386
|
-
margin-bottom:
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
.dialog-info {
|
|
390
|
-
flex: 1;
|
|
380
|
+
margin-bottom: 4px;
|
|
391
381
|
}
|
|
392
382
|
|
|
393
383
|
.toggle-advanced {
|
|
394
384
|
display: flex;
|
|
395
385
|
align-items: center;
|
|
396
386
|
cursor: pointer;
|
|
397
|
-
margin:
|
|
387
|
+
margin: 8px 0;
|
|
398
388
|
|
|
399
389
|
&:hover {
|
|
400
390
|
text-decoration: none;
|
|
@@ -403,19 +393,8 @@ export default {
|
|
|
403
393
|
}
|
|
404
394
|
|
|
405
395
|
.version-selector {
|
|
406
|
-
margin: 0 10px 10px 10px;
|
|
407
396
|
width: auto;
|
|
408
397
|
}
|
|
409
398
|
}
|
|
410
|
-
|
|
411
|
-
.dialog-buttons {
|
|
412
|
-
display: flex;
|
|
413
|
-
justify-content: flex-end;
|
|
414
|
-
margin-top: 10px;
|
|
415
|
-
|
|
416
|
-
> *:not(:last-child) {
|
|
417
|
-
margin-right: 10px;
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
399
|
}
|
|
421
400
|
</style>
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import AsyncButton from '@shell/components/AsyncButton';
|
|
3
|
+
import { CATALOG } from '@shell/config/types';
|
|
4
|
+
import { UI_PLUGIN_NAMESPACE } from '@shell/config/uiplugins';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Dialog shown when user tries to install an extension that is already installed from a different source.
|
|
8
|
+
* Prompts the user to uninstall the existing version first before installing from the new source.
|
|
9
|
+
*/
|
|
10
|
+
export default {
|
|
11
|
+
emits: ['close'],
|
|
12
|
+
|
|
13
|
+
components: { AsyncButton },
|
|
14
|
+
|
|
15
|
+
props: {
|
|
16
|
+
/**
|
|
17
|
+
* The installed plugin that needs to be uninstalled
|
|
18
|
+
*/
|
|
19
|
+
installedPlugin: {
|
|
20
|
+
type: Object,
|
|
21
|
+
default: () => {},
|
|
22
|
+
required: true
|
|
23
|
+
},
|
|
24
|
+
/**
|
|
25
|
+
* Callback to update install status on extensions main screen
|
|
26
|
+
*/
|
|
27
|
+
updateStatus: {
|
|
28
|
+
type: Function,
|
|
29
|
+
default: () => {},
|
|
30
|
+
required: true
|
|
31
|
+
},
|
|
32
|
+
/**
|
|
33
|
+
* Callback when modal is closed
|
|
34
|
+
*/
|
|
35
|
+
closed: {
|
|
36
|
+
type: Function,
|
|
37
|
+
default: () => {},
|
|
38
|
+
required: true
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
data() {
|
|
43
|
+
return { busy: false };
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
methods: {
|
|
47
|
+
closeDialog(result) {
|
|
48
|
+
this.closed(result);
|
|
49
|
+
this.$emit('close');
|
|
50
|
+
},
|
|
51
|
+
async uninstall() {
|
|
52
|
+
this.busy = true;
|
|
53
|
+
|
|
54
|
+
const plugin = this.installedPlugin;
|
|
55
|
+
|
|
56
|
+
this.updateStatus(plugin.id, 'uninstall');
|
|
57
|
+
|
|
58
|
+
// Delete the CR if this is a developer plugin (there is no Helm App, so need to remove the CRD ourselves)
|
|
59
|
+
if (plugin.uiplugin?.isDeveloper) {
|
|
60
|
+
// Delete the custom resource
|
|
61
|
+
await plugin.uiplugin.remove();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Find the app for this plugin using direct lookup (more efficient than findAll)
|
|
65
|
+
let pluginApp = null;
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
const appId = `${ UI_PLUGIN_NAMESPACE }/${ plugin.name }`;
|
|
69
|
+
|
|
70
|
+
pluginApp = await this.$store.dispatch('management/find', {
|
|
71
|
+
type: CATALOG.APP,
|
|
72
|
+
id: appId
|
|
73
|
+
});
|
|
74
|
+
} catch (e) {
|
|
75
|
+
// If the app cannot be found (e.g. already removed), proceed without error
|
|
76
|
+
pluginApp = null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (pluginApp) {
|
|
80
|
+
try {
|
|
81
|
+
await pluginApp.remove();
|
|
82
|
+
} catch (e) {
|
|
83
|
+
this.$store.dispatch('growl/error', {
|
|
84
|
+
title: this.t('plugins.error.generic'),
|
|
85
|
+
message: e.message ? e.message : e,
|
|
86
|
+
timeout: 10000
|
|
87
|
+
}, { root: true });
|
|
88
|
+
|
|
89
|
+
this.busy = false;
|
|
90
|
+
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
await this.$store.dispatch('management/findAll', { type: CATALOG.OPERATION });
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Close the dialog
|
|
98
|
+
this.closeDialog({ uninstalled: true, plugin });
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
</script>
|
|
103
|
+
|
|
104
|
+
<template>
|
|
105
|
+
<div class="plugin-install-dialog">
|
|
106
|
+
<h4 class="mt-10">
|
|
107
|
+
{{ t('plugins.install.alreadyInstalledTitle') }}
|
|
108
|
+
</h4>
|
|
109
|
+
<div class="mt-10 dialog-panel">
|
|
110
|
+
<div class="dialog-info">
|
|
111
|
+
<p>
|
|
112
|
+
{{ t('plugins.install.alreadyInstalledPrompt') }}
|
|
113
|
+
</p>
|
|
114
|
+
</div>
|
|
115
|
+
<div class="dialog-buttons">
|
|
116
|
+
<button
|
|
117
|
+
:disabled="busy"
|
|
118
|
+
class="btn role-secondary"
|
|
119
|
+
data-testid="uninstall-existing-ext-modal-cancel-btn"
|
|
120
|
+
@click="closeDialog(false)"
|
|
121
|
+
>
|
|
122
|
+
{{ t('generic.cancel') }}
|
|
123
|
+
</button>
|
|
124
|
+
<AsyncButton
|
|
125
|
+
mode="uninstall"
|
|
126
|
+
:action-label="t('plugins.install.uninstallExisting')"
|
|
127
|
+
data-testid="uninstall-existing-ext-modal-uninstall-btn"
|
|
128
|
+
@click="uninstall()"
|
|
129
|
+
/>
|
|
130
|
+
</div>
|
|
131
|
+
</div>
|
|
132
|
+
</div>
|
|
133
|
+
</template>
|
|
134
|
+
|
|
135
|
+
<style lang="scss" scoped>
|
|
136
|
+
@import '@shell/assets/styles/base/_mixins.scss';
|
|
137
|
+
|
|
138
|
+
.plugin-install-dialog {
|
|
139
|
+
@include extension-dialog;
|
|
140
|
+
}
|
|
141
|
+
</style>
|
|
@@ -65,7 +65,7 @@ export default {
|
|
|
65
65
|
|
|
66
66
|
const plugin = this.plugin;
|
|
67
67
|
|
|
68
|
-
this.updateStatus(plugin.
|
|
68
|
+
this.updateStatus(plugin.id, 'uninstall');
|
|
69
69
|
|
|
70
70
|
// Delete the CR if this is a developer plugin (there is no Helm App, so need to remove the CRD ourselves)
|
|
71
71
|
if (plugin.uiplugin?.isDeveloper) {
|
|
@@ -132,31 +132,9 @@ export default {
|
|
|
132
132
|
</template>
|
|
133
133
|
|
|
134
134
|
<style lang="scss" scoped>
|
|
135
|
-
.
|
|
136
|
-
padding: 10px;
|
|
137
|
-
|
|
138
|
-
h4 {
|
|
139
|
-
font-weight: bold;
|
|
140
|
-
}
|
|
135
|
+
@import '@shell/assets/styles/base/_mixins.scss';
|
|
141
136
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
flex-direction: column;
|
|
145
|
-
min-height: 100px;
|
|
146
|
-
|
|
147
|
-
.dialog-info {
|
|
148
|
-
flex: 1;
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
.dialog-buttons {
|
|
153
|
-
display: flex;
|
|
154
|
-
justify-content: flex-end;
|
|
155
|
-
margin-top: 10px;
|
|
156
|
-
|
|
157
|
-
> *:not(:last-child) {
|
|
158
|
-
margin-right: 10px;
|
|
159
|
-
}
|
|
160
|
-
}
|
|
137
|
+
.plugin-install-dialog {
|
|
138
|
+
@include extension-dialog;
|
|
161
139
|
}
|
|
162
140
|
</style>
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { shallowMount, VueWrapper } from '@vue/test-utils';
|
|
2
|
+
import UninstallExistingExtensionDialog from '@shell/dialog/UninstallExistingExtensionDialog.vue';
|
|
3
|
+
|
|
4
|
+
const t = (key: string): string => key;
|
|
5
|
+
|
|
6
|
+
describe('component: UninstallExistingExtensionDialog', () => {
|
|
7
|
+
let wrapper: VueWrapper<any>;
|
|
8
|
+
|
|
9
|
+
const mountComponent = (propsData = {}) => {
|
|
10
|
+
const store = { dispatch: jest.fn().mockResolvedValue([]) };
|
|
11
|
+
|
|
12
|
+
const defaultProps = {
|
|
13
|
+
installedPlugin: {
|
|
14
|
+
id: 'test-plugin', name: 'test-plugin', label: 'Test Plugin'
|
|
15
|
+
},
|
|
16
|
+
updateStatus: jest.fn(),
|
|
17
|
+
closed: jest.fn(),
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
return shallowMount(UninstallExistingExtensionDialog, {
|
|
21
|
+
propsData: {
|
|
22
|
+
...defaultProps,
|
|
23
|
+
...propsData,
|
|
24
|
+
},
|
|
25
|
+
global: {
|
|
26
|
+
mocks: {
|
|
27
|
+
$store: store,
|
|
28
|
+
$router: { go: jest.fn() },
|
|
29
|
+
t,
|
|
30
|
+
},
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
describe('rendering', () => {
|
|
36
|
+
it('should render the dialog title', () => {
|
|
37
|
+
wrapper = mountComponent();
|
|
38
|
+
|
|
39
|
+
const title = wrapper.find('h4');
|
|
40
|
+
|
|
41
|
+
expect(title.text()).toBe('plugins.install.alreadyInstalledTitle');
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('should render the dialog prompt', () => {
|
|
45
|
+
wrapper = mountComponent();
|
|
46
|
+
|
|
47
|
+
const prompt = wrapper.find('.dialog-info p');
|
|
48
|
+
|
|
49
|
+
expect(prompt.text()).toBe('plugins.install.alreadyInstalledPrompt');
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('should render cancel button', () => {
|
|
53
|
+
wrapper = mountComponent();
|
|
54
|
+
|
|
55
|
+
const cancelBtn = wrapper.find('[data-testid="uninstall-existing-ext-modal-cancel-btn"]');
|
|
56
|
+
|
|
57
|
+
expect(cancelBtn.exists()).toBe(true);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('should render uninstall button', () => {
|
|
61
|
+
wrapper = mountComponent();
|
|
62
|
+
|
|
63
|
+
const uninstallBtn = wrapper.find('[data-testid="uninstall-existing-ext-modal-uninstall-btn"]');
|
|
64
|
+
|
|
65
|
+
expect(uninstallBtn.exists()).toBe(true);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
describe('closeDialog', () => {
|
|
70
|
+
it('should call closed callback and emit close event when cancel is clicked', async() => {
|
|
71
|
+
const closedFn = jest.fn();
|
|
72
|
+
|
|
73
|
+
wrapper = mountComponent({ closed: closedFn });
|
|
74
|
+
|
|
75
|
+
const cancelBtn = wrapper.find('[data-testid="uninstall-existing-ext-modal-cancel-btn"]');
|
|
76
|
+
|
|
77
|
+
await cancelBtn.trigger('click');
|
|
78
|
+
|
|
79
|
+
expect(closedFn).toHaveBeenCalledWith(false);
|
|
80
|
+
expect(wrapper.emitted('close')).toBeTruthy();
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
describe('uninstall', () => {
|
|
85
|
+
it('should call updateStatus with uninstall action', async() => {
|
|
86
|
+
const updateStatusFn = jest.fn();
|
|
87
|
+
const installedPlugin = {
|
|
88
|
+
id: 'test-plugin', name: 'test-plugin', label: 'Test Plugin'
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
wrapper = mountComponent({ installedPlugin, updateStatus: updateStatusFn });
|
|
92
|
+
|
|
93
|
+
await wrapper.vm.uninstall();
|
|
94
|
+
|
|
95
|
+
expect(updateStatusFn).toHaveBeenCalledWith('test-plugin', 'uninstall');
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('should remove developer plugin CR if isDeveloper is true', async() => {
|
|
99
|
+
const removeFn = jest.fn().mockResolvedValue(undefined);
|
|
100
|
+
const installedPlugin = {
|
|
101
|
+
id: 'test-plugin',
|
|
102
|
+
name: 'test-plugin',
|
|
103
|
+
label: 'Test Plugin',
|
|
104
|
+
uiplugin: { isDeveloper: true, remove: removeFn }
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
wrapper = mountComponent({ installedPlugin });
|
|
108
|
+
|
|
109
|
+
await wrapper.vm.uninstall();
|
|
110
|
+
|
|
111
|
+
expect(removeFn).toHaveBeenCalledWith();
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import CreateEditView from '@shell/mixins/create-edit-view';
|
|
3
|
+
import AsyncButton from '@shell/components/AsyncButton.vue';
|
|
3
4
|
import Footer from '@shell/components/form/Footer';
|
|
4
5
|
import { LabeledInput } from '@components/Form/LabeledInput';
|
|
5
6
|
import NameNsDescription from '@shell/components/form/NameNsDescription';
|
|
@@ -16,6 +17,7 @@ import { getVersionData } from '@shell/config/version';
|
|
|
16
17
|
import { RcItemCard } from '@components/RcItemCard';
|
|
17
18
|
import { _CREATE, _EDIT, TARGET, _VIEW } from '@shell/config/query-params';
|
|
18
19
|
import { RcIconType } from '@components/RcIcon/types';
|
|
20
|
+
import { requireAsset } from '@shell/utils/require-asset';
|
|
19
21
|
|
|
20
22
|
export default {
|
|
21
23
|
name: 'CruCatalogRepo',
|
|
@@ -23,6 +25,7 @@ export default {
|
|
|
23
25
|
emits: ['input'],
|
|
24
26
|
|
|
25
27
|
components: {
|
|
28
|
+
AsyncButton,
|
|
26
29
|
Footer,
|
|
27
30
|
LabeledInput,
|
|
28
31
|
NameNsDescription,
|
|
@@ -64,7 +67,7 @@ export default {
|
|
|
64
67
|
{
|
|
65
68
|
id: CLUSTER_REPO_TYPES.OCI_URL,
|
|
66
69
|
header: { title: { key: 'catalog.repo.target.oci.title' } },
|
|
67
|
-
image: { src:
|
|
70
|
+
image: { src: requireAsset('@shell/assets/images/providers/oci-open-containers.svg'), alt: { key: 'catalog.repo.target.oci.title' } },
|
|
68
71
|
content: { key: 'catalog.repo.target.oci.description' },
|
|
69
72
|
},
|
|
70
73
|
];
|
|
@@ -74,7 +77,7 @@ export default {
|
|
|
74
77
|
clusterRepoTargets.push({
|
|
75
78
|
id: CLUSTER_REPO_TYPES.SUSE_APP_COLLECTION,
|
|
76
79
|
header: { title: { key: 'catalog.repo.target.suseAppCollection.title' } },
|
|
77
|
-
image: { src:
|
|
80
|
+
image: { src: requireAsset('@shell/assets/images/content/suse.svg'), alt: { key: 'catalog.repo.target.suseAppCollection.title' } },
|
|
78
81
|
content: { key: 'catalog.repo.target.suseAppCollection.description' },
|
|
79
82
|
});
|
|
80
83
|
}
|
|
@@ -90,6 +93,7 @@ export default {
|
|
|
90
93
|
ociMaxRetries: this.value.spec.exponentialBackOffValues?.maxRetries,
|
|
91
94
|
getVersionData,
|
|
92
95
|
isView: this.mode === _VIEW,
|
|
96
|
+
isCreate: this.mode === _CREATE,
|
|
93
97
|
clusterRepoTargets,
|
|
94
98
|
previousName: '',
|
|
95
99
|
previousDescription: '',
|
|
@@ -450,7 +454,17 @@ export default {
|
|
|
450
454
|
:errors="errors"
|
|
451
455
|
@save="save"
|
|
452
456
|
@done="done"
|
|
453
|
-
|
|
457
|
+
>
|
|
458
|
+
<template
|
|
459
|
+
v-if="isCreate"
|
|
460
|
+
#save
|
|
461
|
+
>
|
|
462
|
+
<AsyncButton
|
|
463
|
+
:action-label="t('catalog.repo.add')"
|
|
464
|
+
@click="save"
|
|
465
|
+
/>
|
|
466
|
+
</template>
|
|
467
|
+
</Footer>
|
|
454
468
|
</form>
|
|
455
469
|
</template>
|
|
456
470
|
|
package/edit/cloudcredential.vue
CHANGED
|
@@ -3,6 +3,7 @@ import { SECRET_TYPES as TYPES } from '@shell/config/secret';
|
|
|
3
3
|
import { MANAGEMENT, NORMAN, SCHEMA, DEFAULT_WORKSPACE } from '@shell/config/types';
|
|
4
4
|
import CreateEditView from '@shell/mixins/create-edit-view';
|
|
5
5
|
import NameNsDescription from '@shell/components/form/NameNsDescription';
|
|
6
|
+
import { requireAsset } from '@shell/utils/require-asset';
|
|
6
7
|
import CruResource from '@shell/components/CruResource';
|
|
7
8
|
import { _CREATE, _EDIT } from '@shell/config/query-params';
|
|
8
9
|
import Loading from '@shell/components/Loading';
|
|
@@ -177,7 +178,7 @@ export default {
|
|
|
177
178
|
|
|
178
179
|
if (!bannerImage) {
|
|
179
180
|
try {
|
|
180
|
-
bannerImage =
|
|
181
|
+
bannerImage = requireAsset(`~shell/assets/images/providers/${ id }.svg`);
|
|
181
182
|
} catch (e) {
|
|
182
183
|
bannerImage = null;
|
|
183
184
|
bannerAbbrv = this.initialDisplayFor(id);
|
|
@@ -13,6 +13,11 @@ import ButtonDropdown from '@shell/components/ButtonDropdown';
|
|
|
13
13
|
import { _CREATE, _VIEW } from '@shell/config/query-params';
|
|
14
14
|
import FormValidation from '@shell/mixins/form-validation';
|
|
15
15
|
import { fetchAlertManagerConfigSpecs } from '@shell/utils/alertmanagerconfig';
|
|
16
|
+
import slackLogo from '@shell/assets/images/vendor/slack.svg';
|
|
17
|
+
import emailLogo from '@shell/assets/images/vendor/email.svg';
|
|
18
|
+
import pagerdutyLogo from '@shell/assets/images/vendor/pagerduty.svg';
|
|
19
|
+
import webhookLogo from '@shell/assets/images/vendor/webhook.svg';
|
|
20
|
+
import customLogo from '@shell/assets/images/vendor/custom.svg';
|
|
16
21
|
|
|
17
22
|
// i18n-uses monitoringReceiver.slack.*, monitoringReceiver.email.*, monitoringReceiver.pagerduty.*
|
|
18
23
|
// i18n-uses monitoringReceiver.opsgenie.*, monitoringReceiver.webhook.*, monitoringReceiver.custom.*
|
|
@@ -23,14 +28,14 @@ export const RECEIVERS_TYPES = [
|
|
|
23
28
|
title: 'monitoringReceiver.slack.title',
|
|
24
29
|
info: 'monitoringReceiver.slack.info',
|
|
25
30
|
key: 'slackConfigs',
|
|
26
|
-
logo:
|
|
31
|
+
logo: slackLogo
|
|
27
32
|
},
|
|
28
33
|
{
|
|
29
34
|
name: 'email',
|
|
30
35
|
label: 'monitoringReceiver.email.label',
|
|
31
36
|
title: 'monitoringReceiver.email.title',
|
|
32
37
|
key: 'emailConfigs',
|
|
33
|
-
logo:
|
|
38
|
+
logo: emailLogo
|
|
34
39
|
},
|
|
35
40
|
{
|
|
36
41
|
name: 'pagerduty',
|
|
@@ -38,21 +43,21 @@ export const RECEIVERS_TYPES = [
|
|
|
38
43
|
title: 'monitoringReceiver.pagerduty.title',
|
|
39
44
|
info: 'monitoringReceiver.pagerduty.info',
|
|
40
45
|
key: 'pagerdutyConfigs',
|
|
41
|
-
logo:
|
|
46
|
+
logo: pagerdutyLogo
|
|
42
47
|
},
|
|
43
48
|
{
|
|
44
49
|
name: 'opsgenie',
|
|
45
50
|
label: 'monitoringReceiver.opsgenie.label',
|
|
46
51
|
title: 'monitoringReceiver.opsgenie.title',
|
|
47
52
|
key: 'opsgenieConfigs',
|
|
48
|
-
logo:
|
|
53
|
+
logo: emailLogo
|
|
49
54
|
},
|
|
50
55
|
{
|
|
51
56
|
name: 'webhook',
|
|
52
57
|
label: 'monitoringReceiver.webhook.label',
|
|
53
58
|
title: 'monitoringReceiver.webhook.title',
|
|
54
59
|
key: 'webhookConfigs',
|
|
55
|
-
logo:
|
|
60
|
+
logo: webhookLogo,
|
|
56
61
|
},
|
|
57
62
|
{
|
|
58
63
|
name: 'custom',
|
|
@@ -60,7 +65,7 @@ export const RECEIVERS_TYPES = [
|
|
|
60
65
|
title: 'monitoringReceiver.custom.title',
|
|
61
66
|
info: 'monitoringReceiver.custom.info',
|
|
62
67
|
key: 'webhookConfigs',
|
|
63
|
-
logo:
|
|
68
|
+
logo: customLogo
|
|
64
69
|
},
|
|
65
70
|
];
|
|
66
71
|
|