@rancher/shell 3.0.12-rc.4 → 3.0.12-rc.6
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/apis/intf/resources-api/resource-base.ts +42 -2
- package/apis/intf/resources-api/resource-instance.ts +101 -0
- package/apis/intf/resources-api/resources-api.ts +177 -23
- package/apis/intf/resources.ts +8 -6
- package/apis/resources/__tests__/resources-api-class.test.ts +309 -28
- package/apis/resources/resources-api-class.ts +232 -43
- package/assets/images/providers/traefik.png +0 -0
- package/assets/styles/global/_button.scss +1 -1
- package/assets/translations/en-us.yaml +49 -14
- package/chart/__tests__/rancher-monitoring-dashboards-index.test.ts +269 -0
- package/chart/rancher-monitoring-dashboards/index.vue +155 -0
- package/cloud-credential/__tests__/generic.test.ts +132 -0
- package/cloud-credential/generic.vue +33 -3
- package/components/ActionDropdownShell.vue +5 -3
- package/components/ButtonGroup.vue +26 -1
- package/components/CruResource.vue +51 -2
- package/components/EtcdInfoBanner.vue +2 -12
- package/components/GrafanaDashboard.vue +2 -8
- package/components/PromptRestore.vue +93 -32
- package/components/Questions/index.vue +1 -0
- package/components/Resource/Detail/Masthead/__tests__/index.test.ts +68 -14
- package/components/Resource/Detail/Masthead/index.vue +8 -3
- package/components/ResourceTable.vue +1 -0
- package/components/SortableTable/index.vue +4 -3
- package/components/Wizard.vue +14 -1
- package/components/__tests__/ButtonGroup.test.ts +56 -0
- package/components/__tests__/PromptRestore.test.ts +169 -19
- package/components/fleet/GitRepoAdvancedTab.vue +1 -0
- package/components/fleet/GitRepoMetadataTab.vue +5 -0
- package/components/fleet/HelmOpAppCoConfigTab.vue +4 -0
- package/components/fleet/HelmOpMetadataTab.vue +5 -0
- package/components/form/FileSelector.vue +39 -1
- package/components/form/PrivateRegistry.constants.ts +7 -0
- package/components/form/PrivateRegistry.vue +253 -18
- package/components/form/Security.vue +1 -1
- package/components/form/SelectOrCreateAuthSecret.vue +140 -17
- package/components/form/__tests__/FileSelector.test.ts +23 -0
- package/components/form/__tests__/PrivateRegistry.test.ts +463 -73
- package/components/form/__tests__/SelectOrCreateAuthSecret.test.ts +122 -0
- package/components/formatter/EtcdSnapshotName.vue +73 -0
- package/components/formatter/Translate.vue +22 -1
- package/components/nav/Group.vue +5 -10
- package/components/nav/Header.vue +8 -1
- package/components/nav/TopLevelMenu.vue +1 -30
- package/components/nav/__tests__/Group.test.ts +61 -0
- package/components/templates/default.vue +7 -0
- package/config/features.js +1 -0
- package/config/labels-annotations.js +3 -0
- package/config/product/manager.js +6 -0
- package/config/secret.ts +10 -0
- package/config/settings.ts +6 -2
- package/config/table-headers.js +5 -2
- package/config/types.js +7 -0
- package/core/__tests__/plugin-products-apply.test.ts +787 -0
- package/core/__tests__/plugin-products-extend.test.ts +189 -0
- package/core/__tests__/plugin-products-helpers.test.ts +81 -78
- package/core/__tests__/plugin-products-new.test.ts +389 -0
- package/core/__tests__/plugin-products-scenarios.test.ts +980 -0
- package/core/__tests__/plugin-products-side-menu.test.ts +1935 -0
- package/core/plugin-products-base.ts +201 -93
- package/core/plugin-products-extending.ts +1 -1
- package/core/plugin-products-external.ts +380 -0
- package/core/plugin-products-helpers.ts +28 -24
- package/core/plugin-products-internal.ts +207 -0
- package/core/plugin-products-top-level.ts +10 -5
- package/core/plugin-products-type-guards.ts +17 -6
- package/core/plugin-products.ts +5 -7
- package/core/plugin-types.ts +1 -389
- package/core/plugin.ts +10 -11
- package/core/types.ts +6 -202
- package/detail/__tests__/provisioning.cattle.io.cluster.test.ts +52 -0
- package/detail/pod.vue +1 -1
- package/detail/provisioning.cattle.io.cluster.vue +79 -5
- package/dialog/RotateEncryptionKeyDialog.vue +33 -9
- package/dialog/__tests__/RotateEncryptionKeyDialog.test.ts +78 -0
- package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +92 -0
- package/edit/__tests__/fleet.cattle.io.helmop.test.ts +101 -0
- package/edit/__tests__/management.cattle.io.setting.test.ts +2 -1
- package/edit/auth/AuthProviderWarningBanners.vue +12 -0
- package/edit/auth/__tests__/AuthProviderWarningBanners.test.ts +10 -1
- package/edit/auth/__tests__/azuread.test.ts +1 -0
- package/edit/auth/__tests__/github.test.ts +1 -0
- package/edit/auth/__tests__/oidc.test.ts +1 -0
- package/edit/auth/__tests__/saml.test.ts +1 -0
- package/edit/auth/ldap/__tests__/index.test.ts +1 -0
- package/edit/compliance.cattle.io.clusterscanprofile.vue +39 -41
- package/edit/fleet.cattle.io.gitrepo.vue +70 -16
- package/edit/fleet.cattle.io.helmop.vue +51 -5
- package/edit/helm.cattle.io.projecthelmchart.vue +1 -0
- package/edit/{management.cattle.io.setting.vue → management.cattle.io.setting/index.vue} +32 -9
- package/edit/management.cattle.io.setting/system-default-registry-pull-secrets.vue +81 -0
- package/edit/provisioning.cattle.io.cluster/SelectCredential.vue +3 -12
- package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +18 -0
- package/edit/provisioning.cattle.io.cluster/index.vue +1 -1
- package/edit/provisioning.cattle.io.cluster/rke2.vue +6 -2
- package/edit/provisioning.cattle.io.cluster/tabs/etcd/index.vue +0 -1
- package/edit/provisioning.cattle.io.cluster/tabs/networking/index.vue +2 -2
- package/edit/provisioning.cattle.io.cluster/tabs/registries/index.vue +14 -55
- package/list/management.cattle.io.user.vue +24 -5
- package/list/utils/management.cattle.io.cluster.utils.ts +1 -1
- package/machine-config/__tests__/generic.test.ts +163 -0
- package/machine-config/components/EC2Networking.vue +31 -7
- package/machine-config/components/__tests__/EC2Networking.test.ts +38 -15
- package/machine-config/generic.vue +32 -5
- package/mixins/resource-fetch.js +1 -1
- package/models/__tests__/provisioning.cattle.io.cluster.test.ts +156 -0
- package/models/__tests__/rke-machine.cattle.io.vmwarevspheremachinetemplate.test.ts +22 -0
- package/models/__tests__/secret.test.ts +68 -1
- package/models/management.cattle.io.cluster.js +21 -3
- package/models/pod.js +25 -12
- package/models/provisioning.cattle.io.cluster.js +59 -9
- package/models/rke-machine.cattle.io.vmwarevspheremachinetemplate.js +9 -0
- package/models/rke.cattle.io.etcdsnapshot.js +17 -9
- package/models/secret.js +19 -0
- package/models/workload.js +12 -7
- package/package.json +1 -1
- package/pages/auth/login.vue +1 -1
- package/pages/c/_cluster/apps/charts/__tests__/install.test.ts +569 -81
- package/pages/c/_cluster/apps/charts/install.vue +143 -28
- package/pages/c/_cluster/auth/config/index.vue +11 -4
- package/pages/c/_cluster/monitoring/index.vue +6 -1
- package/pkg/require-asset.lib.js +25 -0
- package/pkg/vue.config.js +7 -0
- package/plugins/dashboard-store/__tests__/resource-class.test.ts +84 -0
- package/plugins/dashboard-store/getters.js +0 -1
- package/plugins/dashboard-store/resource-class.js +62 -15
- package/plugins/steve/__tests__/steve-class-resource-api.test.ts +147 -0
- package/plugins/steve/steve-class.js +43 -0
- package/plugins/steve/subscribe.js +11 -0
- package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +115 -0
- package/rancher-components/Form/LabeledInput/LabeledInput.vue +83 -3
- package/rancher-components/Form/TextArea/TextAreaAutoGrow.vue +30 -0
- package/rancher-components/Form/TextArea/__tests__/TextAreaAutoGrow.test.ts +95 -0
- package/rancher-components/RcButton/index.ts +1 -1
- package/rancher-components/RcDropdown/RcDropdownTrigger.vue +6 -1
- package/store/__tests__/auth.test.ts +18 -0
- package/store/__tests__/features.test.ts +131 -0
- package/store/__tests__/growl.test.ts +374 -0
- package/store/__tests__/modal.test.ts +131 -0
- package/store/__tests__/notifications.test.ts +466 -1
- package/store/__tests__/slideInPanel.test.ts +88 -0
- package/store/__tests__/type-map.utils.test.ts +433 -0
- package/store/__tests__/ui-context.test.ts +255 -0
- package/store/auth.js +11 -1
- package/store/features.js +5 -0
- package/store/plugins.js +6 -0
- package/types/rancher/steve.api.ts +2 -2
- package/types/shell/index.d.ts +120 -0
- package/types/store/dashboard-store.types.ts +2 -2
- package/types/store/type-map.ts +262 -1
- package/utils/__tests__/operation-cr.test.ts +34 -0
- package/utils/async.ts +2 -0
- package/utils/grafana.js +2 -17
- package/utils/monitoring.js +38 -1
- package/utils/operation-cr.js +19 -0
- package/utils/require-asset.ts +7 -0
- package/utils/validators/__tests__/private-registry.test.ts +27 -15
- package/utils/validators/private-registry.ts +15 -4
- package/core/__tests__/plugin-products.test.ts +0 -4694
package/core/types.ts
CHANGED
|
@@ -1,13 +1,9 @@
|
|
|
1
|
-
import { ProductFunction } from './plugin';
|
|
2
1
|
import { RouteRecordRaw } from 'vue-router';
|
|
3
2
|
import type { ExtensionManager } from '@shell/types/extension-manager';
|
|
4
3
|
import { PaginationSettingsStores } from '@shell/types/resources/settings';
|
|
5
|
-
import
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
ProductChildGroup,
|
|
9
|
-
ProductChildPage
|
|
10
|
-
} from './plugin-types';
|
|
4
|
+
import { IExtensionProducts } from '@shell/core/plugin-products-external';
|
|
5
|
+
import { RouteRecordRawWithParams } from '@shell/core/plugin-types';
|
|
6
|
+
import { TypeMapProduct } from '@shell/types/store/type-map';
|
|
11
7
|
|
|
12
8
|
// Cluster Provisioning types
|
|
13
9
|
export * from './types-provisioning';
|
|
@@ -200,137 +196,6 @@ export type ExtensionEnvironment = {
|
|
|
200
196
|
docsVersion: string; /** e.g. 'v2.10' */
|
|
201
197
|
};
|
|
202
198
|
|
|
203
|
-
export interface ProductOptions {
|
|
204
|
-
/**
|
|
205
|
-
* The category this product belongs under. i.e. 'config'
|
|
206
|
-
*/
|
|
207
|
-
category?: string;
|
|
208
|
-
|
|
209
|
-
/**
|
|
210
|
-
* Hide the Copy KubeConfig button in the header
|
|
211
|
-
*/
|
|
212
|
-
hideCopyConfig?: boolean;
|
|
213
|
-
|
|
214
|
-
/**
|
|
215
|
-
* Hide the Download KubeConfig button in the header
|
|
216
|
-
*/
|
|
217
|
-
hideKubeConfig?: boolean;
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* Hide the Kubectl Shell button in the header
|
|
221
|
-
*/
|
|
222
|
-
hideKubeShell?: boolean;
|
|
223
|
-
|
|
224
|
-
/**
|
|
225
|
-
* Hide the Namespace location
|
|
226
|
-
*/
|
|
227
|
-
hideNamespaceLocation?: boolean;
|
|
228
|
-
|
|
229
|
-
/**
|
|
230
|
-
* Hide the system resources
|
|
231
|
-
*/
|
|
232
|
-
|
|
233
|
-
hideSystemResources?: boolean;
|
|
234
|
-
/**
|
|
235
|
-
* The icon that should be displayed beside this item in the navigation.
|
|
236
|
-
*/
|
|
237
|
-
icon?: string,
|
|
238
|
-
|
|
239
|
-
/**
|
|
240
|
-
* Only load the product if the feature is present
|
|
241
|
-
*/
|
|
242
|
-
ifFeature?: string | RegExp;
|
|
243
|
-
|
|
244
|
-
/**
|
|
245
|
-
* Only load the product if the type is present
|
|
246
|
-
*/
|
|
247
|
-
ifHave?: string;
|
|
248
|
-
|
|
249
|
-
/**
|
|
250
|
-
* Only load the product if the group is present
|
|
251
|
-
*/
|
|
252
|
-
ifHaveGroup?: string | RegExp;
|
|
253
|
-
|
|
254
|
-
/**
|
|
255
|
-
* Only load the product if the type is present
|
|
256
|
-
*/
|
|
257
|
-
ifHaveType?: string | RegExp;
|
|
258
|
-
|
|
259
|
-
/**
|
|
260
|
-
* Hide the product if the type is present (opposite of ifHaveType)
|
|
261
|
-
*/
|
|
262
|
-
ifNotHaveType?: string | RegExp;
|
|
263
|
-
|
|
264
|
-
/**
|
|
265
|
-
* The vuex store that this product should use by default i.e. 'management'
|
|
266
|
-
*/
|
|
267
|
-
inStore?: string;
|
|
268
|
-
|
|
269
|
-
/**
|
|
270
|
-
* Show the cluster switcher in the navigation
|
|
271
|
-
*/
|
|
272
|
-
showClusterSwitcher?: boolean;
|
|
273
|
-
|
|
274
|
-
/**
|
|
275
|
-
* Indicates whether UI Extensions can add pages to this product
|
|
276
|
-
*/
|
|
277
|
-
extendable?: boolean;
|
|
278
|
-
|
|
279
|
-
/**
|
|
280
|
-
* Show the namespace filter in the header
|
|
281
|
-
*/
|
|
282
|
-
showNamespaceFilter?: boolean;
|
|
283
|
-
|
|
284
|
-
/**
|
|
285
|
-
* A number used to determine where in navigation this item will be placed. The highest number will be at the top of the list.
|
|
286
|
-
*/
|
|
287
|
-
weight?: number;
|
|
288
|
-
|
|
289
|
-
/**
|
|
290
|
-
* The route that the product will lead to if click on in navigation.
|
|
291
|
-
*/
|
|
292
|
-
to?: PluginRouteRecordRaw;
|
|
293
|
-
|
|
294
|
-
/**
|
|
295
|
-
* Whether the product can be removed by users (default: false — products are built-in/not removable unless explicitly set to true)
|
|
296
|
-
*/
|
|
297
|
-
removable?: boolean;
|
|
298
|
-
|
|
299
|
-
/**
|
|
300
|
-
* Alternative to the icon property. Uses require
|
|
301
|
-
*/
|
|
302
|
-
svg?: Function;
|
|
303
|
-
|
|
304
|
-
/**
|
|
305
|
-
* Product name
|
|
306
|
-
*/
|
|
307
|
-
name?: string;
|
|
308
|
-
|
|
309
|
-
/**
|
|
310
|
-
* controls whether a workspace switcher dropdown appears in the header (instead of the namespace filter) if set to true
|
|
311
|
-
*/
|
|
312
|
-
showWorkspaceSwitcher?: boolean;
|
|
313
|
-
|
|
314
|
-
/**
|
|
315
|
-
*
|
|
316
|
-
*/
|
|
317
|
-
label?: string;
|
|
318
|
-
|
|
319
|
-
labelKey?: string;
|
|
320
|
-
|
|
321
|
-
iconHeader?: string;
|
|
322
|
-
|
|
323
|
-
// Do not use - internal use only
|
|
324
|
-
version?: number;
|
|
325
|
-
|
|
326
|
-
/**
|
|
327
|
-
* Leaving these here for completeness but I don't think these should be advertised as useable to plugin creators.
|
|
328
|
-
*/
|
|
329
|
-
// ifHaveVerb: string | RegExp;
|
|
330
|
-
// supportRoute: string;
|
|
331
|
-
// typeStoreMap: string;
|
|
332
|
-
}
|
|
333
|
-
|
|
334
199
|
/**
|
|
335
200
|
* Configuration required to show a header in a ResourceTable
|
|
336
201
|
*/
|
|
@@ -399,17 +264,6 @@ export interface HeaderOptions {
|
|
|
399
264
|
*/
|
|
400
265
|
export type PaginationHeaderOptions = Omit<HeaderOptions, 'getValue'>
|
|
401
266
|
|
|
402
|
-
export type ResourceTypeConfig = {
|
|
403
|
-
options?: {
|
|
404
|
-
isCreatable?: boolean;
|
|
405
|
-
isEditable?: boolean;
|
|
406
|
-
},
|
|
407
|
-
listHeaders?: {
|
|
408
|
-
legacy?: HeaderOptions[];
|
|
409
|
-
paginated?: PaginationHeaderOptions[];
|
|
410
|
-
}
|
|
411
|
-
};
|
|
412
|
-
|
|
413
267
|
/**
|
|
414
268
|
* External extension configuration for @HeaderOptions
|
|
415
269
|
*/
|
|
@@ -568,7 +422,7 @@ export interface DSLReturnType {
|
|
|
568
422
|
* @param options {@link ProductOptions}
|
|
569
423
|
* @returns {@link void}
|
|
570
424
|
*/
|
|
571
|
-
product: (options:
|
|
425
|
+
product: (options: TypeMapProduct) => void;
|
|
572
426
|
|
|
573
427
|
/**
|
|
574
428
|
/**
|
|
@@ -691,62 +545,12 @@ export type ModelExtensionContext = {
|
|
|
691
545
|
/**
|
|
692
546
|
* Constructor signature for a model extension
|
|
693
547
|
*/
|
|
694
|
-
export type ModelExtensionConstructor = (context: ModelExtensionContext) => Object;
|
|
548
|
+
export type ModelExtensionConstructor = new (context: ModelExtensionContext) => Object;
|
|
695
549
|
|
|
696
550
|
/**
|
|
697
551
|
* Interface for a UI Extension
|
|
698
552
|
*/
|
|
699
|
-
export interface IExtension {
|
|
700
|
-
/**
|
|
701
|
-
* Register a top-level product as a flag on the plugin
|
|
702
|
-
* @internal - DO NOT USE - Internal API only
|
|
703
|
-
*/
|
|
704
|
-
_registerTopLevelProduct(): void;
|
|
705
|
-
/**
|
|
706
|
-
*
|
|
707
|
-
* @internal - DO NOT USE - Internal API only
|
|
708
|
-
*/
|
|
709
|
-
_setStartRouteWithProduct(value: boolean): void;
|
|
710
|
-
|
|
711
|
-
/**
|
|
712
|
-
* Add a product to the sidebar, with children and a side menu for navigation for internal pages
|
|
713
|
-
* @param name
|
|
714
|
-
* @param config
|
|
715
|
-
*/
|
|
716
|
-
addProduct(product: ProductMetadata, config: ProductChildPage[]): void;
|
|
717
|
-
addProduct(product: ProductMetadata, config: ProductChildGroup[]): void;
|
|
718
|
-
addProduct(product: ProductMetadata, config: ProductChild[]): void;
|
|
719
|
-
|
|
720
|
-
/**
|
|
721
|
-
* Add a product to the sidebar, without children (no side menu, single page only)
|
|
722
|
-
* @param product
|
|
723
|
-
*/
|
|
724
|
-
addProduct(product: ProductSinglePage): void;
|
|
725
|
-
|
|
726
|
-
/**
|
|
727
|
-
* Add a product with just a name (convenience/bridge method for quick setup).
|
|
728
|
-
* Creates a basic product with an empty page component automatically.
|
|
729
|
-
* This is useful for getting started quickly - expand to the full API once you're ready to add custom pages.
|
|
730
|
-
* @param productName Simple product name - will be used as both the name and label
|
|
731
|
-
*/
|
|
732
|
-
addProduct(productName: string): void;
|
|
733
|
-
|
|
734
|
-
/**
|
|
735
|
-
* Add a product to the sidebar (deprecated, use other signatures of addProduct instead)
|
|
736
|
-
* @deprecated Use other `addProduct` signatures instead
|
|
737
|
-
* @param importFn Function that will import the module containing a product definition
|
|
738
|
-
*/
|
|
739
|
-
addProduct(importFn: ProductFunction): void;
|
|
740
|
-
|
|
741
|
-
/**
|
|
742
|
-
* Extend an existing product in Rancher, with children and a side menu for navigation for internal pages
|
|
743
|
-
*
|
|
744
|
-
* @param product Product to be extended
|
|
745
|
-
* @param config Product extension configuration
|
|
746
|
-
*/
|
|
747
|
-
extendProduct(product: StandardProductName | string, config: ProductChildPage[]): void;
|
|
748
|
-
extendProduct(product: StandardProductName | string, config: ProductChildGroup[]): void;
|
|
749
|
-
extendProduct(product: StandardProductName | string, config: ProductChild[]): void;
|
|
553
|
+
export interface IExtension extends IExtensionProducts {
|
|
750
554
|
|
|
751
555
|
/**
|
|
752
556
|
* Add a locale to the i18n store
|
|
@@ -241,4 +241,56 @@ describe('view: provisioning.cattle.io.cluster', () => {
|
|
|
241
241
|
expect(wrapper.vm.fakeMachines).toHaveLength(0);
|
|
242
242
|
});
|
|
243
243
|
});
|
|
244
|
+
|
|
245
|
+
describe('computed: showLog', () => {
|
|
246
|
+
it('returns true when mgmt has a log link and extDetailTabs.logs is enabled', async() => {
|
|
247
|
+
const value = { mgmt: { hasLink: (link: string) => link === 'log' } };
|
|
248
|
+
|
|
249
|
+
const wrapper = shallowMount(ProvisioningCattleIoCluster, {
|
|
250
|
+
props: { value },
|
|
251
|
+
global: { mocks },
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
await wrapper.setData({ extDetailTabs: { logs: true } });
|
|
255
|
+
|
|
256
|
+
expect(wrapper.vm.showLog).toStrictEqual(true);
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
it('returns false when mgmt does not have a log link', async() => {
|
|
260
|
+
const value = { mgmt: { hasLink: () => false } };
|
|
261
|
+
|
|
262
|
+
const wrapper = shallowMount(ProvisioningCattleIoCluster, {
|
|
263
|
+
props: { value },
|
|
264
|
+
global: { mocks },
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
await wrapper.setData({ extDetailTabs: { logs: true } });
|
|
268
|
+
|
|
269
|
+
expect(wrapper.vm.showLog).toStrictEqual(false);
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
it('returns false when mgmt has a log link but extDetailTabs.logs is disabled', async() => {
|
|
273
|
+
const value = { mgmt: { hasLink: (link: string) => link === 'log' } };
|
|
274
|
+
|
|
275
|
+
const wrapper = shallowMount(ProvisioningCattleIoCluster, {
|
|
276
|
+
props: { value },
|
|
277
|
+
global: { mocks },
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
await wrapper.setData({ extDetailTabs: { logs: false } });
|
|
281
|
+
|
|
282
|
+
expect(wrapper.vm.showLog).toStrictEqual(false);
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
it('returns false when mgmt is undefined', async() => {
|
|
286
|
+
const value = {};
|
|
287
|
+
|
|
288
|
+
const wrapper = shallowMount(ProvisioningCattleIoCluster, {
|
|
289
|
+
props: { value },
|
|
290
|
+
global: { mocks },
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
expect(wrapper.vm.showLog).toBeFalsy();
|
|
294
|
+
});
|
|
295
|
+
});
|
|
244
296
|
});
|
package/detail/pod.vue
CHANGED
|
@@ -38,7 +38,7 @@ export default {
|
|
|
38
38
|
const projectId = namespace?.metadata?.labels[PROJECT];
|
|
39
39
|
|
|
40
40
|
if (projectId) {
|
|
41
|
-
this.POD_PROJECT_METRICS_DETAIL_URL = `/api/v1/namespaces/cattle-project-${ projectId }-monitoring/services/http:cattle-project-${ projectId }-monitoring-grafana:80/proxy/d/rancher-pod-containers-1/rancher-pod-containers?orgId=1
|
|
41
|
+
this.POD_PROJECT_METRICS_DETAIL_URL = `/api/v1/namespaces/cattle-project-${ projectId }-monitoring/services/http:cattle-project-${ projectId }-monitoring-grafana:80/proxy/d/rancher-pod-containers-1/rancher-pod-containers?orgId=1`;
|
|
42
42
|
this.POD_PROJECT_METRICS_SUMMARY_URL = `/api/v1/namespaces/cattle-project-${ projectId }-monitoring/services/http:cattle-project-${ projectId }-monitoring-grafana:80/proxy/d/rancher-pod-1/rancher-pod?orgId=1`;
|
|
43
43
|
|
|
44
44
|
this.showProjectMetrics = await allDashboardsExist(this.$store, this.currentCluster.id, [this.POD_PROJECT_METRICS_DETAIL_URL, this.POD_PROJECT_METRICS_SUMMARY_URL], 'cluster', projectId);
|
|
@@ -6,7 +6,9 @@ import SortableTable from '@shell/components/SortableTable';
|
|
|
6
6
|
import CopyCode from '@shell/components/CopyCode';
|
|
7
7
|
import Tab from '@shell/components/Tabbed/Tab';
|
|
8
8
|
import { allHash } from '@shell/utils/promise';
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
CAPI, MANAGEMENT, NORMAN, SNAPSHOT, OPERATION
|
|
11
|
+
} from '@shell/config/types';
|
|
10
12
|
import {
|
|
11
13
|
STATE, NAME as NAME_COL, AGE, INTERNAL_EXTERNAL_IP, STATE_NORMAN, ROLES, MACHINE_NODE_OS, MANAGEMENT_NODE_OS, NAME,
|
|
12
14
|
} from '@shell/config/table-headers';
|
|
@@ -149,6 +151,21 @@ export default {
|
|
|
149
151
|
fetchOne.snapshots = this.$store.dispatch('management/findAll', { type: SNAPSHOT });
|
|
150
152
|
}
|
|
151
153
|
|
|
154
|
+
// Fetch operation CRDs for clusters with day 2 ops enabled
|
|
155
|
+
if ( this.value.isImportedWithDayTwoOps ) {
|
|
156
|
+
const operationTypes = [
|
|
157
|
+
OPERATION.ETCD_SNAPSHOT,
|
|
158
|
+
OPERATION.ETCD_SNAPSHOT_RESTORE,
|
|
159
|
+
OPERATION.ENCRYPTION_KEY_ROTATE,
|
|
160
|
+
];
|
|
161
|
+
|
|
162
|
+
for (const opType of operationTypes) {
|
|
163
|
+
if (this.$store.getters['management/canList'](opType)) {
|
|
164
|
+
fetchOne[`operations_${ opType }`] = this.$store.dispatch('management/findAll', { type: opType });
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
152
169
|
if ( this.value.isImported || this.value.isCustom || this.value.isHostedKubernetesProvider ) {
|
|
153
170
|
fetchOne.clusterToken = this.value.getOrCreateToken();
|
|
154
171
|
}
|
|
@@ -210,9 +227,7 @@ export default {
|
|
|
210
227
|
if ( this.$store.getters['management/canList'](MANAGEMENT.RKE_TEMPLATE) ) {
|
|
211
228
|
this.$store.dispatch('management/findAll', { type: MANAGEMENT.RKE_TEMPLATE });
|
|
212
229
|
}
|
|
213
|
-
},
|
|
214
230
|
|
|
215
|
-
created() {
|
|
216
231
|
if ( this.showLog ) {
|
|
217
232
|
this.connectLog();
|
|
218
233
|
}
|
|
@@ -292,6 +307,7 @@ export default {
|
|
|
292
307
|
registration: true, // in this component
|
|
293
308
|
snapshots: true, // in this component
|
|
294
309
|
autoscaler: true, // in this component
|
|
310
|
+
operations: true, // in this component
|
|
295
311
|
related: true, // in ResourceTabs
|
|
296
312
|
events: true, // in ResourceTabs
|
|
297
313
|
conditions: true, // in ResourceTabs
|
|
@@ -477,13 +493,54 @@ export default {
|
|
|
477
493
|
showSnapshots() {
|
|
478
494
|
if (this.value.isRke1) {
|
|
479
495
|
return false;
|
|
480
|
-
} else if (this.value.isRke2) {
|
|
496
|
+
} else if (this.value.isRke2 || (this.value.isImported && this.value.isDayTwoOpsEnabled)) {
|
|
481
497
|
return this.$store.getters['management/canList'](SNAPSHOT) && this.extDetailTabs.snapshots;
|
|
482
498
|
}
|
|
483
499
|
|
|
484
500
|
return false;
|
|
485
501
|
},
|
|
486
502
|
|
|
503
|
+
showOperations() {
|
|
504
|
+
return this.value.isImportedWithDayTwoOps && this.extDetailTabs.operations;
|
|
505
|
+
},
|
|
506
|
+
|
|
507
|
+
clusterOperations() {
|
|
508
|
+
if (!this.value.isImportedWithDayTwoOps) {
|
|
509
|
+
return [];
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
const mgmtId = this.value.mgmt?.id;
|
|
513
|
+
const operationTypes = [
|
|
514
|
+
OPERATION.ETCD_SNAPSHOT,
|
|
515
|
+
OPERATION.ETCD_SNAPSHOT_RESTORE,
|
|
516
|
+
OPERATION.ENCRYPTION_KEY_ROTATE,
|
|
517
|
+
];
|
|
518
|
+
|
|
519
|
+
const allOps = [];
|
|
520
|
+
|
|
521
|
+
for (const opType of operationTypes) {
|
|
522
|
+
const resources = this.$store.getters['management/all'](opType) || [];
|
|
523
|
+
|
|
524
|
+
allOps.push(...resources.filter((op) => op.spec?.clusterRef?.name === mgmtId));
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
return allOps;
|
|
528
|
+
},
|
|
529
|
+
|
|
530
|
+
operationHeaders() {
|
|
531
|
+
return [
|
|
532
|
+
STATE,
|
|
533
|
+
NAME,
|
|
534
|
+
{
|
|
535
|
+
name: 'type',
|
|
536
|
+
labelKey: 'tableHeaders.type',
|
|
537
|
+
value: 'type',
|
|
538
|
+
sort: 'type',
|
|
539
|
+
},
|
|
540
|
+
AGE,
|
|
541
|
+
];
|
|
542
|
+
},
|
|
543
|
+
|
|
487
544
|
isRke1() {
|
|
488
545
|
return this.value.isRke1;
|
|
489
546
|
},
|
|
@@ -566,7 +623,10 @@ export default {
|
|
|
566
623
|
{
|
|
567
624
|
...STATE_NORMAN, value: 'snapshotFile.status', formatterOpts: { arbitrary: true }
|
|
568
625
|
},
|
|
569
|
-
|
|
626
|
+
{
|
|
627
|
+
...NAME,
|
|
628
|
+
formatter: 'EtcdSnapshotName',
|
|
629
|
+
},
|
|
570
630
|
{
|
|
571
631
|
name: 'size',
|
|
572
632
|
labelKey: 'tableHeaders.size',
|
|
@@ -1150,6 +1210,20 @@ export default {
|
|
|
1150
1210
|
</template>
|
|
1151
1211
|
</SortableTable>
|
|
1152
1212
|
</Tab>
|
|
1213
|
+
<Tab
|
|
1214
|
+
v-if="showOperations"
|
|
1215
|
+
name="operations"
|
|
1216
|
+
:label="t('cluster.tabs.operations')"
|
|
1217
|
+
:weight="0"
|
|
1218
|
+
>
|
|
1219
|
+
<SortableTable
|
|
1220
|
+
:headers="operationHeaders"
|
|
1221
|
+
default-sort-by="age"
|
|
1222
|
+
:table-actions="false"
|
|
1223
|
+
:rows="clusterOperations"
|
|
1224
|
+
:search="false"
|
|
1225
|
+
/>
|
|
1226
|
+
</Tab>
|
|
1153
1227
|
<AutoscalerTab
|
|
1154
1228
|
v-if="showAutoScalerTab"
|
|
1155
1229
|
:value="value"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script>
|
|
2
|
-
import { SNAPSHOT } from '@shell/config/types';
|
|
2
|
+
import { SNAPSHOT, OPERATION } from '@shell/config/types';
|
|
3
3
|
import AsyncButton from '@shell/components/AsyncButton';
|
|
4
4
|
import { Card } from '@components/Card';
|
|
5
5
|
import { Banner } from '@components/Banner';
|
|
@@ -10,7 +10,7 @@ import day from 'dayjs';
|
|
|
10
10
|
import { escapeHtml } from '@shell/utils/string';
|
|
11
11
|
import { DATE_FORMAT, TIME_FORMAT } from '@shell/store/prefs';
|
|
12
12
|
import { set } from '@shell/utils/object';
|
|
13
|
-
|
|
13
|
+
import { createOperationCR } from '@shell/utils/operation-cr';
|
|
14
14
|
export default {
|
|
15
15
|
emits: ['close'],
|
|
16
16
|
|
|
@@ -66,6 +66,10 @@ export default {
|
|
|
66
66
|
async getEtcdBackups() {
|
|
67
67
|
let etcdBackups = await this.$store.dispatch('management/findAll', { type: SNAPSHOT });
|
|
68
68
|
|
|
69
|
+
if (this.cluster.isImportedWithDayTwoOps) {
|
|
70
|
+
return etcdBackups
|
|
71
|
+
.filter((backup) => backup.metadata.namespace === this.cluster.mgmt?.id && backup.spec?.clusterName === this.cluster.mgmt?.metadata?.name );
|
|
72
|
+
}
|
|
69
73
|
etcdBackups = etcdBackups.filter((backup) => backup.clusterId === this.cluster.id);
|
|
70
74
|
|
|
71
75
|
return etcdBackups;
|
|
@@ -82,12 +86,32 @@ export default {
|
|
|
82
86
|
|
|
83
87
|
async apply(buttonDone) {
|
|
84
88
|
try {
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
89
|
+
const isImportedWithDayTwoOps = this.cluster?.isImportedWithDayTwoOps || this.cluster?.mgmt?.isDayTwoOpsEnabled;
|
|
90
|
+
|
|
91
|
+
if (isImportedWithDayTwoOps) {
|
|
92
|
+
if (!isImportedWithDayTwoOps) {
|
|
93
|
+
throw new Error(this.t('promptRotateEncryptionKey.error.unableToResolveTargetCluster'));
|
|
94
|
+
}
|
|
95
|
+
// For imported clusters with day 2 ops, create an encryption key rotation operation CR
|
|
96
|
+
const namespace = this.cluster.mgmt?.id;
|
|
97
|
+
const safePrefix = this.cluster.mgmt?.id;
|
|
98
|
+
const spec = {
|
|
99
|
+
clusterRef: {
|
|
100
|
+
apiVersion: 'management.cattle.io/v3',
|
|
101
|
+
kind: 'Cluster',
|
|
102
|
+
name: this.cluster.mgmt?.id,
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
createOperationCR(this.$store.dispatch, OPERATION.ENCRYPTION_KEY_ROTATE, spec, namespace, safePrefix);
|
|
107
|
+
} else {
|
|
108
|
+
const currentGeneration = this.cluster.spec?.rkeConfig?.rotateEncryptionKeys?.generation || 0;
|
|
109
|
+
|
|
110
|
+
// To rotate the encryption keys, increment
|
|
111
|
+
// rkeConfig.rotateEncyrptionKeys.generation in the YAML.
|
|
112
|
+
set(this.cluster, 'spec.rkeConfig.rotateEncryptionKeys.generation', currentGeneration + 1);
|
|
113
|
+
await this.cluster.save();
|
|
114
|
+
}
|
|
91
115
|
|
|
92
116
|
this.close(buttonDone);
|
|
93
117
|
} catch (err) {
|
|
@@ -128,7 +152,7 @@ export default {
|
|
|
128
152
|
<Banner
|
|
129
153
|
v-else
|
|
130
154
|
color="error"
|
|
131
|
-
label-key="promptRotateEncryptionKey.error"
|
|
155
|
+
label-key="promptRotateEncryptionKey.error.noBackup"
|
|
132
156
|
/>
|
|
133
157
|
</div>
|
|
134
158
|
</div>
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { shallowMount } from '@vue/test-utils';
|
|
2
|
+
import RotateEncryptionKeyDialog from '@shell/dialog/RotateEncryptionKeyDialog.vue';
|
|
3
|
+
import { OPERATION } from '@shell/config/types';
|
|
4
|
+
import { createOperationCR } from '@shell/utils/operation-cr';
|
|
5
|
+
|
|
6
|
+
jest.mock('@shell/utils/operation-cr', () => ({ createOperationCR: jest.fn() }));
|
|
7
|
+
|
|
8
|
+
describe('component: RotateEncryptionKeyDialog', () => {
|
|
9
|
+
const t = (key: string) => key;
|
|
10
|
+
|
|
11
|
+
const createWrapper = (cluster: any, dispatch = jest.fn()) => {
|
|
12
|
+
return shallowMount(RotateEncryptionKeyDialog, {
|
|
13
|
+
propsData: { cluster },
|
|
14
|
+
global: {
|
|
15
|
+
mocks: {
|
|
16
|
+
t,
|
|
17
|
+
$store: {
|
|
18
|
+
dispatch,
|
|
19
|
+
getters: {
|
|
20
|
+
isRancher: true,
|
|
21
|
+
'prefs/get': jest.fn((key: string) => (key.includes('date') ? 'YYYY-MM-DD' : 'HH:mm:ss')),
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
directives: { 'clean-html': true }
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
beforeEach(() => {
|
|
31
|
+
jest.clearAllMocks();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('should create operation CR for imported day 2 ops clusters', async() => {
|
|
35
|
+
(createOperationCR as jest.Mock).mockResolvedValue(undefined);
|
|
36
|
+
|
|
37
|
+
const cluster = {
|
|
38
|
+
isImportedWithDayTwoOps: true,
|
|
39
|
+
mgmt: { id: 'c-m-1' },
|
|
40
|
+
save: jest.fn(),
|
|
41
|
+
spec: { rkeConfig: {} }
|
|
42
|
+
};
|
|
43
|
+
const dispatch = jest.fn();
|
|
44
|
+
const buttonDone = jest.fn();
|
|
45
|
+
const wrapper = createWrapper(cluster, dispatch);
|
|
46
|
+
|
|
47
|
+
await wrapper.vm.apply(buttonDone);
|
|
48
|
+
|
|
49
|
+
expect(createOperationCR).toHaveBeenCalledWith(dispatch, OPERATION.ENCRYPTION_KEY_ROTATE, {
|
|
50
|
+
clusterRef: {
|
|
51
|
+
apiVersion: 'management.cattle.io/v3',
|
|
52
|
+
kind: 'Cluster',
|
|
53
|
+
name: 'c-m-1',
|
|
54
|
+
}
|
|
55
|
+
}, 'c-m-1', 'c-m-1');
|
|
56
|
+
expect(cluster.save).not.toHaveBeenCalled();
|
|
57
|
+
expect(buttonDone).toHaveBeenCalledWith(true);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('should update generation and save for non-day-2 clusters', async() => {
|
|
61
|
+
const save = jest.fn().mockResolvedValue(undefined);
|
|
62
|
+
const cluster = {
|
|
63
|
+
isImportedWithDayTwoOps: false,
|
|
64
|
+
mgmt: { id: 'c-m-1' },
|
|
65
|
+
save,
|
|
66
|
+
spec: { rkeConfig: { rotateEncryptionKeys: { generation: 4 } } }
|
|
67
|
+
};
|
|
68
|
+
const buttonDone = jest.fn();
|
|
69
|
+
const wrapper = createWrapper(cluster);
|
|
70
|
+
|
|
71
|
+
await wrapper.vm.apply(buttonDone);
|
|
72
|
+
|
|
73
|
+
expect(createOperationCR).not.toHaveBeenCalled();
|
|
74
|
+
expect(cluster.spec.rkeConfig.rotateEncryptionKeys.generation).toBe(5);
|
|
75
|
+
expect(save).toHaveBeenCalledWith();
|
|
76
|
+
expect(buttonDone).toHaveBeenCalledWith(true);
|
|
77
|
+
});
|
|
78
|
+
});
|