@rancher/shell 3.0.9 → 3.0.11
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 -0
- package/assets/styles/themes/_light.scss +6 -6
- package/assets/styles/themes/_modern.scss +14 -6
- package/assets/translations/en-us.yaml +9 -10
- package/chart/__tests__/rancher-backup-index.test.ts +248 -0
- package/chart/rancher-backup/index.vue +41 -2
- package/components/BrandImage.vue +6 -5
- package/components/ConsumptionGauge.vue +12 -4
- package/components/CopyToClipboard.vue +28 -0
- package/components/CopyToClipboardText.vue +4 -0
- package/components/CruResource.vue +1 -0
- package/components/DynamicContent/DynamicContentIcon.vue +3 -2
- package/components/ExplorerProjectsNamespaces.vue +1 -4
- package/components/GlobalRoleBindings.vue +1 -5
- package/components/LazyImage.vue +2 -1
- package/components/Resource/Detail/Card/Scaler.vue +4 -4
- package/components/ResourceDetail/index.vue +0 -21
- package/components/Tabbed/index.vue +6 -0
- package/components/__tests__/ConsumptionGauge.test.ts +31 -0
- package/components/__tests__/CruResource.test.ts +35 -1
- package/components/form/ProjectMemberEditor.vue +0 -10
- package/components/nav/TopLevelMenu.helper.ts +7 -79
- package/components/nav/__tests__/TopLevelMenu.helper.test.ts +2 -53
- package/composables/useIsNewDetailPageEnabled.test.ts +98 -0
- package/composables/useIsNewDetailPageEnabled.ts +12 -0
- package/config/private-label.js +2 -1
- package/config/product/apps.js +1 -0
- package/config/product/explorer.js +11 -1
- package/config/table-headers.js +0 -9
- package/config/types.js +0 -1
- package/core/__tests__/extension-manager-impl.test.js +187 -2
- package/core/extension-manager-impl.js +4 -2
- package/core/plugin-helpers.ts +31 -0
- package/detail/__tests__/node.test.ts +83 -0
- package/detail/management.cattle.io.oidcclient.vue +2 -1
- package/detail/node.vue +1 -0
- package/edit/auth/github-app-steps.vue +2 -0
- package/edit/auth/github-steps.vue +2 -0
- package/edit/catalog.cattle.io.clusterrepo.vue +17 -3
- package/edit/cloudcredential.vue +2 -1
- package/edit/management.cattle.io.user.vue +60 -35
- package/edit/monitoring.coreos.com.alertmanagerconfig/receiverConfig.vue +11 -6
- package/edit/provisioning.cattle.io.cluster/index.vue +5 -4
- package/edit/provisioning.cattle.io.cluster/shared.ts +4 -2
- package/edit/secret/generic.vue +1 -0
- package/edit/secret/index.vue +2 -1
- package/edit/service.vue +2 -14
- package/edit/token.vue +29 -68
- package/list/management.cattle.io.feature.vue +7 -1
- package/list/provisioning.cattle.io.cluster.vue +0 -49
- package/mixins/brand.js +2 -1
- package/models/catalog.cattle.io.clusterrepo.js +9 -0
- package/models/cluster.x-k8s.io.machinedeployment.js +8 -3
- package/models/management.cattle.io.authconfig.js +2 -1
- package/models/management.cattle.io.cluster.js +4 -3
- package/models/monitoring.coreos.com.receiver.js +11 -6
- package/models/provisioning.cattle.io.cluster.js +2 -2
- package/models/token.js +0 -4
- package/package.json +12 -12
- package/pages/account/index.vue +67 -96
- package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +66 -9
- package/pages/c/_cluster/apps/charts/index.vue +3 -8
- package/pages/c/_cluster/apps/charts/install.vue +8 -9
- package/pages/c/_cluster/explorer/index.vue +2 -19
- package/pages/c/_cluster/istio/index.vue +4 -2
- package/pages/c/_cluster/longhorn/index.vue +2 -1
- package/pages/c/_cluster/monitoring/index.vue +2 -2
- package/pages/c/_cluster/neuvector/index.vue +2 -1
- package/pages/c/_cluster/settings/performance.vue +0 -5
- package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +2 -1
- package/pages/c/_cluster/uiplugins/index.vue +2 -1
- package/pkg/auto-import.js +41 -0
- package/plugins/dashboard-store/resource-class.js +2 -2
- package/plugins/steve/__tests__/steve-class.test.ts +1 -1
- package/plugins/steve/steve-class.js +3 -3
- package/plugins/steve/steve-pagination-utils.ts +2 -5
- package/plugins/steve/subscribe.js +29 -4
- package/rancher-components/Pill/RcCounterBadge/RcCounterBadge.vue +7 -7
- package/rancher-components/Pill/RcStatusBadge/RcStatusBadge.vue +5 -2
- package/rancher-components/RcButton/RcButton.vue +3 -3
- package/rancher-components/RcButtonSplit/RcButtonSplit.test.ts +253 -0
- package/rancher-components/RcButtonSplit/RcButtonSplit.vue +158 -0
- package/rancher-components/RcButtonSplit/index.ts +1 -0
- package/rancher-components/RcIcon/types.ts +2 -2
- package/rancher-components/RcSection/RcSection.test.ts +323 -0
- package/rancher-components/RcSection/RcSection.vue +252 -0
- package/rancher-components/RcSection/RcSectionActions.test.ts +212 -0
- package/rancher-components/RcSection/RcSectionActions.vue +85 -0
- package/rancher-components/RcSection/RcSectionBadges.test.ts +149 -0
- package/rancher-components/RcSection/RcSectionBadges.vue +29 -0
- package/rancher-components/RcSection/index.ts +12 -0
- package/rancher-components/RcSection/types.ts +86 -0
- package/scripts/test-plugins-build.sh +9 -8
- package/types/shell/index.d.ts +93 -108
- package/utils/__tests__/require-asset.test.ts +98 -0
- package/utils/async.ts +1 -5
- package/utils/brand.ts +3 -1
- package/utils/favicon.js +4 -3
- package/utils/require-asset.ts +95 -0
- package/utils/style.ts +17 -0
- package/utils/units.js +14 -5
- package/vue.config.js +4 -3
- package/components/HarvesterServiceAddOnConfig.vue +0 -207
- package/models/ext.cattle.io.token.js +0 -48
|
@@ -571,17 +571,17 @@
|
|
|
571
571
|
--rc-success: #{$green001};
|
|
572
572
|
--rc-success-secondary: #{$green002};
|
|
573
573
|
|
|
574
|
-
--rc-warning: #{$
|
|
575
|
-
--rc-warning-secondary: #{$
|
|
574
|
+
--rc-warning: #{$yellow002};
|
|
575
|
+
--rc-warning-secondary: #{$yellow001};
|
|
576
576
|
|
|
577
577
|
--rc-error: #{$red001};
|
|
578
578
|
--rc-error-secondary: #{$red002};
|
|
579
579
|
|
|
580
|
-
--rc-unknown: #{$
|
|
581
|
-
--rc-unknown-secondary: #{$
|
|
580
|
+
--rc-unknown: #{$gray004};
|
|
581
|
+
--rc-unknown-secondary: #{$gray001};
|
|
582
582
|
|
|
583
|
-
--rc-none: #{$
|
|
584
|
-
--rc-none-secondary: #{$
|
|
583
|
+
--rc-none: #{$gray004};
|
|
584
|
+
--rc-none-secondary: #{$gray002};
|
|
585
585
|
|
|
586
586
|
--rc-primary-hover: #{$blue003};
|
|
587
587
|
|
|
@@ -701,17 +701,17 @@ BODY, .theme-light {
|
|
|
701
701
|
--rc-success: #{$green001};
|
|
702
702
|
--rc-success-secondary: #{$green002};
|
|
703
703
|
|
|
704
|
-
--rc-warning: #{$
|
|
705
|
-
--rc-warning-secondary: #{$
|
|
704
|
+
--rc-warning: #{$yellow002};
|
|
705
|
+
--rc-warning-secondary: #{$yellow001};
|
|
706
706
|
|
|
707
707
|
--rc-error: #{$red001};
|
|
708
708
|
--rc-error-secondary: #{$red002};
|
|
709
709
|
|
|
710
|
-
--rc-unknown: #{$
|
|
711
|
-
--rc-unknown-secondary: #{$
|
|
710
|
+
--rc-unknown: #{$gray004};
|
|
711
|
+
--rc-unknown-secondary: #{$gray001};
|
|
712
712
|
|
|
713
|
-
--rc-none: #{$
|
|
714
|
-
--rc-none-secondary: #{$
|
|
713
|
+
--rc-none: #{$gray004};
|
|
714
|
+
--rc-none-secondary: #{$gray002};
|
|
715
715
|
|
|
716
716
|
--rc-primary-hover: #{$blue003};
|
|
717
717
|
|
|
@@ -724,6 +724,10 @@ BODY, .theme-light {
|
|
|
724
724
|
--rc-disabled-background: #{$gray001};
|
|
725
725
|
--rc-disabled-text-color: #{$gray004};
|
|
726
726
|
|
|
727
|
+
--rc-section-background-primary: #{$lightest};
|
|
728
|
+
--rc-section-background-secondary: #{$lighter};
|
|
729
|
+
--rc-section-action-color: #{$gray009};
|
|
730
|
+
|
|
727
731
|
--rc-image-bg: #{$lightest};
|
|
728
732
|
--rc-image-color: #{$darkest};
|
|
729
733
|
|
|
@@ -1068,6 +1072,10 @@ BODY, .theme-dark {
|
|
|
1068
1072
|
--rc-disabled-background: #{$gray005};
|
|
1069
1073
|
--rc-disabled-text-color: #{$gray004};
|
|
1070
1074
|
|
|
1075
|
+
--rc-section-background-primary: #{$gray007};
|
|
1076
|
+
--rc-section-background-secondary: #{$gray008};
|
|
1077
|
+
--rc-section-action-color: #{$gray010};
|
|
1078
|
+
|
|
1071
1079
|
--rc-image-bg: #{$lightest};
|
|
1072
1080
|
--rc-image-color: #{$darkest};
|
|
1073
1081
|
|
|
@@ -30,6 +30,7 @@ generic:
|
|
|
30
30
|
comma: ', '
|
|
31
31
|
copy: Copy
|
|
32
32
|
copyToClipboard: Copy text to Clipboard
|
|
33
|
+
copyValueToClipboard: 'Copy {value} to Clipboard'
|
|
33
34
|
copiedToClipboard: Text copied to Clipboard
|
|
34
35
|
create: Create
|
|
35
36
|
created: Created
|
|
@@ -438,7 +439,6 @@ accountAndKeys:
|
|
|
438
439
|
notAllowed: You do not have permission to manage API Keys
|
|
439
440
|
apiEndpoint: "API Endpoint:"
|
|
440
441
|
copyApiEnpoint: Copy API Endpoint to clipboard
|
|
441
|
-
normanTokenDeprecation: The API Keys feature is being migrated to a new API. Any existing API Keys from the legacy API will continue to work, but new API Keys will be created using the new API.
|
|
442
442
|
add:
|
|
443
443
|
description:
|
|
444
444
|
label: Description
|
|
@@ -464,9 +464,7 @@ accountAndKeys:
|
|
|
464
464
|
month: Months
|
|
465
465
|
year: Years
|
|
466
466
|
scope: Scope
|
|
467
|
-
userPrincipal: User Principal
|
|
468
467
|
noScope: No Scope
|
|
469
|
-
enabled: Token enabled
|
|
470
468
|
info:
|
|
471
469
|
accessKey: Access Key
|
|
472
470
|
secretKey: Secret Key
|
|
@@ -475,7 +473,7 @@ accountAndKeys:
|
|
|
475
473
|
keyCreated: A new API Key has been created
|
|
476
474
|
bearerTokenTip: "Access Key and Secret Key can be sent as the username and password for HTTP Basic auth to authorize requests. You can also combine them to use as a Bearer token:"
|
|
477
475
|
ttlLimitedWarning: The Expiry time for this API Key was reduced due to system configuration
|
|
478
|
-
|
|
476
|
+
|
|
479
477
|
addClusterMemberDialog:
|
|
480
478
|
title: Add Cluster Member
|
|
481
479
|
|
|
@@ -1020,6 +1018,11 @@ asyncButton:
|
|
|
1020
1018
|
waiting: Generating…
|
|
1021
1019
|
|
|
1022
1020
|
backupRestoreOperator:
|
|
1021
|
+
monitoring:
|
|
1022
|
+
label: Monitoring
|
|
1023
|
+
enableMetrics: Enable Metrics
|
|
1024
|
+
enableServiceMonitor: Enable ServiceMonitor
|
|
1025
|
+
serviceMonitorTooltip: Requires rancher-monitoring to be installed before this can be enabled.
|
|
1023
1026
|
backup:
|
|
1024
1027
|
label: Resource Set
|
|
1025
1028
|
description: The Resource Set determines which resources the backup-restore-operator collects in a backup
|
|
@@ -1376,6 +1379,7 @@ catalog:
|
|
|
1376
1379
|
releaseName: Release Name
|
|
1377
1380
|
releaseNamespace: Release Namespace
|
|
1378
1381
|
repo:
|
|
1382
|
+
add: Add Repository
|
|
1379
1383
|
action:
|
|
1380
1384
|
refresh: Refresh
|
|
1381
1385
|
all: All
|
|
@@ -2721,8 +2725,6 @@ cluster:
|
|
|
2721
2725
|
v2: RKE2/K3s
|
|
2722
2726
|
validation:
|
|
2723
2727
|
iamInstanceProfileName: If the Amazon cloud provider is selected the "IAM Instance Profile Name" must be defined for each Machine Pool
|
|
2724
|
-
capi:
|
|
2725
|
-
notSupported: Managing clusters provisioned using CAPI infrastructure providers via the UI is currently limited to Rancher Turtles provisioned clusters.
|
|
2726
2728
|
|
|
2727
2729
|
clusterIndexPage:
|
|
2728
2730
|
hardwareResourceGauge:
|
|
@@ -5031,6 +5033,7 @@ node:
|
|
|
5031
5033
|
cpu: CPU
|
|
5032
5034
|
memory: MEMORY
|
|
5033
5035
|
pods: PODS
|
|
5036
|
+
running: Running
|
|
5034
5037
|
diskPressure: Disk Pressure
|
|
5035
5038
|
kubelet: kubelet
|
|
5036
5039
|
memoryPressure: Memory Pressure
|
|
@@ -5775,7 +5778,6 @@ projectMembers:
|
|
|
5775
5778
|
createNs: Create Namespaces
|
|
5776
5779
|
configmapsManage: Manage Config Maps
|
|
5777
5780
|
ingressManage: Manage Ingress
|
|
5778
|
-
projectcatalogsManage: Manage Project Catalogs
|
|
5779
5781
|
projectroletemplatebindingsManage: Manage Project Members
|
|
5780
5782
|
secretsManage: Manage Secrets
|
|
5781
5783
|
serviceaccountsManage: Manage Service Accounts
|
|
@@ -5785,7 +5787,6 @@ projectMembers:
|
|
|
5785
5787
|
configmapsView: View Config Maps
|
|
5786
5788
|
ingressView: View Ingress
|
|
5787
5789
|
monitoringUiView: View Monitoring
|
|
5788
|
-
projectcatalogsView: View Project Catalogs
|
|
5789
5790
|
projectroletemplatebindingsView: View Project Members
|
|
5790
5791
|
secretsView: View Secrets
|
|
5791
5792
|
serviceaccountsView: View Service Accounts
|
|
@@ -6857,7 +6858,6 @@ storageClass:
|
|
|
6857
6858
|
tooltip: By default the default storage class on the host Harvester cluster is used.
|
|
6858
6859
|
|
|
6859
6860
|
tableHeaders:
|
|
6860
|
-
isLegacy: Legacy
|
|
6861
6861
|
assuredConcurrencyShares: Assured Concurrency Shares
|
|
6862
6862
|
autoscaler: Autoscaler
|
|
6863
6863
|
accessKey: Access Key
|
|
@@ -8949,7 +8949,6 @@ performance:
|
|
|
8949
8949
|
label: Server-side Pagination
|
|
8950
8950
|
description: By default Lists will fetch all resources and paginate (create the page given sort and filter settings) locally in the browser. Server-Side Pagination moves this out from the browser to the server. This improves performance of the UI, especially in systems with lots of resources.
|
|
8951
8951
|
applicable: "Server-side pagination applies to Resource Types"
|
|
8952
|
-
featureFlag: Enabling/Disabling <i class="mr-5">"Server-side Pagination"</i> is now solely done so via the <i class="mr-5">ui-sql-cache</i> <a href="{ffUrl}">Feature Flag</a>
|
|
8953
8952
|
resources:
|
|
8954
8953
|
generic: most resources in the cluster's 'More Resources' section
|
|
8955
8954
|
all: All Resources
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import { shallowMount } from '@vue/test-utils';
|
|
2
|
+
import RancherBackup from '@shell/chart/rancher-backup/index.vue';
|
|
3
|
+
import { set } from '@shell/utils/object';
|
|
4
|
+
|
|
5
|
+
describe('rancher-backup: index', () => {
|
|
6
|
+
const defaultMocks = {
|
|
7
|
+
$store: {
|
|
8
|
+
dispatch: jest.fn().mockResolvedValue([]),
|
|
9
|
+
getters: {
|
|
10
|
+
'i18n/t': (text: string) => text,
|
|
11
|
+
t: (text: string) => text,
|
|
12
|
+
'cluster/all': () => [],
|
|
13
|
+
'cluster/paginationEnabled': () => false,
|
|
14
|
+
currentStore: () => 'cluster',
|
|
15
|
+
currentCluster: () => ({ id: 'local' }),
|
|
16
|
+
getStoreNameByProductId: 'cluster',
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
$fetchState: { pending: false }
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const defaultStubs = {
|
|
23
|
+
Tab: { template: '<div><slot /></div>' },
|
|
24
|
+
Tabbed: { template: '<div><slot /></div>' },
|
|
25
|
+
S3: true,
|
|
26
|
+
RadioGroup: true,
|
|
27
|
+
LabeledInput: true,
|
|
28
|
+
LabeledSelect: true,
|
|
29
|
+
Banner: true,
|
|
30
|
+
Checkbox: true
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const createWrapper = (propsData = {}, mocks = {}, monitoringInstalled = false) => {
|
|
34
|
+
return shallowMount(RancherBackup, {
|
|
35
|
+
props: {
|
|
36
|
+
value: {},
|
|
37
|
+
mode: 'create',
|
|
38
|
+
...propsData
|
|
39
|
+
},
|
|
40
|
+
global: {
|
|
41
|
+
mocks: {
|
|
42
|
+
...defaultMocks,
|
|
43
|
+
...mocks
|
|
44
|
+
},
|
|
45
|
+
stubs: defaultStubs
|
|
46
|
+
},
|
|
47
|
+
computed: {
|
|
48
|
+
monitoringStatus: () => ({ installed: monitoringInstalled }),
|
|
49
|
+
radioOptions: () => ({
|
|
50
|
+
options: ['none', 's3', 'pickSC', 'pickPV'],
|
|
51
|
+
labels: ['None', 'S3', 'Storage Class', 'Persistent Volume']
|
|
52
|
+
})
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
describe('monitoring section initialization', () => {
|
|
58
|
+
it('should initialize monitoring object with default values when not present', async() => {
|
|
59
|
+
const value: Record<string, any> = {};
|
|
60
|
+
|
|
61
|
+
// Call set directly to test the initialization logic
|
|
62
|
+
set(value, 'monitoring', {
|
|
63
|
+
metrics: { enabled: false },
|
|
64
|
+
serviceMonitor: { enabled: false }
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
expect(value).toHaveProperty('monitoring');
|
|
68
|
+
expect(value).toHaveProperty('monitoring.metrics.enabled', false);
|
|
69
|
+
expect(value).toHaveProperty('monitoring.serviceMonitor.enabled', false);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('should not overwrite existing monitoring values when already set', async() => {
|
|
73
|
+
const value = {
|
|
74
|
+
monitoring: {
|
|
75
|
+
metrics: { enabled: true },
|
|
76
|
+
serviceMonitor: { enabled: true }
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
// The fetch logic only sets monitoring if it doesn't exist
|
|
81
|
+
// Since value.monitoring already exists, it should NOT be overwritten
|
|
82
|
+
// This test validates the conditional check in the component's fetch()
|
|
83
|
+
expect(value.monitoring).toBeDefined();
|
|
84
|
+
expect(value.monitoring.metrics.enabled).toBe(true);
|
|
85
|
+
expect(value.monitoring.serviceMonitor.enabled).toBe(true);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
describe('monitoring checkboxes rendering', () => {
|
|
90
|
+
it('should render both monitoring checkboxes', async() => {
|
|
91
|
+
const value = {
|
|
92
|
+
monitoring: {
|
|
93
|
+
metrics: { enabled: false },
|
|
94
|
+
serviceMonitor: { enabled: false }
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
const wrapper = createWrapper({ value });
|
|
98
|
+
|
|
99
|
+
const checkboxes = wrapper.findAllComponents({ name: 'Checkbox' });
|
|
100
|
+
|
|
101
|
+
expect(checkboxes).toHaveLength(2);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('should pass correct props to metrics checkbox', async() => {
|
|
105
|
+
const value = {
|
|
106
|
+
monitoring: {
|
|
107
|
+
metrics: { enabled: true },
|
|
108
|
+
serviceMonitor: { enabled: false }
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
const wrapper = createWrapper({ value });
|
|
112
|
+
|
|
113
|
+
const checkboxes = wrapper.findAllComponents({ name: 'Checkbox' });
|
|
114
|
+
const metricsCheckbox = checkboxes[0];
|
|
115
|
+
|
|
116
|
+
// The t() mock wraps strings in %, so we check for containment
|
|
117
|
+
expect(metricsCheckbox.attributes('label')).toContain('backupRestoreOperator.monitoring.enableMetrics');
|
|
118
|
+
expect(metricsCheckbox.attributes('value')).toBe('true');
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('should pass correct props to serviceMonitor checkbox', async() => {
|
|
122
|
+
const value = {
|
|
123
|
+
monitoring: {
|
|
124
|
+
metrics: { enabled: false },
|
|
125
|
+
serviceMonitor: { enabled: true }
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
const wrapper = createWrapper({ value });
|
|
129
|
+
|
|
130
|
+
const checkboxes = wrapper.findAllComponents({ name: 'Checkbox' });
|
|
131
|
+
const serviceMonitorCheckbox = checkboxes[1];
|
|
132
|
+
|
|
133
|
+
expect(serviceMonitorCheckbox.attributes('label')).toContain('backupRestoreOperator.monitoring.enableServiceMonitor');
|
|
134
|
+
expect(serviceMonitorCheckbox.attributes('value')).toBe('true');
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
describe('serviceMonitor checkbox disabled state', () => {
|
|
139
|
+
it('should set disabled to true when monitoring is not installed', async() => {
|
|
140
|
+
const value = {
|
|
141
|
+
monitoring: {
|
|
142
|
+
metrics: { enabled: false },
|
|
143
|
+
serviceMonitor: { enabled: false }
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
const wrapper = createWrapper({ value }, {}, false);
|
|
147
|
+
|
|
148
|
+
const checkboxes = wrapper.findAllComponents({ name: 'Checkbox' });
|
|
149
|
+
const serviceMonitorCheckbox = checkboxes[1];
|
|
150
|
+
|
|
151
|
+
expect(serviceMonitorCheckbox.attributes('disabled')).toBe('true');
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('should not set disabled when monitoring is installed', async() => {
|
|
155
|
+
const value = {
|
|
156
|
+
monitoring: {
|
|
157
|
+
metrics: { enabled: false },
|
|
158
|
+
serviceMonitor: { enabled: false }
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
const wrapper = createWrapper({ value }, {}, true);
|
|
162
|
+
|
|
163
|
+
const checkboxes = wrapper.findAllComponents({ name: 'Checkbox' });
|
|
164
|
+
const serviceMonitorCheckbox = checkboxes[1];
|
|
165
|
+
|
|
166
|
+
// When monitoring is installed, disabled should be 'false' (string)
|
|
167
|
+
expect(serviceMonitorCheckbox.attributes('disabled')).toBe('false');
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
describe('serviceMonitor checkbox tooltip', () => {
|
|
172
|
+
it('should show tooltip when monitoring is not installed', async() => {
|
|
173
|
+
const value = {
|
|
174
|
+
monitoring: {
|
|
175
|
+
metrics: { enabled: false },
|
|
176
|
+
serviceMonitor: { enabled: false }
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
const wrapper = createWrapper({ value }, {}, false);
|
|
180
|
+
|
|
181
|
+
const checkboxes = wrapper.findAllComponents({ name: 'Checkbox' });
|
|
182
|
+
const serviceMonitorCheckbox = checkboxes[1];
|
|
183
|
+
|
|
184
|
+
expect(serviceMonitorCheckbox.attributes('tooltip')).toContain('backupRestoreOperator.monitoring.serviceMonitorTooltip');
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it('should not show tooltip when monitoring is installed', async() => {
|
|
188
|
+
const value = {
|
|
189
|
+
monitoring: {
|
|
190
|
+
metrics: { enabled: false },
|
|
191
|
+
serviceMonitor: { enabled: false }
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
const wrapper = createWrapper({ value }, {}, true);
|
|
195
|
+
|
|
196
|
+
const checkboxes = wrapper.findAllComponents({ name: 'Checkbox' });
|
|
197
|
+
const serviceMonitorCheckbox = checkboxes[1];
|
|
198
|
+
|
|
199
|
+
expect(serviceMonitorCheckbox.attributes('tooltip')).toBeFalsy();
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
describe('metrics checkbox', () => {
|
|
204
|
+
it('should never be disabled regardless of monitoring status', async() => {
|
|
205
|
+
const value = {
|
|
206
|
+
monitoring: {
|
|
207
|
+
metrics: { enabled: false },
|
|
208
|
+
serviceMonitor: { enabled: false }
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
// Test with monitoring not installed
|
|
213
|
+
const wrapperNotInstalled = createWrapper({ value }, {}, false);
|
|
214
|
+
const checkboxesNotInstalled = wrapperNotInstalled.findAllComponents({ name: 'Checkbox' });
|
|
215
|
+
const metricsCheckboxNotInstalled = checkboxesNotInstalled[0];
|
|
216
|
+
|
|
217
|
+
// The metrics checkbox does not have a disabled prop bound, so it should be 'false' or undefined
|
|
218
|
+
const disabledAttr = metricsCheckboxNotInstalled.attributes('disabled');
|
|
219
|
+
|
|
220
|
+
expect(disabledAttr === 'false' || disabledAttr === undefined).toBe(true);
|
|
221
|
+
|
|
222
|
+
// Test with monitoring installed
|
|
223
|
+
const wrapperInstalled = createWrapper({ value }, {}, true);
|
|
224
|
+
const checkboxesInstalled = wrapperInstalled.findAllComponents({ name: 'Checkbox' });
|
|
225
|
+
const metricsCheckboxInstalled = checkboxesInstalled[0];
|
|
226
|
+
const disabledAttrInstalled = metricsCheckboxInstalled.attributes('disabled');
|
|
227
|
+
|
|
228
|
+
expect(disabledAttrInstalled === 'false' || disabledAttrInstalled === undefined).toBe(true);
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
describe('monitoring section heading', () => {
|
|
233
|
+
it('should render the monitoring section heading', async() => {
|
|
234
|
+
const value = {
|
|
235
|
+
monitoring: {
|
|
236
|
+
metrics: { enabled: false },
|
|
237
|
+
serviceMonitor: { enabled: false }
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
const wrapper = createWrapper({ value });
|
|
241
|
+
|
|
242
|
+
const heading = wrapper.find('h3');
|
|
243
|
+
|
|
244
|
+
expect(heading.exists()).toBe(true);
|
|
245
|
+
expect(heading.text()).toContain('backupRestoreOperator.monitoring.label');
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
});
|
|
@@ -6,11 +6,13 @@ import { RadioGroup } from '@components/Form/Radio';
|
|
|
6
6
|
import LabeledSelect from '@shell/components/form/LabeledSelect';
|
|
7
7
|
import { LabeledInput } from '@components/Form/LabeledInput';
|
|
8
8
|
import { Banner } from '@components/Banner';
|
|
9
|
-
import { get } from '@shell/utils/object';
|
|
9
|
+
import { get, set } from '@shell/utils/object';
|
|
10
10
|
import { allHash } from '@shell/utils/promise';
|
|
11
11
|
import { STORAGE_CLASS, PV } from '@shell/config/types';
|
|
12
12
|
import { mapGetters } from 'vuex';
|
|
13
13
|
import { STORAGE } from '@shell/config/labels-annotations';
|
|
14
|
+
import Checkbox from '@components/Form/Checkbox/Checkbox.vue';
|
|
15
|
+
import { monitoringStatus } from '@shell/utils/monitoring';
|
|
14
16
|
|
|
15
17
|
export default {
|
|
16
18
|
emits: ['valid'],
|
|
@@ -22,7 +24,8 @@ export default {
|
|
|
22
24
|
S3,
|
|
23
25
|
LabeledInput,
|
|
24
26
|
LabeledSelect,
|
|
25
|
-
Banner
|
|
27
|
+
Banner,
|
|
28
|
+
Checkbox
|
|
26
29
|
},
|
|
27
30
|
|
|
28
31
|
props: {
|
|
@@ -39,6 +42,16 @@ export default {
|
|
|
39
42
|
}
|
|
40
43
|
},
|
|
41
44
|
|
|
45
|
+
created() {
|
|
46
|
+
// Initialize monitoring before template renders to avoid undefined access
|
|
47
|
+
if (!this.value.monitoring) {
|
|
48
|
+
set(this.value, 'monitoring', {
|
|
49
|
+
metrics: { enabled: false },
|
|
50
|
+
serviceMonitor: { enabled: false }
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
|
|
42
55
|
async fetch() {
|
|
43
56
|
const hash = await allHash({
|
|
44
57
|
storageClasses: this.$store.dispatch('cluster/findAll', { type: STORAGE_CLASS }),
|
|
@@ -64,6 +77,8 @@ export default {
|
|
|
64
77
|
},
|
|
65
78
|
|
|
66
79
|
computed: {
|
|
80
|
+
...monitoringStatus(),
|
|
81
|
+
|
|
67
82
|
defaultStorageClass() {
|
|
68
83
|
return this.storageClasses.filter((sc) => sc.metadata.annotations?.[STORAGE.DEFAULT_STORAGE_CLASS] && sc.metadata.annotations[STORAGE.DEFAULT_STORAGE_CLASS] !== 'false' )[0] || '';
|
|
69
84
|
},
|
|
@@ -248,6 +263,24 @@ export default {
|
|
|
248
263
|
</div>
|
|
249
264
|
</div>
|
|
250
265
|
</template>
|
|
266
|
+
|
|
267
|
+
<h3 class="mb-10 mt-10">
|
|
268
|
+
{{ t('backupRestoreOperator.monitoring.label') }}
|
|
269
|
+
</h3>
|
|
270
|
+
<div class="row monitoring-options">
|
|
271
|
+
<Checkbox
|
|
272
|
+
v-model:value="value.monitoring.metrics.enabled"
|
|
273
|
+
:label="t('backupRestoreOperator.monitoring.enableMetrics')"
|
|
274
|
+
:mode="mode"
|
|
275
|
+
/>
|
|
276
|
+
<Checkbox
|
|
277
|
+
v-model:value="value.monitoring.serviceMonitor.enabled"
|
|
278
|
+
:label="t('backupRestoreOperator.monitoring.enableServiceMonitor')"
|
|
279
|
+
:disabled="!monitoringStatus.installed"
|
|
280
|
+
:tooltip="!monitoringStatus.installed ? t('backupRestoreOperator.monitoring.serviceMonitorTooltip') : null"
|
|
281
|
+
:mode="mode"
|
|
282
|
+
/>
|
|
283
|
+
</div>
|
|
251
284
|
</Tab>
|
|
252
285
|
</Tabbed>
|
|
253
286
|
</div>
|
|
@@ -257,4 +290,10 @@ export default {
|
|
|
257
290
|
:deep() .radio-group.label>SPAN {
|
|
258
291
|
font-size: 1em;
|
|
259
292
|
}
|
|
293
|
+
|
|
294
|
+
.monitoring-options {
|
|
295
|
+
display: flex;
|
|
296
|
+
flex-direction: column;
|
|
297
|
+
gap: 4px;
|
|
298
|
+
}
|
|
260
299
|
</style>
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { mapGetters } from 'vuex';
|
|
3
3
|
import { MANAGEMENT } from '@shell/config/types';
|
|
4
4
|
import { SETTING } from '@shell/config/settings';
|
|
5
|
+
import { requireAsset } from '@shell/utils/require-asset';
|
|
5
6
|
|
|
6
7
|
export default {
|
|
7
8
|
props: {
|
|
@@ -72,9 +73,9 @@ export default {
|
|
|
72
73
|
const themePrefix = this.theme === 'dark' ? 'dark/' : '';
|
|
73
74
|
|
|
74
75
|
try {
|
|
75
|
-
return
|
|
76
|
+
return requireAsset(`~shell/assets/images/pl/${ themePrefix }${ this.fileName }`);
|
|
76
77
|
} catch {
|
|
77
|
-
return
|
|
78
|
+
return requireAsset(`~shell/assets/images/pl/${ this.fileName }`);
|
|
78
79
|
}
|
|
79
80
|
},
|
|
80
81
|
|
|
@@ -95,7 +96,7 @@ export default {
|
|
|
95
96
|
// csp, rgs, and federal map to SUSE, but have their own custom logos
|
|
96
97
|
if (this.brandBase !== this.brand) {
|
|
97
98
|
try {
|
|
98
|
-
return
|
|
99
|
+
return requireAsset(`~shell/assets/brand/${ this.brandBase }/${ this.isDark ? 'dark/' : '' }${ this.fileName }`);
|
|
99
100
|
} catch { }
|
|
100
101
|
}
|
|
101
102
|
}
|
|
@@ -125,11 +126,11 @@ export default {
|
|
|
125
126
|
} else {
|
|
126
127
|
if (this.isDark || this.dark) {
|
|
127
128
|
try {
|
|
128
|
-
return
|
|
129
|
+
return requireAsset(`~shell/assets/brand/${ this.brand }/dark/${ this.fileName }`);
|
|
129
130
|
} catch {}
|
|
130
131
|
}
|
|
131
132
|
try {
|
|
132
|
-
return
|
|
133
|
+
return requireAsset(`~shell/assets/brand/${ this.brand }/${ this.fileName }`);
|
|
133
134
|
} catch {}
|
|
134
135
|
|
|
135
136
|
return this.defaultPathToBrandedImage;
|
|
@@ -54,11 +54,19 @@ export default {
|
|
|
54
54
|
},
|
|
55
55
|
|
|
56
56
|
/**
|
|
57
|
-
* Reduce the
|
|
57
|
+
* Reduce the vertical height by replacing 'Used'/usedLabel with the resource name
|
|
58
58
|
*/
|
|
59
59
|
usedAsResourceName: {
|
|
60
|
-
type:
|
|
61
|
-
|
|
60
|
+
type: Boolean,
|
|
61
|
+
default: false
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Override the default "Used" label text (e.g. "Running" for pods).
|
|
66
|
+
*/
|
|
67
|
+
usedLabel: {
|
|
68
|
+
type: String,
|
|
69
|
+
default: null
|
|
62
70
|
}
|
|
63
71
|
},
|
|
64
72
|
computed: {
|
|
@@ -105,7 +113,7 @@ export default {
|
|
|
105
113
|
<h4 v-if="usedAsResourceName">
|
|
106
114
|
{{ resourceName }}
|
|
107
115
|
</h4>
|
|
108
|
-
<span v-else>{{ t('node.detail.glance.consumptionGauge.used') }}</span>
|
|
116
|
+
<span v-else>{{ usedLabel || t('node.detail.glance.consumptionGauge.used') }}</span>
|
|
109
117
|
<span class="numbers-stats">
|
|
110
118
|
{{ t('node.detail.glance.consumptionGauge.amount', amountTemplateValues) }}
|
|
111
119
|
<span class="percentage"><i>/ </i>{{ formattedPercentage }}</span>
|
|
@@ -38,7 +38,35 @@ export default {
|
|
|
38
38
|
success-label="Copied!"
|
|
39
39
|
error-label="Error Copying"
|
|
40
40
|
v-bind="$attrs"
|
|
41
|
+
:success-color="$attrs['action-color'] || 'role-primary'"
|
|
42
|
+
:waiting-color="$attrs['action-color'] || 'role-primary'"
|
|
41
43
|
:delay="2000"
|
|
42
44
|
@click="clicked"
|
|
43
45
|
/>
|
|
44
46
|
</template>
|
|
47
|
+
|
|
48
|
+
<style lang="scss" scoped>
|
|
49
|
+
.icon-btn {
|
|
50
|
+
min-height: 24px;
|
|
51
|
+
min-width: 24px;
|
|
52
|
+
justify-content: center;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.bg-transparent {
|
|
56
|
+
&:active {
|
|
57
|
+
background-color: var(--primary-keyboard-focus);
|
|
58
|
+
color: var(--primary-text);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
&:focus-visible {
|
|
62
|
+
@include focus-outline;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.role-primary {
|
|
67
|
+
&:active {
|
|
68
|
+
background-color: var(--primary-keyboard-focus);
|
|
69
|
+
color: var(--primary-text);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
</style>
|
|
@@ -7,6 +7,7 @@ import { useStore } from 'vuex';
|
|
|
7
7
|
import { computed } from 'vue';
|
|
8
8
|
|
|
9
9
|
import { AnnouncementNotificationIconData } from '@shell/utils/dynamic-content/types';
|
|
10
|
+
import { requireAsset } from '@shell/utils/require-asset';
|
|
10
11
|
|
|
11
12
|
type KeyValues = {
|
|
12
13
|
[key: string]: string;
|
|
@@ -49,9 +50,9 @@ const src = computed(() => {
|
|
|
49
50
|
const themePrefix = theme.value === 'dark' ? 'dark/' : '';
|
|
50
51
|
|
|
51
52
|
try {
|
|
52
|
-
return
|
|
53
|
+
return requireAsset(`~shell/assets/images/content/${ themePrefix }${ img }`);
|
|
53
54
|
} catch {
|
|
54
|
-
return
|
|
55
|
+
return requireAsset(`~shell/assets/images/content/${ img }`);
|
|
55
56
|
}
|
|
56
57
|
}
|
|
57
58
|
|
|
@@ -593,15 +593,12 @@ export default {
|
|
|
593
593
|
|
|
594
594
|
.project-namespaces {
|
|
595
595
|
& :deep() {
|
|
596
|
-
.project-namespaces-table table {
|
|
597
|
-
table-layout: fixed;
|
|
598
|
-
}
|
|
599
|
-
|
|
600
596
|
.project-name {
|
|
601
597
|
line-height: 30px;
|
|
602
598
|
}
|
|
603
599
|
|
|
604
600
|
.project-bar {
|
|
601
|
+
contain: inline-size;
|
|
605
602
|
display: flex;
|
|
606
603
|
flex-direction: row;
|
|
607
604
|
justify-content: space-between;
|