@rancher/shell 3.0.5-rc.8 → 3.0.5-rc.9
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/_color.scss +4 -1
- package/assets/styles/global/_tooltip.scss +7 -4
- package/assets/styles/themes/_dark.scss +11 -0
- package/assets/styles/themes/_light.scss +13 -1
- package/assets/styles/themes/_modern.scss +22 -0
- package/assets/translations/en-us.yaml +136 -14
- package/assets/translations/zh-hans.yaml +0 -1
- package/chart/monitoring/grafana/index.vue +8 -2
- package/components/ActionMenuShell.vue +3 -1
- package/components/Cron/CronExpressionEditor.vue +299 -0
- package/components/Cron/CronExpressionEditorModal.vue +247 -0
- package/components/Cron/CronTooltip.vue +87 -0
- package/components/Cron/types.ts +13 -0
- package/components/ForceDirectedTreeChart/composable.ts +11 -0
- package/components/PromptModal.vue +1 -1
- package/components/Resource/Detail/Card/__tests__/StateCard.test.ts +1 -0
- package/components/Resource/Detail/CopyToClipboard.vue +78 -0
- package/components/Resource/Detail/FetchLoader/__tests__/composables.test.ts +69 -0
- package/components/Resource/Detail/FetchLoader/composables.ts +27 -0
- package/components/Resource/Detail/Metadata/Annotations/__tests__/index.test.ts +1 -1
- package/components/Resource/Detail/Metadata/Annotations/index.vue +1 -1
- package/components/Resource/Detail/Metadata/IdentifyingInformation/__tests__/identifying-fields.test.ts +13 -61
- package/components/Resource/Detail/Metadata/IdentifyingInformation/__tests__/index.test.ts +33 -6
- package/components/Resource/Detail/Metadata/IdentifyingInformation/identifying-fields.ts +24 -38
- package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +25 -5
- package/components/Resource/Detail/Metadata/KeyValue.vue +12 -23
- package/components/Resource/Detail/Metadata/KeyValueRow.vue +144 -0
- package/components/Resource/Detail/Metadata/Labels/__tests__/index.test.ts +1 -0
- package/components/Resource/Detail/Metadata/Labels/index.vue +1 -0
- package/components/Resource/Detail/Metadata/__tests__/KeyValue.test.ts +30 -32
- package/components/Resource/Detail/Metadata/__tests__/KeyValueRow.test.ts +108 -0
- package/components/Resource/Detail/Metadata/__tests__/composables.test.ts +0 -3
- package/components/Resource/Detail/Metadata/__tests__/index.test.ts +12 -5
- package/components/Resource/Detail/Metadata/composables.ts +1 -4
- package/components/Resource/Detail/Metadata/index.vue +1 -0
- package/components/Resource/Detail/Preview/Content.vue +63 -0
- package/components/Resource/Detail/Preview/Preview.vue +128 -0
- package/components/Resource/Detail/Preview/__tests__/Content.spec.ts +71 -0
- package/components/Resource/Detail/Preview/__tests__/Preview.spec.ts +121 -0
- package/components/Resource/Detail/ResourcePopover/ResourcePopoverCard.vue +141 -0
- package/components/Resource/Detail/ResourcePopover/__tests__/ResourcePopoverCard.test.ts +136 -0
- package/components/Resource/Detail/ResourcePopover/__tests__/index.test.ts +245 -0
- package/components/Resource/Detail/ResourcePopover/index.vue +226 -0
- package/components/Resource/Detail/SpacedRow.vue +1 -0
- package/components/Resource/Detail/TitleBar/__tests__/composables.test.ts +0 -5
- package/components/Resource/Detail/TitleBar/__tests__/index.test.ts +1 -1
- package/components/Resource/Detail/TitleBar/composables.ts +1 -3
- package/components/Resource/Detail/TitleBar/index.vue +2 -29
- package/components/Resource/Detail/ViewOptions/composable.ts +9 -0
- package/components/Resource/Detail/ViewOptions/index.vue +41 -0
- package/components/Resource/Detail/__tests__/CopyToClipboard.spec.ts +82 -0
- package/components/ResourceDetail/Masthead/legacy.vue +0 -19
- package/components/ResourceDetail/index.vue +1 -26
- package/components/ResourceTable.vue +24 -0
- package/components/SortableTable/index.vue +7 -1
- package/components/SortableTable/paging.js +3 -0
- package/components/Tabbed/Tab.vue +43 -1
- package/components/Tabbed/index.vue +3 -1
- package/components/__tests__/Cron/CronExpressionEditor.test.ts +151 -0
- package/components/__tests__/Cron/CronExpressionEditorModal.test.ts +81 -0
- package/components/auth/login/saml.vue +86 -0
- package/components/form/LabeledSelect.vue +8 -8
- package/components/form/ResourceTabs/composable.ts +54 -0
- package/components/form/ResourceTabs/index.vue +10 -7
- package/components/form/Select.vue +13 -10
- package/components/form/__tests__/LabeledSelect.test.ts +133 -0
- package/components/form/__tests__/Select.test.ts +134 -0
- package/composables/useExtensionManager.ts +17 -0
- package/config/home-links.js +12 -0
- package/config/labels-annotations.js +0 -1
- package/config/page-actions.js +0 -1
- package/config/product/explorer.js +3 -1
- package/config/product/fleet.js +2 -7
- package/config/product/manager.js +0 -5
- package/config/query-params.js +1 -0
- package/config/router/navigation-guards/clusters.js +2 -1
- package/config/router/navigation-guards/products.js +1 -1
- package/core/extension-manager-impl.js +518 -0
- package/core/plugins.js +35 -468
- package/core/types.ts +8 -2
- package/detail/__tests__/autoscaling.horizontalpodautoscaler.test.ts +1 -0
- package/detail/catalog.cattle.io.app.vue +7 -4
- package/detail/fleet.cattle.io.bundle.vue +1 -5
- package/detail/fleet.cattle.io.cluster.vue +3 -2
- package/detail/fleet.cattle.io.gitrepo.vue +76 -49
- package/detail/fleet.cattle.io.helmop.vue +78 -49
- package/dialog/AddonConfigConfirmationDialog.vue +1 -1
- package/dialog/GenericPrompt.vue +1 -1
- package/dialog/ImportDialog.vue +9 -2
- package/dialog/InstallExtensionDialog.vue +18 -10
- package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +2 -1
- package/edit/__tests__/resources.cattle.io.restore.test.ts +106 -0
- package/edit/cloudcredential.vue +31 -17
- package/edit/constraints.gatekeeper.sh.constraint/index.vue +10 -2
- package/edit/fleet.cattle.io.cluster.vue +19 -0
- package/edit/fleet.cattle.io.gitrepo.vue +23 -16
- package/edit/monitoring.coreos.com.alertmanagerconfig/index.vue +12 -11
- package/edit/monitoring.coreos.com.alertmanagerconfig/receiverConfig.vue +11 -1
- package/edit/provisioning.cattle.io.cluster/index.vue +14 -19
- package/edit/provisioning.cattle.io.cluster/rke2.vue +11 -3
- package/edit/resources.cattle.io.restore.vue +5 -8
- package/list/__tests__/workload.test.ts +1 -0
- package/list/workload.vue +8 -1
- package/machine-config/components/GCEImage.vue +6 -5
- package/machine-config/google.vue +11 -6
- package/mixins/__tests__/chart.test.ts +139 -1
- package/mixins/chart.js +58 -18
- package/models/__tests__/namespace.test.ts +69 -0
- package/models/apps.statefulset.js +8 -10
- package/models/chart.js +5 -1
- package/models/fleet-application.js +16 -46
- package/models/fleet.cattle.io.bundle.js +1 -38
- package/models/fleet.cattle.io.gitrepo.js +4 -0
- package/models/fleet.cattle.io.helmop.js +4 -0
- package/models/management.cattle.io.project.js +12 -0
- package/models/namespace.js +30 -0
- package/models/workload.js +3 -0
- package/package.json +10 -10
- package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +26 -11
- package/pages/c/_cluster/apps/charts/chart.vue +29 -20
- package/pages/c/_cluster/apps/charts/index.vue +1 -0
- package/pages/c/_cluster/apps/charts/install.vue +6 -5
- package/pages/c/_cluster/explorer/tools/__tests__/index.test.ts +102 -12
- package/pages/c/_cluster/explorer/tools/index.vue +145 -254
- package/pages/c/_cluster/manager/cloudCredential/index.vue +18 -1
- package/pages/c/_cluster/manager/drivers/kontainerDriver/index.vue +12 -2
- package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +1 -1
- package/pages/c/_cluster/uiplugins/__tests__/index.spec.ts +318 -0
- package/pages/c/_cluster/uiplugins/index.vue +221 -363
- package/pages/home.vue +1 -9
- package/plugins/dashboard-store/resource-class.js +49 -0
- package/public/index.html +2 -1
- package/rancher-components/Card/Card.vue +1 -1
- package/rancher-components/Form/Checkbox/Checkbox.vue +1 -1
- package/rancher-components/Form/Radio/RadioButton.vue +1 -1
- package/rancher-components/Form/Radio/RadioGroup.vue +1 -1
- package/rancher-components/LabeledTooltip/LabeledTooltip.vue +1 -11
- package/rancher-components/Pill/RcCounterBadge/RcCounterBadge.test.ts +53 -0
- package/rancher-components/Pill/RcCounterBadge/RcCounterBadge.vue +65 -0
- package/rancher-components/Pill/RcCounterBadge/index.ts +1 -0
- package/rancher-components/Pill/RcCounterBadge/types.ts +7 -0
- package/rancher-components/Pill/RcStatusBadge/RcStatusBadge.vue +1 -1
- package/rancher-components/Pill/RcStatusBadge/index.ts +1 -1
- package/rancher-components/Pill/RcStatusIndicator/RcStatusIndicator.vue +3 -3
- package/rancher-components/Pill/RcStatusIndicator/types.ts +1 -1
- package/rancher-components/Pill/RcTag/RcTag.test.ts +64 -0
- package/rancher-components/Pill/RcTag/RcTag.vue +94 -0
- package/rancher-components/Pill/RcTag/index.ts +1 -0
- package/rancher-components/Pill/RcTag/types.ts +9 -0
- package/rancher-components/Pill/types.ts +1 -0
- package/rancher-components/RcItemCard/RcItemCard.vue +1 -0
- package/rancher-components/RcItemCard/RcItemCardAction.vue +12 -0
- package/store/__tests__/catalog.test.ts +63 -0
- package/store/catalog.js +2 -2
- package/store/type-map.js +3 -15
- package/types/extension-manager.ts +26 -0
- package/types/shell/index.d.ts +121 -16
- package/utils/__tests__/product.test.ts +129 -0
- package/utils/__tests__/resource.test.ts +87 -0
- package/utils/alertmanagerconfig.js +2 -2
- package/utils/auth.js +3 -76
- package/utils/product.ts +39 -0
- package/utils/resource.ts +35 -0
- package/utils/select.js +0 -24
- package/utils/validators/formRules/__tests__/index.test.ts +3 -0
- package/utils/validators/formRules/index.ts +2 -1
- package/vue.config.js +1 -1
- package/components/Resource/Detail/Metadata/Rectangle.vue +0 -34
- package/components/Resource/Detail/Metadata/__tests__/Rectangle.test.ts +0 -24
- package/components/ResourceDetail/Masthead/__tests__/legacy.test.ts +0 -65
- /package/components/{ForceDirectedTreeChart.vue → ForceDirectedTreeChart/index.vue} +0 -0
|
@@ -29,7 +29,10 @@ describe('chartMixin', () => {
|
|
|
29
29
|
return () => 'repo';
|
|
30
30
|
},
|
|
31
31
|
'catalog/chart': () => {
|
|
32
|
-
return {
|
|
32
|
+
return {
|
|
33
|
+
id: chartId,
|
|
34
|
+
matchingInstalledApps: []
|
|
35
|
+
};
|
|
33
36
|
},
|
|
34
37
|
'i18n/t': () => jest.fn()
|
|
35
38
|
}
|
|
@@ -101,4 +104,139 @@ describe('chartMixin', () => {
|
|
|
101
104
|
expect(warnings).toHaveLength(expected as number);
|
|
102
105
|
}
|
|
103
106
|
);
|
|
107
|
+
|
|
108
|
+
describe('fetchChart', () => {
|
|
109
|
+
it('should call catalog/version with showDeprecated', async() => {
|
|
110
|
+
const mockStore = {
|
|
111
|
+
dispatch: jest.fn(() => Promise.resolve()),
|
|
112
|
+
getters: {
|
|
113
|
+
currentCluster: () => {},
|
|
114
|
+
isRancher: () => true,
|
|
115
|
+
'catalog/repo': () => {
|
|
116
|
+
return () => 'repo';
|
|
117
|
+
},
|
|
118
|
+
'catalog/chart': () => {
|
|
119
|
+
return {
|
|
120
|
+
id: 'chart-id', versions: [{ version: '1.0.0' }], matchingInstalledApps: []
|
|
121
|
+
};
|
|
122
|
+
},
|
|
123
|
+
'catalog/version': jest.fn(),
|
|
124
|
+
'prefs/get': () => {},
|
|
125
|
+
'i18n/t': () => jest.fn()
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const DummyComponent = {
|
|
130
|
+
mixins: [ChartMixin],
|
|
131
|
+
template: '<div></div>',
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const wrapper = mount(
|
|
135
|
+
DummyComponent,
|
|
136
|
+
{
|
|
137
|
+
global: {
|
|
138
|
+
mocks: {
|
|
139
|
+
$store: mockStore,
|
|
140
|
+
$route: {
|
|
141
|
+
query: {
|
|
142
|
+
chart: 'chart_name',
|
|
143
|
+
deprecated: 'true'
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
await wrapper.vm.fetchChart();
|
|
151
|
+
|
|
152
|
+
expect(mockStore.getters['catalog/version']).toHaveBeenCalledWith(expect.objectContaining({ showDeprecated: true }));
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
describe('action', () => {
|
|
157
|
+
const DummyComponent = {
|
|
158
|
+
mixins: [ChartMixin],
|
|
159
|
+
template: '<div></div>',
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
const mockStore = {
|
|
163
|
+
dispatch: jest.fn(() => Promise.resolve()),
|
|
164
|
+
getters: { 'i18n/t': (key: string) => key }
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
it('should return "install" action when not installed', () => {
|
|
168
|
+
const wrapper = mount(DummyComponent, {
|
|
169
|
+
data: () => ({ existing: null }),
|
|
170
|
+
global: { mocks: { $store: mockStore } }
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
expect(wrapper.vm.action).toStrictEqual({
|
|
174
|
+
name: 'install',
|
|
175
|
+
tKey: 'install',
|
|
176
|
+
icon: 'icon-plus',
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it('should return "editVersion" action when installed and on same version', () => {
|
|
181
|
+
const wrapper = mount(DummyComponent, {
|
|
182
|
+
data: () => ({
|
|
183
|
+
existing: { spec: { chart: { metadata: { version: '1.0.0' } } } },
|
|
184
|
+
version: { version: '1.0.0' }
|
|
185
|
+
}),
|
|
186
|
+
global: {
|
|
187
|
+
mocks: {
|
|
188
|
+
$store: mockStore,
|
|
189
|
+
$route: { query: {} }
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
expect(wrapper.vm.action).toStrictEqual({
|
|
195
|
+
name: 'editVersion',
|
|
196
|
+
tKey: 'edit',
|
|
197
|
+
icon: 'icon-edit',
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it('should return "upgrade" action when installed and on a newer version', () => {
|
|
202
|
+
const wrapper = mount(DummyComponent, {
|
|
203
|
+
data: () => ({
|
|
204
|
+
existing: { spec: { chart: { metadata: { version: '1.0.0' } } } },
|
|
205
|
+
version: { version: '1.0.1' }
|
|
206
|
+
}),
|
|
207
|
+
global: {
|
|
208
|
+
mocks: {
|
|
209
|
+
$store: mockStore,
|
|
210
|
+
$route: { query: {} }
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
expect(wrapper.vm.action).toStrictEqual({
|
|
216
|
+
name: 'upgradeVersion',
|
|
217
|
+
tKey: 'upgrade',
|
|
218
|
+
icon: 'icon-upgrade-alt',
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
it('should return "downgrade" action when installed and on an older version', () => {
|
|
222
|
+
const wrapper = mount(DummyComponent, {
|
|
223
|
+
data: () => ({
|
|
224
|
+
existing: { spec: { chart: { metadata: { version: '1.0.1' } } } },
|
|
225
|
+
version: { version: '1.0.0' }
|
|
226
|
+
}),
|
|
227
|
+
global: {
|
|
228
|
+
mocks: {
|
|
229
|
+
$store: mockStore,
|
|
230
|
+
$route: { query: {} }
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
expect(wrapper.vm.action).toStrictEqual({
|
|
236
|
+
name: 'downgrade',
|
|
237
|
+
tKey: 'downgrade',
|
|
238
|
+
icon: 'icon-history',
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
});
|
|
104
242
|
});
|
package/mixins/chart.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
|
|
2
2
|
import { mapGetters } from 'vuex';
|
|
3
|
-
|
|
4
3
|
import {
|
|
5
4
|
REPO_TYPE, REPO, CHART, VERSION, NAMESPACE, NAME, DESCRIPTION as DESCRIPTION_QUERY, DEPRECATED as DEPRECATED_QUERY, HIDDEN, _FLAGGED, _CREATE, _EDIT
|
|
6
5
|
} from '@shell/config/query-params';
|
|
@@ -9,10 +8,9 @@ import { SHOW_PRE_RELEASE, mapPref } from '@shell/store/prefs';
|
|
|
9
8
|
import { NAME as EXPLORER } from '@shell/config/product/explorer';
|
|
10
9
|
import { NAME as MANAGER } from '@shell/config/product/manager';
|
|
11
10
|
import { OPA_GATE_KEEPER_ID } from '@shell/pages/c/_cluster/gatekeeper/index.vue';
|
|
12
|
-
|
|
13
11
|
import { formatSi, parseSi } from '@shell/utils/units';
|
|
14
12
|
import { CAPI, CATALOG } from '@shell/config/types';
|
|
15
|
-
import { isPrerelease } from '@shell/utils/version';
|
|
13
|
+
import { isPrerelease, compare } from '@shell/utils/version';
|
|
16
14
|
import difference from 'lodash/difference';
|
|
17
15
|
import { LINUX, APP_UPGRADE_STATUS } from '@shell/store/catalog';
|
|
18
16
|
import { clone } from '@shell/utils/object';
|
|
@@ -230,11 +228,27 @@ export default {
|
|
|
230
228
|
},
|
|
231
229
|
|
|
232
230
|
action() {
|
|
233
|
-
if (this.existing) {
|
|
234
|
-
return
|
|
231
|
+
if (!this.existing) {
|
|
232
|
+
return {
|
|
233
|
+
name: 'install', tKey: 'install', icon: 'icon-plus'
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (this.currentVersion === this.targetVersion) {
|
|
238
|
+
return {
|
|
239
|
+
name: 'editVersion', tKey: 'edit', icon: 'icon-edit'
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (compare(this.currentVersion, this.targetVersion) < 0) {
|
|
244
|
+
return {
|
|
245
|
+
name: 'upgradeVersion', tKey: 'upgrade', icon: 'icon-upgrade-alt'
|
|
246
|
+
};
|
|
235
247
|
}
|
|
236
248
|
|
|
237
|
-
return
|
|
249
|
+
return {
|
|
250
|
+
name: 'downgrade', tKey: 'downgrade', icon: 'icon-history'
|
|
251
|
+
};
|
|
238
252
|
},
|
|
239
253
|
|
|
240
254
|
isChartTargeted() {
|
|
@@ -276,7 +290,10 @@ export default {
|
|
|
276
290
|
async fetchChart() {
|
|
277
291
|
this.versionInfoError = null;
|
|
278
292
|
|
|
279
|
-
await
|
|
293
|
+
await Promise.all([
|
|
294
|
+
this.$store.dispatch('catalog/load'),
|
|
295
|
+
this.$store.dispatch('cluster/findAll', { type: CATALOG.APP })
|
|
296
|
+
]);
|
|
280
297
|
|
|
281
298
|
this.fetchStoreChart();
|
|
282
299
|
|
|
@@ -316,6 +333,15 @@ export default {
|
|
|
316
333
|
this.mode = _CREATE;
|
|
317
334
|
this.existing = null;
|
|
318
335
|
}
|
|
336
|
+
} else if (this.chart) {
|
|
337
|
+
const matching = this.chart.matchingInstalledApps;
|
|
338
|
+
|
|
339
|
+
if (matching.length === 1) {
|
|
340
|
+
this.existing = matching[0];
|
|
341
|
+
this.mode = _EDIT;
|
|
342
|
+
} else {
|
|
343
|
+
this.mode = _CREATE;
|
|
344
|
+
}
|
|
319
345
|
} else {
|
|
320
346
|
// Regular create
|
|
321
347
|
|
|
@@ -345,10 +371,11 @@ export default {
|
|
|
345
371
|
|
|
346
372
|
try {
|
|
347
373
|
this.version = this.$store.getters['catalog/version']({
|
|
348
|
-
repoType:
|
|
349
|
-
repoName:
|
|
350
|
-
chartName:
|
|
351
|
-
versionName:
|
|
374
|
+
repoType: this.query.repoType,
|
|
375
|
+
repoName: this.query.repoName,
|
|
376
|
+
chartName: this.query.chartName,
|
|
377
|
+
versionName: this.query.versionName,
|
|
378
|
+
showDeprecated: this.showDeprecated
|
|
352
379
|
});
|
|
353
380
|
} catch (e) {
|
|
354
381
|
console.error('Unable to fetch Version: ', e); // eslint-disable-line no-console
|
|
@@ -503,18 +530,24 @@ export default {
|
|
|
503
530
|
version: this.query.versionName,
|
|
504
531
|
};
|
|
505
532
|
|
|
533
|
+
const query = {
|
|
534
|
+
[REPO_TYPE]: provider.repoType,
|
|
535
|
+
[REPO]: provider.repoName,
|
|
536
|
+
[CHART]: provider.name,
|
|
537
|
+
[VERSION]: provider.version,
|
|
538
|
+
};
|
|
539
|
+
|
|
540
|
+
if (this.showDeprecated) {
|
|
541
|
+
query[DEPRECATED_QUERY] = this.query.deprecated;
|
|
542
|
+
}
|
|
543
|
+
|
|
506
544
|
return {
|
|
507
545
|
name: install ? 'c-cluster-apps-charts-install' : 'c-cluster-apps-charts-chart',
|
|
508
546
|
params: {
|
|
509
547
|
cluster: this.$route.params.cluster,
|
|
510
548
|
product: this.$store.getters['productId'],
|
|
511
549
|
},
|
|
512
|
-
query
|
|
513
|
-
[REPO_TYPE]: provider.repoType,
|
|
514
|
-
[REPO]: provider.repoName,
|
|
515
|
-
[CHART]: provider.name,
|
|
516
|
-
[VERSION]: provider.version,
|
|
517
|
-
}
|
|
550
|
+
query
|
|
518
551
|
};
|
|
519
552
|
},
|
|
520
553
|
|
|
@@ -530,13 +563,20 @@ export default {
|
|
|
530
563
|
},
|
|
531
564
|
|
|
532
565
|
clusterToolsLocation() {
|
|
566
|
+
const query = {};
|
|
567
|
+
|
|
568
|
+
if (this.showDeprecated) {
|
|
569
|
+
query[DEPRECATED_QUERY] = this.query.deprecated;
|
|
570
|
+
}
|
|
571
|
+
|
|
533
572
|
return {
|
|
534
573
|
name: `c-cluster-explorer-tools`,
|
|
535
574
|
params: {
|
|
536
575
|
product: EXPLORER,
|
|
537
576
|
cluster: this.$store.getters['clusterId'],
|
|
538
577
|
resource: CATALOG.APP,
|
|
539
|
-
}
|
|
578
|
+
},
|
|
579
|
+
query
|
|
540
580
|
};
|
|
541
581
|
},
|
|
542
582
|
|
|
@@ -186,4 +186,73 @@ describe('class Namespace', () => {
|
|
|
186
186
|
it.todo('should return the resourceQuota');
|
|
187
187
|
it.todo('should set the resourceQuota as reactive Vue property');
|
|
188
188
|
it.todo('should reset project with cleanForNew');
|
|
189
|
+
|
|
190
|
+
describe('glance', () => {
|
|
191
|
+
it('should return projectGlance instead of namespace when namespace is in a project', () => {
|
|
192
|
+
const t = jest.fn((key) => key);
|
|
193
|
+
const ctx = { rootGetters: { 'i18n/t': t } };
|
|
194
|
+
const namespace = new Namespace({}, ctx);
|
|
195
|
+
|
|
196
|
+
const project = {
|
|
197
|
+
detailLocation: 'project-detail',
|
|
198
|
+
nameDisplay: 'My Project',
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
jest.spyOn(namespace, 'project', 'get').mockReturnValue(project);
|
|
202
|
+
Object.defineProperty(namespace, '_glance', { get: jest.fn(() => [{ name: 'namespace' }, { name: 'other' }]) });
|
|
203
|
+
|
|
204
|
+
const result = namespace.glance;
|
|
205
|
+
|
|
206
|
+
expect(result).toHaveLength(2);
|
|
207
|
+
expect(result[0].name).toBe('project');
|
|
208
|
+
expect(result[0].label).toBe('component.resource.detail.glance.project');
|
|
209
|
+
expect(result[0].formatter).toBe('Link');
|
|
210
|
+
expect(result[0].formatterOpts?.to).toBe('project-detail');
|
|
211
|
+
expect(result[0].content).toBe('My Project');
|
|
212
|
+
expect(result[1].name).toBe('other');
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it('should remove namespace from glance when namespace is not in a project', () => {
|
|
216
|
+
const namespace = new Namespace({});
|
|
217
|
+
|
|
218
|
+
jest.spyOn(namespace, 'project', 'get').mockReturnValue(null);
|
|
219
|
+
Object.defineProperty(namespace, '_glance', { get: jest.fn(() => [{ name: 'namespace' }, { name: 'other' }]) });
|
|
220
|
+
|
|
221
|
+
const result = namespace.glance;
|
|
222
|
+
|
|
223
|
+
expect(result).toHaveLength(1);
|
|
224
|
+
expect(result[0].name).toBe('other');
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
describe('projectGlance', () => {
|
|
229
|
+
it('should return undefined if namespace is not in a project', () => {
|
|
230
|
+
const namespace = new Namespace({});
|
|
231
|
+
|
|
232
|
+
jest.spyOn(namespace, 'project', 'get').mockReturnValue(null);
|
|
233
|
+
|
|
234
|
+
expect(namespace.projectGlance).toBeUndefined();
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it('should return project glance information if namespace is in a project', () => {
|
|
238
|
+
const t = jest.fn((key) => key);
|
|
239
|
+
const ctx = { rootGetters: { 'i18n/t': t } };
|
|
240
|
+
const namespace = new Namespace({}, ctx);
|
|
241
|
+
|
|
242
|
+
const project = {
|
|
243
|
+
detailLocation: 'project-detail',
|
|
244
|
+
nameDisplay: 'My Project',
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
jest.spyOn(namespace, 'project', 'get').mockReturnValue(project);
|
|
248
|
+
|
|
249
|
+
const result = namespace.projectGlance;
|
|
250
|
+
|
|
251
|
+
expect(result?.name).toBe('project');
|
|
252
|
+
expect(result?.label).toBe('component.resource.detail.glance.project');
|
|
253
|
+
expect(result?.formatter).toBe('Link');
|
|
254
|
+
expect(result?.formatterOpts.to).toBe('project-detail');
|
|
255
|
+
expect(result?.content).toBe('My Project');
|
|
256
|
+
});
|
|
257
|
+
});
|
|
189
258
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import Workload from './workload';
|
|
2
|
-
import { WORKLOAD_TYPES,
|
|
2
|
+
import { WORKLOAD_TYPES, WORKLOAD_TYPE_TO_KIND_MAPPING } from '@shell/config/types';
|
|
3
3
|
|
|
4
4
|
export default class StatefulSet extends Workload {
|
|
5
5
|
async rollBack(cluster, statefulSet, revision) {
|
|
@@ -21,16 +21,14 @@ export default class StatefulSet extends Workload {
|
|
|
21
21
|
await this.rollBackWorkload(cluster, statefulSet, 'statefulsets', body);
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
/**
|
|
25
|
+
* See fetchPods description for more info
|
|
26
|
+
*/
|
|
26
27
|
get pods() {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const pods = this.$getters['podsByNamespace'](this.metadata.namespace);
|
|
32
|
-
|
|
33
|
-
return pods.filter((pod) => {
|
|
28
|
+
if (this.podMatchExpression) {
|
|
29
|
+
// Given https://github.com/rancher/dashboard/issues/7555 we want to avoid scenarios where we show pods that have an applicable label but aren't applicable (?!)
|
|
30
|
+
// super.pods is the pods that match the statefulsets podSelector, so start from that and then filter further by pod's owner
|
|
31
|
+
return super.pods.filter((pod) => {
|
|
34
32
|
// a bit of a duplication of podRelationship, but always safe to check...
|
|
35
33
|
if (pod.metadata?.ownerReferences?.length) {
|
|
36
34
|
const ownerReferencesStatefulSet = pod.metadata?.ownerReferences?.find((own) => own.kind === WORKLOAD_TYPE_TO_KIND_MAPPING[WORKLOAD_TYPES.STATEFUL_SET]);
|
package/models/chart.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { compatibleVersionsFor, APP_UPGRADE_STATUS } from '@shell/store/catalog';
|
|
2
2
|
import {
|
|
3
|
-
REPO_TYPE, REPO, CHART, VERSION, _FLAGGED, HIDE_SIDE_NAV, CATEGORY, TAG
|
|
3
|
+
REPO_TYPE, REPO, CHART, VERSION, _FLAGGED, HIDE_SIDE_NAV, CATEGORY, TAG, DEPRECATED as DEPRECATED_QUERY
|
|
4
4
|
} from '@shell/config/query-params';
|
|
5
5
|
import { BLANK_CLUSTER } from '@shell/store/store-types.js';
|
|
6
6
|
import SteveModel from '@shell/plugins/steve/steve-class';
|
|
@@ -29,6 +29,10 @@ export default class Chart extends SteveModel {
|
|
|
29
29
|
[VERSION]: version,
|
|
30
30
|
};
|
|
31
31
|
|
|
32
|
+
if (this.deprecated) {
|
|
33
|
+
out[DEPRECATED_QUERY] = true;
|
|
34
|
+
}
|
|
35
|
+
|
|
32
36
|
if ( from ) {
|
|
33
37
|
out[from] = _FLAGGED;
|
|
34
38
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { matching, convertSelectorObj } from '@shell/utils/selector';
|
|
2
2
|
import isEmpty from 'lodash/isEmpty';
|
|
3
3
|
import { escapeHtml } from '@shell/utils/string';
|
|
4
|
-
import { FLEET
|
|
4
|
+
import { FLEET } from '@shell/config/types';
|
|
5
5
|
import { FLEET as FLEET_ANNOTATIONS } from '@shell/config/labels-annotations';
|
|
6
6
|
import { addObject, addObjects, findBy } from '@shell/utils/array';
|
|
7
7
|
import SteveModel from '@shell/plugins/steve/steve-class';
|
|
@@ -29,8 +29,19 @@ function normalizeStateCounts(data) {
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
export default class FleetApplication extends SteveModel {
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
async getCurrentUser() {
|
|
33
|
+
const user = this.$rootGetters['auth/v3User'];
|
|
34
|
+
|
|
35
|
+
if (user?.id) {
|
|
36
|
+
return user;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const res = await this.$dispatch('rancher/request', {
|
|
40
|
+
url: '/v3/users?me=true',
|
|
41
|
+
method: 'get',
|
|
42
|
+
}, { root: true });
|
|
43
|
+
|
|
44
|
+
return res?.data?.[0] || {};
|
|
34
45
|
}
|
|
35
46
|
|
|
36
47
|
pause() {
|
|
@@ -48,10 +59,6 @@ export default class FleetApplication extends SteveModel {
|
|
|
48
59
|
delete this.metadata.labels[FLEET_ANNOTATIONS.CREATED_BY_USER_ID];
|
|
49
60
|
}
|
|
50
61
|
|
|
51
|
-
if (this.metadata?.labels?.[FLEET_ANNOTATIONS.CREATED_BY_USER_NAME]) {
|
|
52
|
-
delete this.metadata.labels[FLEET_ANNOTATIONS.CREATED_BY_USER_NAME];
|
|
53
|
-
}
|
|
54
|
-
|
|
55
62
|
super.goToClone();
|
|
56
63
|
}
|
|
57
64
|
|
|
@@ -144,11 +151,11 @@ export default class FleetApplication extends SteveModel {
|
|
|
144
151
|
}
|
|
145
152
|
|
|
146
153
|
statusResourceCountsForCluster(clusterId) {
|
|
147
|
-
if (!this.targetClusters.some((c) => c.id === clusterId)) {
|
|
154
|
+
if (!(this.targetClusters || []).some((c) => c.id === clusterId)) {
|
|
148
155
|
return {};
|
|
149
156
|
}
|
|
150
157
|
|
|
151
|
-
return this.status?.perClusterResourceCounts[clusterId] || { desiredReady: 0 };
|
|
158
|
+
return this.status?.perClusterResourceCounts?.[clusterId] || { desiredReady: 0 };
|
|
152
159
|
}
|
|
153
160
|
|
|
154
161
|
get resourcesStatuses() {
|
|
@@ -224,43 +231,6 @@ export default class FleetApplication extends SteveModel {
|
|
|
224
231
|
return primaryDisplayStatusFromCount(resourceCounts) || STATES_ENUM.ACTIVE;
|
|
225
232
|
}
|
|
226
233
|
|
|
227
|
-
get authorId() {
|
|
228
|
-
return this.metadata?.labels?.[FLEET_ANNOTATIONS.CREATED_BY_USER_ID];
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
get author() {
|
|
232
|
-
if (this.authorId) {
|
|
233
|
-
return this.$rootGetters['management/byId'](MANAGEMENT.USER, this.authorId);
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
return null;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
get createdBy() {
|
|
240
|
-
const displayName = this.metadata?.labels?.[FLEET_ANNOTATIONS.CREATED_BY_USER_NAME];
|
|
241
|
-
|
|
242
|
-
if (!displayName) {
|
|
243
|
-
return null;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
return {
|
|
247
|
-
displayName,
|
|
248
|
-
location: !this.author ? null : {
|
|
249
|
-
name: 'c-cluster-product-resource-id',
|
|
250
|
-
params: {
|
|
251
|
-
cluster: '_',
|
|
252
|
-
product: 'auth',
|
|
253
|
-
resource: MANAGEMENT.USER,
|
|
254
|
-
id: this.author.id,
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
};
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
get showCreatedBy() {
|
|
261
|
-
return !!this.createdBy;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
234
|
get clustersList() {
|
|
265
235
|
return this.$getters['all'](FLEET.CLUSTER);
|
|
266
236
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { escapeHtml, ucFirst } from '@shell/utils/string';
|
|
2
2
|
import SteveModel from '@shell/plugins/steve/steve-class';
|
|
3
3
|
import { addObject, addObjects, findBy } from '@shell/utils/array';
|
|
4
|
-
import { FLEET
|
|
4
|
+
import { FLEET } from '@shell/config/types';
|
|
5
5
|
import { FLEET as FLEET_ANNOTATIONS } from '@shell/config/labels-annotations';
|
|
6
6
|
import { convertSelectorObj, matching } from '@shell/utils/selector';
|
|
7
7
|
|
|
@@ -129,41 +129,4 @@ export default class FleetBundle extends SteveModel {
|
|
|
129
129
|
);
|
|
130
130
|
}
|
|
131
131
|
}
|
|
132
|
-
|
|
133
|
-
get authorId() {
|
|
134
|
-
return this.metadata?.labels?.[FLEET_ANNOTATIONS.CREATED_BY_USER_ID];
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
get author() {
|
|
138
|
-
if (this.authorId) {
|
|
139
|
-
return this.$rootGetters['management/byId'](MANAGEMENT.USER, this.authorId);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
return null;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
get createdBy() {
|
|
146
|
-
const displayName = this.metadata?.labels?.[FLEET_ANNOTATIONS.CREATED_BY_USER_NAME];
|
|
147
|
-
|
|
148
|
-
if (!displayName) {
|
|
149
|
-
return null;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
return {
|
|
153
|
-
displayName,
|
|
154
|
-
location: !this.author ? null : {
|
|
155
|
-
name: 'c-cluster-product-resource-id',
|
|
156
|
-
params: {
|
|
157
|
-
cluster: '_',
|
|
158
|
-
product: 'auth',
|
|
159
|
-
resource: MANAGEMENT.USER,
|
|
160
|
-
id: this.author.id,
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
};
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
get showCreatedBy() {
|
|
167
|
-
return !!this.createdBy;
|
|
168
|
-
}
|
|
169
132
|
}
|
|
@@ -199,4 +199,8 @@ export default class HelmOp extends FleetApplication {
|
|
|
199
199
|
get bundleDeployments() {
|
|
200
200
|
return this.$getters['matching'](FLEET.BUNDLE_DEPLOYMENT, { [FLEET_ANNOTATIONS.HELM_NAME]: this.name });
|
|
201
201
|
}
|
|
202
|
+
|
|
203
|
+
get fullDetailPageOverride() {
|
|
204
|
+
return true;
|
|
205
|
+
}
|
|
202
206
|
}
|
|
@@ -179,4 +179,16 @@ export default class Project extends HybridModel {
|
|
|
179
179
|
get confirmRemove() {
|
|
180
180
|
return true;
|
|
181
181
|
}
|
|
182
|
+
|
|
183
|
+
get glance() {
|
|
184
|
+
const glance = [...this._glance];
|
|
185
|
+
|
|
186
|
+
const namespaceIndex = glance.findIndex((item) => item.name === 'namespace');
|
|
187
|
+
|
|
188
|
+
if (namespaceIndex > -1) {
|
|
189
|
+
glance.splice(namespaceIndex, 1);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return glance;
|
|
193
|
+
}
|
|
182
194
|
}
|
package/models/namespace.js
CHANGED
|
@@ -269,4 +269,34 @@ export default class Namespace extends SteveModel {
|
|
|
269
269
|
get hideDetailLocation() {
|
|
270
270
|
return !!this.$rootGetters['currentProduct'].hideNamespaceLocation;
|
|
271
271
|
}
|
|
272
|
+
|
|
273
|
+
get glance() {
|
|
274
|
+
const glance = [...this._glance];
|
|
275
|
+
|
|
276
|
+
const namespaceIndex = glance.findIndex((item) => item.name === 'namespace');
|
|
277
|
+
|
|
278
|
+
if (namespaceIndex > -1) {
|
|
279
|
+
glance.splice(namespaceIndex, 1, this.projectGlance);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// projectGlance could be undefined
|
|
283
|
+
return glance.filter(Boolean);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
get projectGlance() {
|
|
287
|
+
// Not all namespaces are in a project
|
|
288
|
+
if (!this.project) {
|
|
289
|
+
return undefined;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return {
|
|
293
|
+
name: 'project',
|
|
294
|
+
label: this.t('component.resource.detail.glance.project'),
|
|
295
|
+
formatter: 'Link',
|
|
296
|
+
formatterOpts: {
|
|
297
|
+
to: this.project.detailLocation, row: {}, options: { internal: true }
|
|
298
|
+
},
|
|
299
|
+
content: this.project.nameDisplay
|
|
300
|
+
};
|
|
301
|
+
}
|
|
272
302
|
}
|
package/models/workload.js
CHANGED
|
@@ -595,6 +595,9 @@ export default class Workload extends WorkloadService {
|
|
|
595
595
|
return selector;
|
|
596
596
|
}
|
|
597
597
|
|
|
598
|
+
/**
|
|
599
|
+
* Match Expression version of the podSelector
|
|
600
|
+
*/
|
|
598
601
|
get podMatchExpression() {
|
|
599
602
|
return this.podSelector ? parse(this.podSelector) : null;
|
|
600
603
|
}
|