@rancher/shell 3.0.11 → 3.0.12-rc.2
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/images/providers/entraid-black.svg +4 -0
- package/assets/images/providers/entraid.svg +9 -0
- package/assets/images/vendor/entraid.svg +9 -0
- package/assets/styles/app.scss +0 -1
- 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 +24 -21
- package/assets/translations/zh-hans.yaml +4 -11
- package/chart/__tests__/S3.test.ts +10 -3
- package/components/CountBox.vue +20 -0
- package/components/CreateDriver.vue +0 -12
- package/components/DetailText.vue +12 -3
- package/components/EmptyProductPage.vue +76 -0
- 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/SelectIconGrid.vue +5 -0
- package/components/SideNav.vue +13 -0
- package/components/__tests__/CountBox.test.ts +72 -0
- package/components/__tests__/DetailText.test.ts +113 -0
- package/components/__tests__/PromptModal.test.ts +2 -0
- package/components/fleet/FleetClusterTargets/index.vue +18 -1
- package/components/fleet/FleetClusters.vue +1 -0
- package/components/fleet/__tests__/FleetClusters.test.ts +71 -0
- package/components/form/InputWithSelect.vue +18 -10
- package/components/form/KeyValue.vue +17 -1
- package/components/form/LabeledSelect.vue +82 -24
- package/components/form/NodeScheduling.vue +17 -3
- package/components/form/PrivateRegistry.vue +69 -0
- package/components/form/Select.vue +73 -56
- package/components/form/ServiceNameSelect.vue +13 -11
- package/components/form/__tests__/KeyValue.test.ts +66 -0
- package/components/form/__tests__/NodeScheduling.test.ts +9 -0
- package/components/form/__tests__/PrivateRegistry.test.ts +133 -0
- package/components/form/labeled-select-utils/useLabeledSelectPagination.ts +138 -0
- package/components/formatter/WorkloadHealthScale.vue +3 -1
- package/components/nav/Group.vue +33 -9
- package/components/nav/Header.vue +56 -10
- package/components/nav/NotificationCenter/Notification.vue +4 -1
- package/components/nav/NotificationCenter/NotificationHeader.vue +20 -8
- package/components/nav/NotificationCenter/__tests__/NotificationHeader.test.ts +80 -0
- package/components/nav/TopLevelMenu.vue +15 -1
- package/components/nav/Type.vue +8 -7
- package/components/nav/WindowManager/index.vue +2 -1
- package/components/nav/WorkspaceSwitcher.vue +13 -0
- package/components/nav/__tests__/Group.test.ts +67 -0
- package/components/nav/__tests__/Header.test.ts +235 -0
- package/components/nav/__tests__/Type.test.ts +20 -3
- package/components/templates/default.vue +34 -4
- package/components/templates/home.vue +12 -25
- package/components/templates/plain.vue +13 -26
- package/composables/useLabeledFormElement.ts +10 -2
- package/composables/useLabeledSelect.ts +60 -0
- package/composables/useUserRetentionValidation.ts +1 -49
- package/config/cookies.js +0 -1
- package/config/labels-annotations.js +1 -0
- package/config/pagination-table-headers.js +8 -1
- package/config/product/apps.js +2 -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/config/query-params.js +1 -0
- package/config/router/routes.js +0 -8
- package/core/__tests__/plugin-products-helpers.test.ts +454 -0
- package/core/__tests__/plugin-products.test.ts +3810 -0
- package/core/extension-manager-impl.js +30 -1
- package/core/plugin-products-base.ts +392 -0
- package/core/plugin-products-extending.ts +44 -0
- package/core/plugin-products-helpers.ts +263 -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 +237 -0
- package/core/plugin.ts +45 -10
- package/core/productDebugger.js +48 -0
- package/core/types.ts +97 -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__/management.cattle.io.fleetworkspace.test.ts +128 -0
- package/detail/fleet.cattle.io.bundle.vue +21 -34
- package/detail/management.cattle.io.fleetworkspace.vue +49 -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/__tests__/fleet.cattle.io.helmop.test.ts +9 -0
- package/edit/__tests__/kontainerDriver.test.ts +0 -13
- package/edit/__tests__/nodeDriver.test.ts +5 -11
- package/edit/__tests__/resources.cattle.io.restore.test.ts +9 -0
- package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/General.test.ts.snap +6 -0
- package/edit/auth/__tests__/oidc.test.ts +54 -0
- package/edit/auth/azuread.vue +1 -1
- package/edit/auth/oidc.vue +8 -0
- package/edit/kontainerDriver.vue +1 -2
- package/edit/nodeDriver.vue +0 -2
- package/edit/provisioning.cattle.io.cluster/AgentEnv.vue +1 -0
- package/edit/provisioning.cattle.io.cluster/__tests__/AgentEnv.test.ts +25 -0
- package/edit/provisioning.cattle.io.cluster/__tests__/Ingress.test.ts +176 -0
- package/edit/provisioning.cattle.io.cluster/index.vue +70 -99
- package/edit/provisioning.cattle.io.cluster/rke2.vue +4 -1
- package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +6 -0
- package/edit/provisioning.cattle.io.cluster/tabs/Ingress.vue +7 -2
- package/initialize/App.vue +29 -2
- package/initialize/install-plugins.js +0 -2
- package/list/__tests__/management.cattle.io.feature.test.ts +105 -0
- package/list/catalog.cattle.io.app.vue +25 -5
- package/list/management.cattle.io.feature.vue +1 -1
- package/list/management.cattle.io.fleetworkspace.vue +8 -0
- package/list/provisioning.cattle.io.cluster.vue +0 -1
- package/list/workload.vue +11 -4
- package/machine-config/amazonec2.vue +1 -0
- package/mixins/chart.js +40 -9
- package/mixins/resource-fetch.js +12 -3
- package/models/__tests__/catalog.cattle.io.app.test.ts +15 -1
- package/models/__tests__/catalog.cattle.io.clusterrepo.test.ts +84 -0
- package/models/__tests__/chart.test.ts +99 -6
- package/models/__tests__/management.cattle.io.feature.test.ts +131 -0
- package/models/__tests__/monitoring.coreos.com.alertmanagerconfig.test.ts +98 -0
- package/models/catalog.cattle.io.app.js +21 -17
- package/models/catalog.cattle.io.clusterrepo.js +39 -11
- package/models/chart.js +33 -19
- package/models/fleet-application.js +1 -1
- package/models/fleet.cattle.io.bundle.js +1 -1
- package/models/kontainerdriver.js +11 -0
- package/models/management.cattle.io.authconfig.js +5 -1
- package/models/management.cattle.io.cluster.js +0 -53
- package/models/management.cattle.io.feature.js +3 -3
- package/models/management.cattle.io.kontainerdriver.js +1 -26
- package/models/monitoring.coreos.com.alertmanagerconfig.js +31 -17
- package/models/nodedriver.js +7 -0
- package/models/pod.js +18 -0
- package/models/workload.js +20 -2
- package/package.json +13 -13
- package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +0 -1
- package/pages/c/_cluster/apps/charts/__tests__/chart.test.ts +189 -0
- package/pages/c/_cluster/apps/charts/__tests__/index.test.ts +55 -0
- package/pages/c/_cluster/apps/charts/__tests__/install.test.ts +53 -0
- package/pages/c/_cluster/apps/charts/chart.vue +217 -33
- package/pages/c/_cluster/apps/charts/index.vue +2 -2
- package/pages/c/_cluster/apps/charts/install.vue +8 -3
- package/pages/c/_cluster/auth/user.retention/index.vue +55 -22
- package/pages/c/_cluster/manager/drivers/kontainerDriver/index.vue +5 -7
- package/pages/c/_cluster/settings/brand.vue +4 -4
- package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +39 -2
- package/pages/c/_cluster/uiplugins/__tests__/PluginInfoPanel.test.ts +61 -0
- package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +246 -23
- package/pages/c/_cluster/uiplugins/index.vue +166 -62
- 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 +7 -0
- package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +205 -1
- package/rancher-components/Form/LabeledInput/LabeledInput.vue +82 -4
- package/rancher-components/Form/ToggleSwitch/ToggleSwitch.vue +1 -1
- package/scripts/test-plugins-build.sh +5 -2
- package/scripts/typegen.sh +13 -1
- package/server/server-middleware.js +2 -2
- package/static/humans.txt +1 -0
- package/static/robots.txt +34 -0
- package/static/welcome-cow.svg +18 -0
- package/store/__tests__/catalog.test.ts +161 -11
- package/store/__tests__/type-map.test.ts +84 -24
- package/store/auth.js +0 -3
- package/store/catalog.js +60 -8
- 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 +8539 -2938
- package/types/store/dashboard-store.types.ts +5 -0
- package/types/store/pagination.types.ts +6 -0
- package/utils/__tests__/git.test.ts +270 -0
- package/utils/__tests__/inactivity.test.ts +316 -0
- package/utils/__tests__/object.test.ts +77 -0
- package/utils/__tests__/time.test.ts +14 -1
- package/utils/__tests__/url.test.ts +246 -0
- package/utils/axios.js +1 -4
- package/utils/dynamic-importer.js +3 -2
- package/utils/object.js +33 -2
- package/utils/pagination-utils.ts +1 -1
- package/utils/time.ts +5 -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 +0 -9
- package/assets/images/providers/azuread-black.svg +0 -22
- package/assets/images/providers/azuread.svg +0 -25
- package/assets/images/vendor/azuread.svg +0 -18
- package/assets/styles/fonts/_dots.scss +0 -18
- package/components/EmberPage.vue +0 -622
- package/components/EmberPageView.vue +0 -39
- package/components/form/labeled-select-utils/labeled-select-pagination.ts +0 -116
- package/mixins/labeled-form-element.ts +0 -225
- package/pages/c/_cluster/explorer/tools/pages/_page.vue +0 -28
- package/pages/c/_cluster/manager/pages/_page.vue +0 -22
- package/pages/c/_cluster/mcapps/pages/_page.vue +0 -22
- package/plugins/ember-cookie.js +0 -17
- package/utils/ember-page.js +0 -30
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { shallowMount } from '@vue/test-utils';
|
|
2
|
+
import DetailWorkspace from '@shell/detail/management.cattle.io.fleetworkspace.vue';
|
|
3
|
+
import { FLEET } from '@shell/config/types';
|
|
4
|
+
import { NAME as FLEET_NAME } from '@shell/config/product/fleet';
|
|
5
|
+
import { BLANK_CLUSTER } from '@shell/store/store-types.js';
|
|
6
|
+
|
|
7
|
+
describe('component: DetailWorkspace', () => {
|
|
8
|
+
const mockValue = {
|
|
9
|
+
id: 'fleet-default',
|
|
10
|
+
counts: {
|
|
11
|
+
gitRepos: 3,
|
|
12
|
+
helmOps: 2,
|
|
13
|
+
clusters: 5,
|
|
14
|
+
cluster: 5,
|
|
15
|
+
clusterGroup: 1,
|
|
16
|
+
clusterGroups: 1,
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const mockRouter = { push: jest.fn() };
|
|
21
|
+
|
|
22
|
+
const defaultStore = {
|
|
23
|
+
commit: jest.fn(),
|
|
24
|
+
dispatch: jest.fn(),
|
|
25
|
+
getters: {
|
|
26
|
+
'i18n/t': (key: string) => key,
|
|
27
|
+
'i18n/exists': () => true,
|
|
28
|
+
currentProduct: { name: FLEET_NAME },
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const createWrapper = (props = {}) => {
|
|
33
|
+
return shallowMount(DetailWorkspace, {
|
|
34
|
+
props: { value: mockValue, ...props },
|
|
35
|
+
global: {
|
|
36
|
+
mocks: {
|
|
37
|
+
$store: defaultStore,
|
|
38
|
+
$route: { params: {} },
|
|
39
|
+
$router: mockRouter,
|
|
40
|
+
},
|
|
41
|
+
stubs: {
|
|
42
|
+
CountBox: { template: '<div />', props: ['clickable', 'count', 'name', 'primaryColorVar'] },
|
|
43
|
+
ResourceTabs: { template: '<div />' },
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
beforeEach(() => {
|
|
50
|
+
jest.clearAllMocks();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
describe('applicationRoute', () => {
|
|
54
|
+
it('should return the fleet application route', () => {
|
|
55
|
+
const wrapper = createWrapper();
|
|
56
|
+
|
|
57
|
+
expect(wrapper.vm.applicationRoute).toStrictEqual({
|
|
58
|
+
name: 'c-cluster-fleet-application',
|
|
59
|
+
params: { cluster: BLANK_CLUSTER },
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
describe('clustersRoute', () => {
|
|
65
|
+
it('should return the fleet clusters list route', () => {
|
|
66
|
+
const wrapper = createWrapper();
|
|
67
|
+
|
|
68
|
+
expect(wrapper.vm.clustersRoute).toStrictEqual({
|
|
69
|
+
name: 'c-cluster-product-resource',
|
|
70
|
+
params: {
|
|
71
|
+
cluster: BLANK_CLUSTER,
|
|
72
|
+
product: FLEET_NAME,
|
|
73
|
+
resource: FLEET.CLUSTER,
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
describe('clusterGroupsRoute', () => {
|
|
80
|
+
it('should return the fleet cluster groups list route', () => {
|
|
81
|
+
const wrapper = createWrapper();
|
|
82
|
+
|
|
83
|
+
expect(wrapper.vm.clusterGroupsRoute).toStrictEqual({
|
|
84
|
+
name: 'c-cluster-product-resource',
|
|
85
|
+
params: {
|
|
86
|
+
cluster: BLANK_CLUSTER,
|
|
87
|
+
product: FLEET_NAME,
|
|
88
|
+
resource: FLEET.CLUSTER_GROUP,
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
describe('setWorkspaceAndNavigate', () => {
|
|
95
|
+
it('should commit updateWorkspace with the workspace id', () => {
|
|
96
|
+
const wrapper = createWrapper();
|
|
97
|
+
const route = wrapper.vm.applicationRoute;
|
|
98
|
+
|
|
99
|
+
wrapper.vm.setWorkspaceAndNavigate(route);
|
|
100
|
+
|
|
101
|
+
expect(defaultStore.commit).toHaveBeenCalledWith('updateWorkspace', {
|
|
102
|
+
value: 'fleet-default',
|
|
103
|
+
getters: defaultStore.getters,
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('should dispatch prefs/set with the workspace id', () => {
|
|
108
|
+
const wrapper = createWrapper();
|
|
109
|
+
const route = wrapper.vm.applicationRoute;
|
|
110
|
+
|
|
111
|
+
wrapper.vm.setWorkspaceAndNavigate(route);
|
|
112
|
+
|
|
113
|
+
expect(defaultStore.dispatch).toHaveBeenCalledWith('prefs/set', {
|
|
114
|
+
key: expect.any(String),
|
|
115
|
+
value: 'fleet-default',
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('should navigate to the given route', () => {
|
|
120
|
+
const wrapper = createWrapper();
|
|
121
|
+
const route = wrapper.vm.clustersRoute;
|
|
122
|
+
|
|
123
|
+
wrapper.vm.setWorkspaceAndNavigate(route);
|
|
124
|
+
|
|
125
|
+
expect(mockRouter.push).toHaveBeenCalledWith(route);
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
});
|
|
@@ -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>
|
|
@@ -4,6 +4,8 @@ import ResourceTabs from '@shell/components/form/ResourceTabs';
|
|
|
4
4
|
import { SCOPE_NAMESPACE, SCOPE_CLUSTER } from '@shell/components/RoleBindings.vue';
|
|
5
5
|
import { NAME as FLEET_NAME } from '@shell/config/product/fleet';
|
|
6
6
|
import { FLEET } from '@shell/config/types';
|
|
7
|
+
import { BLANK_CLUSTER } from '@shell/store/store-types.js';
|
|
8
|
+
import { WORKSPACE } from '@shell/store/prefs';
|
|
7
9
|
|
|
8
10
|
export default {
|
|
9
11
|
name: 'DetailWorkspace',
|
|
@@ -34,6 +36,35 @@ export default {
|
|
|
34
36
|
return this.t(`typeLabel."${ FLEET.HELM_OP }"`, { count: this.value.counts.helmOps });
|
|
35
37
|
},
|
|
36
38
|
|
|
39
|
+
applicationRoute() {
|
|
40
|
+
return {
|
|
41
|
+
name: 'c-cluster-fleet-application',
|
|
42
|
+
params: { cluster: BLANK_CLUSTER }
|
|
43
|
+
};
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
clustersRoute() {
|
|
47
|
+
return {
|
|
48
|
+
name: 'c-cluster-product-resource',
|
|
49
|
+
params: {
|
|
50
|
+
cluster: BLANK_CLUSTER,
|
|
51
|
+
product: FLEET_NAME,
|
|
52
|
+
resource: FLEET.CLUSTER,
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
clusterGroupsRoute() {
|
|
58
|
+
return {
|
|
59
|
+
name: 'c-cluster-product-resource',
|
|
60
|
+
params: {
|
|
61
|
+
cluster: BLANK_CLUSTER,
|
|
62
|
+
product: FLEET_NAME,
|
|
63
|
+
resource: FLEET.CLUSTER_GROUP,
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
},
|
|
67
|
+
|
|
37
68
|
SCOPE_NAMESPACE() {
|
|
38
69
|
return SCOPE_NAMESPACE;
|
|
39
70
|
},
|
|
@@ -46,6 +77,16 @@ export default {
|
|
|
46
77
|
return FLEET_NAME;
|
|
47
78
|
}
|
|
48
79
|
},
|
|
80
|
+
|
|
81
|
+
methods: {
|
|
82
|
+
setWorkspaceAndNavigate(route) {
|
|
83
|
+
const workspaceId = this.value.id;
|
|
84
|
+
|
|
85
|
+
this.$store.commit('updateWorkspace', { value: workspaceId, getters: this.$store.getters });
|
|
86
|
+
this.$store.dispatch('prefs/set', { key: WORKSPACE, value: workspaceId });
|
|
87
|
+
this.$router.push(route);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
49
90
|
};
|
|
50
91
|
</script>
|
|
51
92
|
|
|
@@ -58,6 +99,8 @@ export default {
|
|
|
58
99
|
:count="value.counts.gitRepos"
|
|
59
100
|
:name="gitRepoLabel"
|
|
60
101
|
:primary-color-var="'--sizzle-3'"
|
|
102
|
+
:clickable="true"
|
|
103
|
+
@click="setWorkspaceAndNavigate(applicationRoute)"
|
|
61
104
|
/>
|
|
62
105
|
</div>
|
|
63
106
|
<div class="col span-3">
|
|
@@ -65,6 +108,8 @@ export default {
|
|
|
65
108
|
:count="value.counts.helmOps"
|
|
66
109
|
:name="helmOpsLabel"
|
|
67
110
|
:primary-color-var="'--sizzle-3'"
|
|
111
|
+
:clickable="true"
|
|
112
|
+
@click="setWorkspaceAndNavigate(applicationRoute)"
|
|
68
113
|
/>
|
|
69
114
|
</div>
|
|
70
115
|
<div class="col span-3">
|
|
@@ -72,6 +117,8 @@ export default {
|
|
|
72
117
|
:count="value.counts.clusters"
|
|
73
118
|
:name="clustersLabel"
|
|
74
119
|
:primary-color-var="'--sizzle-1'"
|
|
120
|
+
:clickable="true"
|
|
121
|
+
@click="setWorkspaceAndNavigate(clustersRoute)"
|
|
75
122
|
/>
|
|
76
123
|
</div>
|
|
77
124
|
<div class="col span-3">
|
|
@@ -79,6 +126,8 @@ export default {
|
|
|
79
126
|
:count="value.counts.clusterGroups"
|
|
80
127
|
:name="clusterGroupsLabel"
|
|
81
128
|
:primary-color-var="'--sizzle-2'"
|
|
129
|
+
:clickable="true"
|
|
130
|
+
@click="setWorkspaceAndNavigate(clusterGroupsRoute)"
|
|
82
131
|
/>
|
|
83
132
|
</div>
|
|
84
133
|
</div>
|
|
@@ -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
|
+
});
|