@rancher/shell 3.0.5-rc.7 → 3.0.5-rc.8
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/brand/classic/metadata.json +3 -0
- package/assets/styles/app.scss +1 -0
- package/assets/styles/base/_color.scss +16 -0
- package/assets/styles/base/_helpers.scss +10 -0
- package/assets/styles/base/_variables.scss +1 -1
- package/assets/styles/fonts/_icons.scss +1 -32
- package/assets/styles/global/_layout.scss +1 -1
- package/assets/styles/themes/_dark.scss +262 -260
- package/assets/styles/themes/_light.scss +538 -515
- package/assets/styles/themes/_modern.scss +914 -0
- package/assets/translations/en-us.yaml +84 -25
- package/chart/__tests__/S3.test.ts +2 -1
- package/cloud-credential/generic.vue +18 -10
- package/cloud-credential/harvester.vue +1 -9
- package/components/AdvancedSection.vue +8 -0
- package/components/ChartReadme.vue +17 -7
- package/components/Drawer/ResourceDetailDrawer/__tests__/composables.test.ts +1 -26
- package/components/Drawer/ResourceDetailDrawer/composables.ts +0 -23
- package/components/Drawer/ResourceDetailDrawer/index.vue +17 -4
- package/components/InstallHelmCharts.vue +656 -0
- package/components/LazyImage.vue +60 -4
- package/components/LocaleSelector.vue +7 -2
- package/components/Markdown.vue +4 -0
- package/components/Resource/Detail/Masthead/composable.ts +16 -0
- package/components/Resource/Detail/Masthead/index.vue +37 -0
- package/components/Resource/Detail/Metadata/IdentifyingInformation/identifying-fields.ts +5 -5
- package/components/Resource/Detail/Metadata/__tests__/composables.test.ts +10 -17
- package/components/Resource/Detail/Metadata/composables.ts +9 -7
- package/components/Resource/Detail/Metadata/index.vue +17 -2
- package/components/Resource/Detail/Page.vue +35 -21
- package/components/Resource/Detail/TitleBar/__tests__/composables.test.ts +8 -9
- package/components/Resource/Detail/TitleBar/composables.ts +2 -3
- package/components/Resource/Detail/TitleBar/index.vue +10 -1
- package/components/ResourceDetail/index.vue +569 -74
- package/components/SlideInPanelManager.vue +10 -3
- package/components/SortableTable/index.vue +4 -4
- package/components/Tabbed/index.vue +29 -3
- package/components/__tests__/LazyImage.spec.ts +121 -0
- package/components/fleet/FleetStatus.vue +4 -0
- package/components/form/ClusterAppearance.vue +5 -0
- package/components/form/Members/ClusterPermissionsEditor.vue +1 -1
- package/components/form/ProjectMemberEditor.vue +1 -1
- package/components/form/ResourceLabeledSelect.vue +19 -6
- package/components/form/ResourceTabs/index.vue +20 -0
- package/components/form/SecretSelector.vue +9 -0
- package/components/form/labeled-select-utils/labeled-select-pagination.ts +3 -38
- package/components/formatter/FleetApplicationSource.vue +25 -17
- package/components/nav/Favorite.vue +4 -0
- package/components/nav/NotificationCenter/Notification.vue +1 -27
- package/components/nav/WindowManager/index.vue +3 -3
- package/config/labels-annotations.js +1 -2
- package/detail/__tests__/provisioning.cattle.io.cluster.test.ts +11 -0
- package/detail/__tests__/workload.test.ts +164 -0
- package/detail/configmap.vue +33 -75
- package/detail/projectsecret.vue +11 -0
- package/detail/provisioning.cattle.io.cluster.vue +350 -324
- package/detail/secret.vue +49 -308
- package/detail/workload/index.vue +38 -21
- package/dialog/InstallExtensionDialog.vue +8 -5
- package/edit/__tests__/fleet.cattle.io.helmop.test.ts +224 -0
- package/edit/fleet.cattle.io.gitrepo.vue +5 -6
- package/edit/fleet.cattle.io.helmop.vue +78 -56
- package/edit/logging.banzaicloud.io.output/index.vue +1 -1
- package/edit/logging.banzaicloud.io.output/providers/awsElasticsearch.vue +5 -6
- package/edit/networking.k8s.io.ingress/Certificate.vue +9 -11
- package/edit/networking.k8s.io.ingress/DefaultBackend.vue +8 -3
- package/edit/networking.k8s.io.ingress/Rule.vue +2 -5
- package/edit/networking.k8s.io.ingress/RulePath.vue +17 -11
- package/edit/networking.k8s.io.networkpolicy/PolicyRuleTarget.vue +11 -10
- package/edit/networking.k8s.io.networkpolicy/PolicyRules.vue +1 -3
- package/edit/networking.k8s.io.networkpolicy/index.vue +17 -17
- package/edit/provisioning.cattle.io.cluster/rke2.vue +21 -13
- package/edit/provisioning.cattle.io.cluster/tabs/AgentConfiguration.vue +9 -7
- package/edit/provisioning.cattle.io.cluster/tabs/DirectoryConfig.vue +10 -12
- package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +39 -38
- package/edit/provisioning.cattle.io.cluster/tabs/etcd/S3Config.vue +41 -19
- package/edit/provisioning.cattle.io.cluster/tabs/etcd/index.vue +16 -3
- package/edit/provisioning.cattle.io.cluster/tabs/registries/RegistryConfigs.vue +30 -31
- package/edit/provisioning.cattle.io.cluster/tabs/registries/RegistryMirrors.vue +9 -10
- package/edit/provisioning.cattle.io.cluster/tabs/registries/index.vue +1 -3
- package/edit/provisioning.cattle.io.cluster/tabs/upgrade/DrainOptions.vue +16 -9
- package/edit/workload/index.vue +5 -14
- package/list/provisioning.cattle.io.cluster.vue +1 -69
- package/machine-config/__tests__/vmwarevsphere.test.ts +5 -7
- package/machine-config/google.vue +9 -1
- package/machine-config/vmwarevsphere.vue +7 -17
- package/mixins/chart.js +0 -2
- package/mixins/resource-fetch-api-pagination.js +3 -4
- package/models/__tests__/chart.test.ts +111 -80
- package/models/__tests__/fleet.cattle.io.helmop.test.ts +224 -0
- package/models/__tests__/node.test.ts +7 -63
- package/models/catalog.cattle.io.app.js +1 -1
- package/models/catalog.cattle.io.operation.js +1 -1
- package/models/chart.js +36 -20
- package/models/cloudcredential.js +2 -163
- package/models/cluster/node.js +7 -7
- package/models/cluster.x-k8s.io.machine.js +3 -3
- package/models/compliance.cattle.io.clusterscan.js +2 -2
- package/models/configmap.js +4 -0
- package/models/constraints.gatekeeper.sh.constraint.js +1 -1
- package/models/fleet-application.js +0 -17
- package/models/fleet.cattle.io.gitrepo.js +15 -1
- package/models/fleet.cattle.io.helmop.js +26 -22
- package/models/management.cattle.io.setting.js +4 -0
- package/models/persistentvolumeclaim.js +1 -1
- package/models/pod.js +2 -2
- package/models/provisioning.cattle.io.cluster.js +16 -40
- package/models/rke.cattle.io.etcdsnapshot.js +1 -1
- package/models/secret.js +4 -0
- package/models/storage.k8s.io.storageclass.js +2 -2
- package/models/workload.js +3 -3
- package/package.json +11 -10
- package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +1 -0
- package/pages/c/_cluster/apps/charts/AppChartCardSubHeader.vue +4 -1
- package/pages/c/_cluster/apps/charts/__tests__/AppChartCardFooter.spec.js +41 -0
- package/pages/c/_cluster/apps/charts/chart.vue +422 -174
- package/pages/c/_cluster/apps/charts/install.vue +1 -1
- package/pages/c/_cluster/explorer/projectsecret.vue +3 -13
- package/pages/c/_cluster/fleet/__tests__/index.test.ts +608 -314
- package/pages/c/_cluster/fleet/index.vue +103 -44
- package/pages/c/_cluster/manager/cloudCredential/index.vue +2 -59
- package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +10 -3
- package/pages/c/_cluster/uiplugins/index.vue +36 -25
- package/plugins/dashboard-store/actions.js +42 -22
- package/plugins/dashboard-store/resource-class.js +31 -0
- package/plugins/steve/__tests__/getters.test.ts +1 -1
- package/plugins/steve/__tests__/subscribe.spec.ts +259 -1
- package/plugins/steve/getters.js +8 -2
- package/plugins/steve/resourceWatcher.js +10 -3
- package/plugins/steve/subscribe.js +192 -19
- package/plugins/steve/worker/web-worker.advanced.js +2 -0
- package/rancher-components/Card/Card.vue +0 -18
- package/rancher-components/Pill/RcStatusBadge/RcStatusBadge.test.ts +15 -0
- package/rancher-components/Pill/RcStatusBadge/RcStatusBadge.vue +65 -0
- package/rancher-components/Pill/RcStatusBadge/index.ts +2 -0
- package/rancher-components/Pill/RcStatusBadge/types.ts +5 -0
- package/rancher-components/Pill/RcStatusIndicator/RcStatusIndicator.test.ts +33 -0
- package/rancher-components/Pill/RcStatusIndicator/RcStatusIndicator.vue +75 -0
- package/rancher-components/Pill/RcStatusIndicator/index.ts +2 -0
- package/rancher-components/Pill/RcStatusIndicator/types.ts +7 -0
- package/rancher-components/Pill/types.ts +2 -0
- package/rancher-components/RcButton/RcButton.vue +1 -1
- package/rancher-components/RcDropdown/RcDropdown.test.ts +98 -0
- package/rancher-components/RcDropdown/RcDropdown.vue +5 -0
- package/rancher-components/RcDropdown/RcDropdownItem.vue +7 -1
- package/rancher-components/RcDropdown/RcDropdownItemCheckbox.vue +2 -1
- package/rancher-components/RcDropdown/RcDropdownItemSelect.vue +2 -1
- package/rancher-components/RcDropdown/useDropdownContext.ts +21 -0
- package/rancher-components/RcDropdown/useDropdownItem.ts +30 -1
- package/rancher-components/RcItemCard/RcItemCard.test.ts +20 -0
- package/rancher-components/RcItemCard/RcItemCard.vue +40 -6
- package/store/__tests__/catalog.test.ts +93 -1
- package/store/aws.js +19 -8
- package/store/catalog.js +8 -3
- package/types/resources/settings.d.ts +1 -1
- package/types/shell/index.d.ts +28 -28
- package/types/uiplugins.ts +73 -0
- package/utils/__tests__/back-off.test.ts +354 -0
- package/utils/__tests__/kontainer.test.ts +19 -0
- package/utils/__tests__/uiplugins.test.ts +84 -0
- package/utils/back-off.ts +176 -0
- package/utils/dynamic-importer.js +8 -0
- package/utils/kontainer.ts +3 -5
- package/utils/style.ts +3 -0
- package/utils/uiplugins.ts +29 -2
- package/utils/validators/__tests__/setting.test.js +92 -0
- package/utils/validators/formRules/__tests__/index.test.ts +88 -7
- package/utils/validators/formRules/index.ts +83 -8
- package/utils/validators/setting.js +17 -0
- package/cloud-credential/__tests__/harvester.test.ts +0 -18
- package/components/ResourceDetail/__tests__/index.test.ts +0 -135
- package/components/ResourceDetail/legacy.vue +0 -562
- package/components/formatter/CloudCredExpired.vue +0 -69
- package/pages/explorer/resource/detail/configmap.vue +0 -42
- package/pages/explorer/resource/detail/projectsecret.vue +0 -9
- package/pages/explorer/resource/detail/secret.vue +0 -63
- package/utils/aws.js +0 -0
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import HelmOp from '@shell/models/fleet.cattle.io.helmop.js';
|
|
2
|
+
|
|
3
|
+
describe('class HelmOp', () => {
|
|
4
|
+
let instance;
|
|
5
|
+
|
|
6
|
+
describe('source getter', () => {
|
|
7
|
+
it('should return correct source for SOURCE_TYPE.REPO (HTTPS)', () => {
|
|
8
|
+
instance = new HelmOp({
|
|
9
|
+
spec: {
|
|
10
|
+
helm: {
|
|
11
|
+
repo: 'https://charts.rancher.io/fleet',
|
|
12
|
+
chart: 'fleet-agent'
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
const source = instance.source;
|
|
18
|
+
|
|
19
|
+
expect(source.value).toBe('https://charts.rancher.io/fleet');
|
|
20
|
+
expect(source.display).toBe('charts.rancher.io/fleet');
|
|
21
|
+
expect(source.icon).toBe('icon icon-application');
|
|
22
|
+
expect(source.showLink).toBe(true);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('should return correct source for SOURCE_TYPE.REPO (GitHub HTTPS .git)', () => {
|
|
26
|
+
instance = new HelmOp({
|
|
27
|
+
spec: {
|
|
28
|
+
helm: {
|
|
29
|
+
repo: 'https://github.com/rancher/fleet.git',
|
|
30
|
+
chart: 'fleet'
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const source = instance.source;
|
|
36
|
+
|
|
37
|
+
expect(source.value).toBe('https://github.com/rancher/fleet.git');
|
|
38
|
+
expect(source.display).toBe('rancher/fleet');
|
|
39
|
+
expect(source.icon).toBe('icon icon-application');
|
|
40
|
+
expect(source.showLink).toBe(true);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('should return correct source for SOURCE_TYPE.REPO (GitHub SSH)', () => {
|
|
44
|
+
instance = new HelmOp({
|
|
45
|
+
spec: {
|
|
46
|
+
helm: {
|
|
47
|
+
repo: 'git@github.com:rancher/fleet.git',
|
|
48
|
+
chart: 'fleet'
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const source = instance.source;
|
|
54
|
+
|
|
55
|
+
expect(source.value).toBe('https://github.com/rancher/fleet');
|
|
56
|
+
expect(source.display).toBe('rancher/fleet');
|
|
57
|
+
expect(source.icon).toBe('icon icon-application');
|
|
58
|
+
expect(source.showLink).toBe(true);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('should return correct source for SOURCE_TYPE.OCI', () => {
|
|
62
|
+
instance = new HelmOp({ spec: { helm: { repo: 'oci://ghcr.io/rancher/some-chart' } } });
|
|
63
|
+
|
|
64
|
+
const source = instance.source;
|
|
65
|
+
|
|
66
|
+
expect(source.value).toBe('oci://ghcr.io');
|
|
67
|
+
expect(source.display).toBe('oci://ghcr.io');
|
|
68
|
+
expect(source.icon).toBe('icon icon-application');
|
|
69
|
+
expect(source.showLink).toBe(false);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('should return correct source for SOURCE_TYPE.TARBALL', () => {
|
|
73
|
+
instance = new HelmOp({ spec: { helm: { chart: 'https://github.com/rancher/fleet-helm-charts/releases/download/fleet-0.12.1-beta.2/fleet-0.12.1-beta.2.tgz' } } });
|
|
74
|
+
|
|
75
|
+
const source = instance.source;
|
|
76
|
+
|
|
77
|
+
expect(source.value).toBe('https://github.com/rancher/fleet-helm-charts/releases/download/fleet-0.12.1-beta.2/fleet-0.12.1-beta.2.tgz');
|
|
78
|
+
expect(source.display).toBe('rancher/fleet-helm-charts/releases/download/fleet-0.12.1-beta.2/fleet-0.12.1-beta.2.tgz');
|
|
79
|
+
expect(source.icon).toBe('icon icon-application');
|
|
80
|
+
expect(source.showLink).toBe(true);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('should handle missing helm spec gracefully', () => {
|
|
84
|
+
instance = new HelmOp({ spec: {} });
|
|
85
|
+
const source = instance.source;
|
|
86
|
+
|
|
87
|
+
expect(source.value).toBe('');
|
|
88
|
+
expect(source.display).toBeNull();
|
|
89
|
+
expect(source.icon).toBe('icon icon-application');
|
|
90
|
+
expect(source.showLink).toBe(false);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
describe('sourceSub getter', () => {
|
|
95
|
+
it('should display chart name and desired version when both are present (SOURCE_TYPE.REPO)', () => {
|
|
96
|
+
instance = new HelmOp({
|
|
97
|
+
spec: {
|
|
98
|
+
helm: {
|
|
99
|
+
repo: 'https://charts.rancher.io/fleet',
|
|
100
|
+
chart: 'fleet-agent',
|
|
101
|
+
version: '0.12.x'
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
const sourceSub = instance.sourceSub;
|
|
107
|
+
|
|
108
|
+
expect(sourceSub.value).toBe('fleet-agent : 0.12.x');
|
|
109
|
+
expect(sourceSub.display).toBe('fleet-agent : 0.12.x');
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('should display chart name and desired version when both are present (SOURCE_TYPE.OCI)', () => {
|
|
113
|
+
instance = new HelmOp({
|
|
114
|
+
spec: {
|
|
115
|
+
helm: {
|
|
116
|
+
repo: 'oci://ghcr.io/rancher/some-chart',
|
|
117
|
+
version: '1.0.0'
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
const sourceSub = instance.sourceSub;
|
|
123
|
+
|
|
124
|
+
expect(sourceSub.value).toBe('rancher/some-chart : 1.0.0');
|
|
125
|
+
expect(sourceSub.display).toBe('rancher/some-chart : 1.0.0');
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('should display only installed version when only it is present', () => {
|
|
129
|
+
instance = new HelmOp({
|
|
130
|
+
status: { version: '0.12.3' },
|
|
131
|
+
spec: { helm: {} }
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
const sourceSub = instance.sourceSub;
|
|
135
|
+
|
|
136
|
+
expect(sourceSub.value).toBe('0.12.3');
|
|
137
|
+
expect(sourceSub.display).toBe('0.12.3');
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('should display semantic version when installed version is missing', () => {
|
|
141
|
+
instance = new HelmOp({
|
|
142
|
+
spec: {
|
|
143
|
+
helm: {
|
|
144
|
+
version: '0.12.x',
|
|
145
|
+
chart: 'test-chart'
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
const sourceSub = instance.sourceSub;
|
|
151
|
+
|
|
152
|
+
expect(sourceSub.value).toBe('0.12.x');
|
|
153
|
+
expect(sourceSub.display).toBe('0.12.x');
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it('should display "semantic -> installed" when both versions are present (no chart)', () => {
|
|
157
|
+
instance = new HelmOp({
|
|
158
|
+
spec: { helm: { version: '0.12.x' } },
|
|
159
|
+
status: { version: '0.12.5' }
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
const sourceSub = instance.sourceSub;
|
|
163
|
+
|
|
164
|
+
expect(sourceSub.value).toBe('0.12.x -> 0.12.5');
|
|
165
|
+
expect(sourceSub.display).toBe('0.12.x -> 0.12.5');
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('should display chart and "semantic -> installed" when all are present', () => {
|
|
169
|
+
instance = new HelmOp({
|
|
170
|
+
spec: {
|
|
171
|
+
helm: {
|
|
172
|
+
repo: 'https://charts.rancher.io/fleet',
|
|
173
|
+
chart: 'fleet-agent',
|
|
174
|
+
version: '0.12.x'
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
status: { version: '0.12.5' }
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
const sourceSub = instance.sourceSub;
|
|
181
|
+
|
|
182
|
+
expect(sourceSub.value).toBe('fleet-agent : 0.12.x -> 0.12.5');
|
|
183
|
+
expect(sourceSub.display).toBe('fleet-agent : 0.12.x -> 0.12.5');
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it('should display chart and only semantic version when all are present but semantic version is equal to installed version (no duplicate info)', () => {
|
|
187
|
+
instance = new HelmOp({
|
|
188
|
+
spec: {
|
|
189
|
+
helm: {
|
|
190
|
+
repo: 'https://charts.rancher.io/fleet',
|
|
191
|
+
chart: 'fleet-agent',
|
|
192
|
+
version: '0.12.3'
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
status: { version: '0.12.3' }
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
const sourceSub = instance.sourceSub;
|
|
199
|
+
|
|
200
|
+
expect(sourceSub.value).toBe('fleet-agent : 0.12.3');
|
|
201
|
+
expect(sourceSub.display).toBe('fleet-agent : 0.12.3');
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it('should return empty string when no version or chart information is available', () => {
|
|
205
|
+
instance = new HelmOp({
|
|
206
|
+
spec: { helm: {} },
|
|
207
|
+
status: {}
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
const sourceSub = instance.sourceSub;
|
|
211
|
+
|
|
212
|
+
expect(sourceSub.value).toBe('');
|
|
213
|
+
expect(sourceSub.display).toBe('');
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it('should correctly handle missing helm spec', () => {
|
|
217
|
+
instance = new HelmOp({ spec: {}, status: {} });
|
|
218
|
+
const sourceSub = instance.sourceSub;
|
|
219
|
+
|
|
220
|
+
expect(sourceSub.value).toBe('');
|
|
221
|
+
expect(sourceSub.display).toBe('');
|
|
222
|
+
});
|
|
223
|
+
});
|
|
224
|
+
});
|
|
@@ -1,74 +1,18 @@
|
|
|
1
|
-
import Node from '@shell/models/
|
|
1
|
+
import Node from '@shell/models/cluster/node';
|
|
2
2
|
|
|
3
3
|
describe('class Node', () => {
|
|
4
|
-
const foo = 'foo';
|
|
5
|
-
const bar = 'bar';
|
|
6
|
-
const t = jest.fn(() => bar);
|
|
7
|
-
const ctx = { rootGetters: { 'i18n/t': t } };
|
|
8
|
-
|
|
9
4
|
const resetMocks = () => {
|
|
10
5
|
// Clear all mock function calls:
|
|
11
6
|
jest.clearAllMocks();
|
|
12
7
|
};
|
|
13
8
|
|
|
14
|
-
it(
|
|
15
|
-
|
|
9
|
+
it.each([
|
|
10
|
+
['1200', 1200],
|
|
11
|
+
['1k', 1000]
|
|
12
|
+
])('given %p status pod capacity value from the backend, should parse the value correctly as %p', (value, result) => {
|
|
13
|
+
const node = new Node({ status: { capacity: { pods: value } } });
|
|
16
14
|
|
|
17
|
-
expect(node.
|
|
15
|
+
expect(node.podCapacity).toStrictEqual(result);
|
|
18
16
|
resetMocks();
|
|
19
17
|
});
|
|
20
|
-
|
|
21
|
-
describe('should return addresses', () => {
|
|
22
|
-
const addresses = [foo];
|
|
23
|
-
|
|
24
|
-
it('if they are present directly on the resource status', () => {
|
|
25
|
-
const node = new Node({ status: { addresses } });
|
|
26
|
-
|
|
27
|
-
expect(node.addresses).toStrictEqual(addresses);
|
|
28
|
-
});
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
describe('should return an internalIp', () => {
|
|
32
|
-
const addresses = [{ type: 'InternalIP', address: foo }];
|
|
33
|
-
|
|
34
|
-
it('if addresses includes an object with an appropriate type and address', () => {
|
|
35
|
-
const node = new Node({ status: { addresses } });
|
|
36
|
-
|
|
37
|
-
expect(node.internalIp).toStrictEqual(foo);
|
|
38
|
-
});
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
describe('should return an externalIp', () => {
|
|
42
|
-
const addresses = [{ type: 'ExternalIP', address: foo }];
|
|
43
|
-
|
|
44
|
-
it('if addresses includes an object with an appropriate type and address', () => {
|
|
45
|
-
const node = new Node({ status: { addresses } });
|
|
46
|
-
|
|
47
|
-
expect(node.externalIp).toStrictEqual(foo);
|
|
48
|
-
});
|
|
49
|
-
it('if internalNodeStatus.addresses includes an object with an appropriate type and address', () => {
|
|
50
|
-
const node = new Node({ status: { internalNodeStatus: { addresses } } });
|
|
51
|
-
|
|
52
|
-
expect(node.externalIp).toStrictEqual(foo);
|
|
53
|
-
});
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
describe('should return an appropriate message', () => {
|
|
57
|
-
it('if there is no internalIp to display', () => {
|
|
58
|
-
const node = new Node({ status: {} }, ctx);
|
|
59
|
-
|
|
60
|
-
expect(node.internalIp).toStrictEqual(bar);
|
|
61
|
-
expect(t).toHaveBeenCalledTimes(1);
|
|
62
|
-
expect(t).toHaveBeenCalledWith('generic.none');
|
|
63
|
-
resetMocks();
|
|
64
|
-
});
|
|
65
|
-
it('if there is no externalIp to display', () => {
|
|
66
|
-
const node = new Node({ status: {} }, ctx);
|
|
67
|
-
|
|
68
|
-
expect(node.externalIp).toStrictEqual(bar);
|
|
69
|
-
expect(t).toHaveBeenCalledTimes(1);
|
|
70
|
-
expect(t).toHaveBeenCalledWith('generic.none');
|
|
71
|
-
resetMocks();
|
|
72
|
-
});
|
|
73
|
-
});
|
|
74
18
|
});
|
package/models/chart.js
CHANGED
|
@@ -4,7 +4,7 @@ import {
|
|
|
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';
|
|
7
|
-
import { CATALOG } from '@shell/config/types';
|
|
7
|
+
import { CATALOG, ZERO_TIME } from '@shell/config/types';
|
|
8
8
|
import { CATALOG as CATALOG_ANNOTATIONS } from '@shell/config/labels-annotations';
|
|
9
9
|
import day from 'dayjs';
|
|
10
10
|
|
|
@@ -121,42 +121,58 @@ export default class Chart extends SteveModel {
|
|
|
121
121
|
*/
|
|
122
122
|
get cardContent() {
|
|
123
123
|
if (!this._cardContent) {
|
|
124
|
-
const
|
|
125
|
-
|
|
124
|
+
const latestVersion = this.versions?.[0] || {};
|
|
125
|
+
const subHeaderItems = [];
|
|
126
|
+
|
|
127
|
+
if (latestVersion) {
|
|
128
|
+
const hasZeroTime = latestVersion.created === ZERO_TIME;
|
|
129
|
+
|
|
130
|
+
subHeaderItems.push({
|
|
126
131
|
icon: 'icon-version-alt',
|
|
127
132
|
iconTooltip: { key: 'tableHeaders.version' },
|
|
128
|
-
label:
|
|
129
|
-
}
|
|
130
|
-
|
|
133
|
+
label: latestVersion.version
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
const lastUpdatedItem = {
|
|
131
137
|
icon: 'icon-refresh-alt',
|
|
132
138
|
iconTooltip: { key: 'tableHeaders.lastUpdated' },
|
|
133
|
-
label: day(
|
|
139
|
+
label: hasZeroTime ? this.t('generic.na') : day(latestVersion.created).format('MMM D, YYYY')
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
if (hasZeroTime) {
|
|
143
|
+
lastUpdatedItem.labelTooltip = this.t('catalog.charts.appChartCard.subHeaderItem.missingVersionDate');
|
|
134
144
|
}
|
|
135
|
-
|
|
145
|
+
|
|
146
|
+
subHeaderItems.push(lastUpdatedItem);
|
|
147
|
+
}
|
|
148
|
+
|
|
136
149
|
const footerItems = [
|
|
137
150
|
{
|
|
138
|
-
type:
|
|
139
|
-
icon:
|
|
140
|
-
iconTooltip:
|
|
141
|
-
labels:
|
|
151
|
+
type: REPO,
|
|
152
|
+
icon: 'icon-repository-alt',
|
|
153
|
+
iconTooltip: { key: 'tableHeaders.repoName' },
|
|
154
|
+
labels: [this.repoNameDisplay],
|
|
155
|
+
labelTooltip: this.t('catalog.charts.findSimilar.message', { type: this.t('catalog.charts.findSimilar.types.repo') }, true)
|
|
142
156
|
}
|
|
143
157
|
];
|
|
144
158
|
|
|
145
159
|
if (this.categories.length) {
|
|
146
160
|
footerItems.push( {
|
|
147
|
-
type:
|
|
148
|
-
icon:
|
|
149
|
-
iconTooltip:
|
|
150
|
-
labels:
|
|
161
|
+
type: CATEGORY,
|
|
162
|
+
icon: 'icon-category-alt',
|
|
163
|
+
iconTooltip: { key: 'generic.category' },
|
|
164
|
+
labels: this.categories,
|
|
165
|
+
labelTooltip: this.t('catalog.charts.findSimilar.message', { type: this.t('catalog.charts.findSimilar.types.category') }, true)
|
|
151
166
|
});
|
|
152
167
|
}
|
|
153
168
|
|
|
154
169
|
if (this.tags.length) {
|
|
155
170
|
footerItems.push({
|
|
156
|
-
type:
|
|
157
|
-
icon:
|
|
158
|
-
iconTooltip:
|
|
159
|
-
labels:
|
|
171
|
+
type: TAG,
|
|
172
|
+
icon: 'icon-tag-alt',
|
|
173
|
+
iconTooltip: { key: 'generic.tags' },
|
|
174
|
+
labels: this.tags,
|
|
175
|
+
labelTooltip: this.t('catalog.charts.findSimilar.message', { type: this.t('catalog.charts.findSimilar.types.tag') }, true)
|
|
160
176
|
});
|
|
161
177
|
}
|
|
162
178
|
|
|
@@ -1,65 +1,11 @@
|
|
|
1
|
-
import { CAPI
|
|
1
|
+
import { CAPI } from '@shell/config/labels-annotations';
|
|
2
2
|
import { fullFields, prefixFields, simplify, suffixFields } from '@shell/store/plugins';
|
|
3
3
|
import { isEmpty, set } from '@shell/utils/object';
|
|
4
|
-
import {
|
|
4
|
+
import { SECRET } from '@shell/config/types';
|
|
5
5
|
import { escapeHtml } from '@shell/utils/string';
|
|
6
6
|
import NormanModel from '@shell/plugins/steve/norman-class';
|
|
7
|
-
import { DATE_FORMAT, TIME_FORMAT } from '@shell/store/prefs';
|
|
8
|
-
import day from 'dayjs';
|
|
9
|
-
|
|
10
|
-
const harvesterProvider = 'harvester';
|
|
11
|
-
|
|
12
|
-
const renew = {
|
|
13
|
-
[harvesterProvider]: {
|
|
14
|
-
renew: ({ cloudCredential, $ctx }) => {
|
|
15
|
-
return renew[harvesterProvider].renewBulk(
|
|
16
|
-
{ cloudCredentials: [cloudCredential], $ctx }
|
|
17
|
-
);
|
|
18
|
-
},
|
|
19
|
-
renewBulk: async({ cloudCredentials, $ctx }) => {
|
|
20
|
-
// A harvester cloud credential (at the moment) is a kubeconfig complete with expiring token
|
|
21
|
-
// So to renew we just need to generate a new kubeconfig and save it to the cc (similar to shell/cloud-credential/harvester.vue)
|
|
22
|
-
await Promise.all(cloudCredentials.map(async(cc) => {
|
|
23
|
-
try {
|
|
24
|
-
if (!cc.harvestercredentialConfig?.clusterId) {
|
|
25
|
-
throw new Error(`credential has no matching harvester cluster`);
|
|
26
|
-
}
|
|
27
|
-
const mgmtCluster = $ctx.rootGetters['management/byId'](MANAGEMENT.CLUSTER, cc.harvestercredentialConfig.clusterId);
|
|
28
|
-
|
|
29
|
-
if (!mgmtCluster) {
|
|
30
|
-
throw new Error(`cannot find harvester cluster`);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const kubeconfigContent = await mgmtCluster.generateKubeConfig();
|
|
34
|
-
|
|
35
|
-
cc.setData('kubeconfigContent', kubeconfigContent);
|
|
36
|
-
|
|
37
|
-
await cc.save();
|
|
38
|
-
} catch (error) {
|
|
39
|
-
console.error(`Unable to refresh harvester cloud credential '${ cc.id }'`, error); // eslint-disable-line no-console
|
|
40
|
-
}
|
|
41
|
-
}));
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
};
|
|
45
7
|
|
|
46
8
|
export default class CloudCredential extends NormanModel {
|
|
47
|
-
get _availableActions() {
|
|
48
|
-
const out = super._availableActions;
|
|
49
|
-
|
|
50
|
-
out.splice(0, 0, { divider: true });
|
|
51
|
-
out.splice(0, 0, {
|
|
52
|
-
action: 'renew',
|
|
53
|
-
enabled: this.canRenew,
|
|
54
|
-
bulkable: this.canBulkRenew,
|
|
55
|
-
bulkAction: 'renewBulk',
|
|
56
|
-
icon: 'icon icon-fw icon-refresh',
|
|
57
|
-
label: this.t('manager.cloudCredentials.renew'),
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
return out;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
9
|
get hasSensitiveData() {
|
|
64
10
|
return true;
|
|
65
11
|
}
|
|
@@ -231,111 +177,4 @@ export default class CloudCredential extends NormanModel {
|
|
|
231
177
|
get doneRoute() {
|
|
232
178
|
return 'c-cluster-manager-secret';
|
|
233
179
|
}
|
|
234
|
-
|
|
235
|
-
get canRenew() {
|
|
236
|
-
return !!renew[this.provider]?.renew && this.expires !== undefined && this.canUpdate;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
get canBulkRenew() {
|
|
240
|
-
return !!renew[this.provider]?.renewBulk;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
get expiresForSort() {
|
|
244
|
-
// Why not just `expires`? Ensures the correct sort order of expired --> expiring --> never expires
|
|
245
|
-
// (instead of 'never expired' --> 'expired' --> 'expiring')
|
|
246
|
-
return this.expires !== undefined ? this.expires : Number.MAX_SAFE_INTEGER;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
get expires() {
|
|
250
|
-
const expires = this.annotations[CLOUD_CREDENTIALS.EXPIRATION];
|
|
251
|
-
|
|
252
|
-
if (typeof expires === 'string') {
|
|
253
|
-
return parseInt(expires);
|
|
254
|
-
} else if (typeof expires === 'number') {
|
|
255
|
-
return expires;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
return undefined; // Weird things happen if this isn't a number
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
get expireData() {
|
|
262
|
-
if (typeof this.expiresIn !== 'number') {
|
|
263
|
-
return null;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
const sevenDays = 1000 * 60 * 60 * 24 * 7;
|
|
267
|
-
|
|
268
|
-
if (this.expiresIn === 0) {
|
|
269
|
-
return {
|
|
270
|
-
expired: true,
|
|
271
|
-
expiring: false,
|
|
272
|
-
};
|
|
273
|
-
} else if (this.expiresIn < sevenDays) {
|
|
274
|
-
return {
|
|
275
|
-
expired: false,
|
|
276
|
-
expiring: true,
|
|
277
|
-
};
|
|
278
|
-
} else if (this.expiresIn) {
|
|
279
|
-
return {
|
|
280
|
-
expired: false,
|
|
281
|
-
expiring: false,
|
|
282
|
-
};
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
return null;
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
get expiresString() {
|
|
289
|
-
if (this.expires === undefined) {
|
|
290
|
-
return '';
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
if (this.expireData.expired) {
|
|
294
|
-
return this.t('manager.cloudCredentials.expired');
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
const dateFormat = escapeHtml( this.$rootGetters['prefs/get'](DATE_FORMAT));
|
|
298
|
-
const timeFormat = escapeHtml( this.$rootGetters['prefs/get'](TIME_FORMAT));
|
|
299
|
-
|
|
300
|
-
return day(this.expires).format(`${ dateFormat } ${ timeFormat }`);
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
get expiresIn() {
|
|
304
|
-
if (this.expires === undefined) {
|
|
305
|
-
return null;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
const timeThen = this.expires;
|
|
309
|
-
const timeNow = Date.now();
|
|
310
|
-
|
|
311
|
-
const expiresIn = timeThen - timeNow;
|
|
312
|
-
|
|
313
|
-
return expiresIn < 0 ? 0 : expiresIn;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
renew() {
|
|
317
|
-
const renewFn = renew[this.provider]?.renew;
|
|
318
|
-
|
|
319
|
-
if (!renewFn) {
|
|
320
|
-
console.error('No fn renew function for ', this.provider); // eslint-disable-line no-console
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
return renewFn({
|
|
324
|
-
cloudCredential: this,
|
|
325
|
-
$ctx: this.$ctx
|
|
326
|
-
});
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
async renewBulk(cloudCredentials = []) {
|
|
330
|
-
const renewBulkFn = renew[this.provider]?.renewBulk;
|
|
331
|
-
|
|
332
|
-
if (!renewBulkFn) {
|
|
333
|
-
console.error('No fn renew bulk function for ', this.provider); // eslint-disable-line no-console
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
return renewBulkFn({
|
|
337
|
-
cloudCredentials,
|
|
338
|
-
$ctx: this.$ctx
|
|
339
|
-
});
|
|
340
|
-
}
|
|
341
180
|
}
|
package/models/cluster/node.js
CHANGED
|
@@ -16,7 +16,7 @@ export default class ClusterNode extends SteveModel {
|
|
|
16
16
|
const cordon = {
|
|
17
17
|
action: 'cordon',
|
|
18
18
|
enabled: !!normanAction.cordon,
|
|
19
|
-
icon: 'icon icon-
|
|
19
|
+
icon: 'icon icon-pause',
|
|
20
20
|
label: 'Cordon',
|
|
21
21
|
total: 1,
|
|
22
22
|
bulkable: true
|
|
@@ -25,7 +25,7 @@ export default class ClusterNode extends SteveModel {
|
|
|
25
25
|
const uncordon = {
|
|
26
26
|
action: 'uncordon',
|
|
27
27
|
enabled: !!normanAction.uncordon,
|
|
28
|
-
icon: 'icon icon-
|
|
28
|
+
icon: 'icon icon-play',
|
|
29
29
|
label: 'Uncordon',
|
|
30
30
|
total: 1,
|
|
31
31
|
bulkable: true
|
|
@@ -34,7 +34,7 @@ export default class ClusterNode extends SteveModel {
|
|
|
34
34
|
const drain = {
|
|
35
35
|
action: 'drain',
|
|
36
36
|
enabled: !!normanAction.drain,
|
|
37
|
-
icon: 'icon icon-
|
|
37
|
+
icon: 'icon icon-dot-open',
|
|
38
38
|
label: this.t('drainNode.action'),
|
|
39
39
|
bulkable: true,
|
|
40
40
|
bulkAction: 'drain'
|
|
@@ -43,7 +43,7 @@ export default class ClusterNode extends SteveModel {
|
|
|
43
43
|
const stopDrain = {
|
|
44
44
|
action: 'stopDrain',
|
|
45
45
|
enabled: !!normanAction.stopDrain,
|
|
46
|
-
icon: 'icon icon-
|
|
46
|
+
icon: 'icon icon-x',
|
|
47
47
|
label: this.t('drainNode.actionStop'),
|
|
48
48
|
bulkable: true,
|
|
49
49
|
};
|
|
@@ -51,14 +51,14 @@ export default class ClusterNode extends SteveModel {
|
|
|
51
51
|
const openSsh = {
|
|
52
52
|
action: 'openSsh',
|
|
53
53
|
enabled: !!this.provisionedMachine?.links?.shell,
|
|
54
|
-
icon: 'icon icon-
|
|
54
|
+
icon: 'icon icon-chevron-right',
|
|
55
55
|
label: 'SSH Shell',
|
|
56
56
|
};
|
|
57
57
|
|
|
58
58
|
const downloadKeys = {
|
|
59
59
|
action: 'downloadKeys',
|
|
60
60
|
enabled: !!this.provisionedMachine?.links?.sshkeys,
|
|
61
|
-
icon: 'icon icon-
|
|
61
|
+
icon: 'icon icon-download',
|
|
62
62
|
label: this.t('node.actions.downloadSSHKey'),
|
|
63
63
|
};
|
|
64
64
|
|
|
@@ -227,7 +227,7 @@ export default class ClusterNode extends SteveModel {
|
|
|
227
227
|
}
|
|
228
228
|
|
|
229
229
|
get podCapacity() {
|
|
230
|
-
return
|
|
230
|
+
return parseSi(this.status.capacity?.pods);
|
|
231
231
|
}
|
|
232
232
|
|
|
233
233
|
get podConsumed() {
|
|
@@ -60,13 +60,13 @@ export default class CapiMachine extends SteveModel {
|
|
|
60
60
|
const openSsh = {
|
|
61
61
|
action: 'openSsh',
|
|
62
62
|
enabled: !!this.links.shell && this.isRunning,
|
|
63
|
-
icon: 'icon icon-
|
|
63
|
+
icon: 'icon icon-chevron-right',
|
|
64
64
|
label: 'SSH Shell',
|
|
65
65
|
};
|
|
66
66
|
const downloadKeys = {
|
|
67
67
|
action: 'downloadKeys',
|
|
68
68
|
enabled: !!this.links.sshkeys,
|
|
69
|
-
icon: 'icon icon-
|
|
69
|
+
icon: 'icon icon-download',
|
|
70
70
|
label: this.t('node.actions.downloadSSHKey'),
|
|
71
71
|
};
|
|
72
72
|
const forceRemove = {
|
|
@@ -80,7 +80,7 @@ export default class CapiMachine extends SteveModel {
|
|
|
80
80
|
action: 'toggleScaleDownModal',
|
|
81
81
|
bulkAction: 'toggleScaleDownModal',
|
|
82
82
|
enabled: !!this.canScaleDown,
|
|
83
|
-
icon: 'icon icon-minus
|
|
83
|
+
icon: 'icon icon-minus',
|
|
84
84
|
label: this.t('node.actions.scaleDown'),
|
|
85
85
|
bulkable: true
|
|
86
86
|
};
|