@rancher/shell 3.0.5-rc.2 → 3.0.5-rc.3
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/data/aws-regions.json +2 -0
- package/assets/styles/global/_layout.scss +0 -1
- package/assets/translations/en-us.yaml +61 -19
- package/assets/translations/zh-hans.yaml +0 -10
- package/chart/monitoring/index.vue +1 -1
- package/components/AsyncButton.vue +2 -0
- package/components/CodeMirror.vue +3 -3
- package/components/CruResource.vue +103 -15
- package/components/ExplorerProjectsNamespaces.vue +7 -2
- package/components/FixedBanner.vue +19 -5
- package/components/PaginatedResourceTable.vue +7 -0
- package/components/ResourceDetail/Masthead.vue +0 -1
- package/components/ResourceList/index.vue +2 -1
- package/components/SlideInPanelManager.vue +1 -2
- package/components/SortableTable/selection.js +1 -1
- package/components/Tabbed/index.vue +6 -0
- package/components/__tests__/AsyncButton.test.ts +39 -0
- package/components/__tests__/CruResource.test.ts +63 -0
- package/components/__tests__/PromptModal.test.ts +0 -2
- package/components/form/ArrayList.vue +134 -118
- package/components/form/BannerSettings.vue +145 -96
- package/components/form/KeyValue.vue +10 -7
- package/components/form/LabeledSelect.vue +9 -2
- package/components/form/MatchExpressions.vue +5 -1
- package/components/form/NameNsDescription.vue +1 -1
- package/components/form/ResourceSelector.vue +26 -23
- package/components/form/ResourceTabs/index.vue +2 -1
- package/components/form/Select.vue +9 -2
- package/components/form/UnitInput.vue +13 -0
- package/components/form/__tests__/ArrayList.test.ts +32 -0
- package/components/form/__tests__/KeyValue.test.ts +36 -0
- package/components/form/__tests__/LabeledSelect.test.ts +33 -0
- package/components/form/__tests__/Select.test.ts +34 -1
- package/components/form/__tests__/UnitInput.test.ts +23 -1
- package/components/formatter/ClusterLink.vue +5 -8
- package/components/formatter/Description.vue +30 -0
- package/components/formatter/__tests__/ClusterLink.test.ts +2 -32
- package/components/nav/NamespaceFilter.vue +1 -1
- package/components/nav/WindowManager/index.vue +1 -0
- package/config/product/explorer.js +16 -13
- package/config/product/manager.js +1 -28
- package/config/settings.ts +11 -13
- package/config/table-headers.js +7 -5
- package/detail/catalog.cattle.io.app.vue +0 -1
- package/detail/provisioning.cattle.io.cluster.vue +13 -3
- package/detail/service.vue +0 -1
- package/detail/workload/index.vue +21 -34
- package/dialog/ExtensionCatalogUninstallDialog.vue +14 -8
- package/edit/__tests__/service.test.ts +2 -1
- package/edit/networking.k8s.io.networkpolicy/PolicyRule.vue +3 -14
- package/edit/networking.k8s.io.networkpolicy/PolicyRuleTarget.vue +57 -62
- package/edit/networking.k8s.io.networkpolicy/PolicyRules.vue +3 -14
- package/edit/networking.k8s.io.networkpolicy/__tests__/PolicyRuleTarget.test.ts +72 -41
- package/edit/networking.k8s.io.networkpolicy/__tests__/utils/mock.json +17 -1
- package/edit/networking.k8s.io.networkpolicy/index.vue +18 -30
- package/edit/provisioning.cattle.io.cluster/index.vue +21 -73
- package/edit/service.vue +13 -28
- package/list/workload.vue +6 -1
- package/mixins/resource-fetch-api-pagination.js +55 -43
- package/mixins/resource-fetch.js +14 -5
- package/models/__tests__/workload.test.ts +1 -0
- package/models/cluster/node.js +1 -0
- package/models/cluster.js +32 -2
- package/models/management.cattle.io.cluster.js +0 -20
- package/models/management.cattle.io.node.js +7 -22
- package/models/management.cattle.io.nodepool.js +12 -0
- package/models/namespace.js +5 -0
- package/models/provisioning.cattle.io.cluster.js +18 -64
- package/models/service.js +24 -9
- package/models/workload.js +70 -31
- package/package.json +1 -1
- package/pages/c/_cluster/apps/charts/install.vue +0 -1
- package/pages/c/_cluster/explorer/index.vue +11 -0
- package/pages/c/_cluster/longhorn/index.vue +2 -2
- package/pages/c/_cluster/settings/banners.vue +56 -2
- package/pages/c/_cluster/settings/performance.vue +7 -26
- package/pages/home.vue +11 -52
- package/plugins/clean-html.js +2 -0
- package/plugins/dashboard-store/__tests__/actions.test.ts +4 -1
- package/plugins/dashboard-store/actions.js +122 -21
- package/plugins/dashboard-store/getters.js +74 -3
- package/plugins/dashboard-store/mutations.js +10 -5
- package/plugins/dashboard-store/resource-class.js +23 -3
- package/plugins/steve/__tests__/getters.test.ts +18 -11
- package/plugins/steve/__tests__/steve-class.test.ts +1 -0
- package/plugins/steve/actions.js +34 -12
- package/plugins/steve/getters.js +39 -10
- package/plugins/steve/steve-class.js +5 -0
- package/plugins/steve/steve-pagination-utils.ts +199 -37
- package/plugins/steve/worker/web-worker.advanced.js +3 -1
- package/rancher-components/Banner/Banner.test.ts +51 -3
- package/rancher-components/Banner/Banner.vue +28 -6
- package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +5 -1
- package/rancher-components/Form/ToggleSwitch/ToggleSwitch.vue +21 -1
- package/store/features.js +0 -1
- package/store/type-map.utils.ts +45 -2
- package/types/fleet.d.ts +1 -1
- package/types/kube/kube-api.ts +22 -0
- package/types/resources/settings.d.ts +0 -4
- package/types/shell/index.d.ts +346 -289
- package/types/store/dashboard-store.types.ts +24 -1
- package/types/store/pagination.types.ts +19 -2
- package/utils/cluster.js +24 -20
- package/utils/grafana.js +1 -0
- package/utils/object.js +0 -12
- package/utils/pagination-utils.ts +6 -2
- package/utils/perf-setting.utils.ts +28 -0
- package/utils/selector-typed.ts +205 -0
- package/utils/selector.js +29 -6
- package/utils/uiplugins.ts +10 -6
- package/utils/v-sphere.ts +5 -1
- package/components/formatter/RKETemplateName.vue +0 -37
- package/dialog/SaveAsRKETemplateDialog.vue +0 -139
|
@@ -3,7 +3,19 @@ import { sortBy } from '@shell/utils/sort';
|
|
|
3
3
|
import HybridModel from '@shell/plugins/steve/hybrid-class';
|
|
4
4
|
import { notOnlyOfRole } from '@shell/models/cluster.x-k8s.io.machine';
|
|
5
5
|
|
|
6
|
+
const RKE1_ALLOWED_ACTIONS = [
|
|
7
|
+
'goToViewYaml',
|
|
8
|
+
'download',
|
|
9
|
+
'viewInApi'
|
|
10
|
+
];
|
|
11
|
+
|
|
6
12
|
export default class MgmtNodePool extends HybridModel {
|
|
13
|
+
get _availableActions() {
|
|
14
|
+
const out = super._availableActions;
|
|
15
|
+
|
|
16
|
+
return out.filter((a) => a.divider || RKE1_ALLOWED_ACTIONS.includes(a.action));
|
|
17
|
+
}
|
|
18
|
+
|
|
7
19
|
get nodeTemplateId() {
|
|
8
20
|
return (this.spec?.nodeTemplateName || '').replace(/:/, '/');
|
|
9
21
|
}
|
package/models/namespace.js
CHANGED
|
@@ -11,6 +11,11 @@ import { HARVESTER_NAME as HARVESTER } from '@shell/config/features';
|
|
|
11
11
|
import { hasPSALabels, getPSATooltipsDescription, getPSALabels } from '@shell/utils/pod-security-admission';
|
|
12
12
|
import { PSAIconsDisplay, PSALabelsNamespaceVersion } from '@shell/config/pod-security-admission';
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* obscure namespaces are reserved and have special meaning if they're also classed as `system`
|
|
16
|
+
*
|
|
17
|
+
* (by default hidden from user given by default `show dynamic namespaces` preference is disabled and `user namespaces` filter is on)
|
|
18
|
+
*/
|
|
14
19
|
const OBSCURE_NAMESPACE_PREFIX = [
|
|
15
20
|
'c-', // cluster namespace
|
|
16
21
|
|
|
@@ -12,6 +12,14 @@ import { HARVESTER_NAME as HARVESTER } from '@shell/config/features';
|
|
|
12
12
|
import { CAPI as CAPI_ANNOTATIONS, NODE_ARCHITECTURE } from '@shell/config/labels-annotations';
|
|
13
13
|
import { KEV1 } from '@shell/models/management.cattle.io.kontainerdriver';
|
|
14
14
|
|
|
15
|
+
const RKE1_ALLOWED_ACTIONS = [
|
|
16
|
+
'openShell',
|
|
17
|
+
'downloadKubeConfig',
|
|
18
|
+
'copyKubeConfig',
|
|
19
|
+
'download',
|
|
20
|
+
'viewInApi'
|
|
21
|
+
];
|
|
22
|
+
|
|
15
23
|
/**
|
|
16
24
|
* Class representing Cluster resource.
|
|
17
25
|
* @extends SteveModel
|
|
@@ -41,17 +49,6 @@ export default class ProvCluster extends SteveModel {
|
|
|
41
49
|
},
|
|
42
50
|
].filter((x) => !!x.content);
|
|
43
51
|
|
|
44
|
-
// RKE Template details
|
|
45
|
-
const rkeTemplate = this.rkeTemplate;
|
|
46
|
-
|
|
47
|
-
if (rkeTemplate) {
|
|
48
|
-
out.push({
|
|
49
|
-
label: this.t('cluster.detail.rkeTemplate'),
|
|
50
|
-
formatter: 'RKETemplateName',
|
|
51
|
-
content: rkeTemplate,
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
|
|
55
52
|
if (!this.machineProvider) {
|
|
56
53
|
out.splice(1, 1);
|
|
57
54
|
|
|
@@ -105,17 +102,6 @@ export default class ProvCluster extends SteveModel {
|
|
|
105
102
|
|
|
106
103
|
const canSnapshot = ready && ((this.isRke2 && this.canUpdate) || (this.isRke1 && this.mgmt?.hasAction('backupEtcd')));
|
|
107
104
|
|
|
108
|
-
const clusterTemplatesSchema = this.$getters['schemaFor']('management.cattle.io.clustertemplate');
|
|
109
|
-
let canUpdateClusterTemplate = false;
|
|
110
|
-
|
|
111
|
-
if (clusterTemplatesSchema && (clusterTemplatesSchema.resourceMethods?.includes('blocked-PUT') || clusterTemplatesSchema.resourceMethods?.includes('PUT'))) {
|
|
112
|
-
canUpdateClusterTemplate = true;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const normanClusterSaveTemplateAction = !!this.normanCluster?.actions?.saveAsTemplate;
|
|
116
|
-
|
|
117
|
-
const canSaveRKETemplate = this.isRke1 && this.mgmt?.status?.driver === 'rancherKubernetesEngine' && !this.mgmt?.spec?.clusterTemplateName && this.hasLink('update') && canUpdateClusterTemplate && normanClusterSaveTemplateAction;
|
|
118
|
-
|
|
119
105
|
const actions = [
|
|
120
106
|
// Note: Actions are not supported in the Steve API, so we check
|
|
121
107
|
// available actions for RKE1 clusters, but not RKE2 clusters.
|
|
@@ -158,12 +144,7 @@ export default class ProvCluster extends SteveModel {
|
|
|
158
144
|
action: 'rotateEncryptionKey',
|
|
159
145
|
label: this.$rootGetters['i18n/t']('nav.rotateEncryptionKeys'),
|
|
160
146
|
icon: 'icon icon-refresh',
|
|
161
|
-
enabled: canEditRKE2cluster
|
|
162
|
-
}, {
|
|
163
|
-
action: 'saveAsRKETemplate',
|
|
164
|
-
label: this.$rootGetters['i18n/t']('nav.saveAsRKETemplate'),
|
|
165
|
-
icon: 'icon icon-folder',
|
|
166
|
-
enabled: canSaveRKETemplate,
|
|
147
|
+
enabled: canEditRKE2cluster
|
|
167
148
|
}, { divider: true }];
|
|
168
149
|
|
|
169
150
|
// Harvester Cluster 1:1 Harvester Cloud Cred
|
|
@@ -190,6 +171,15 @@ export default class ProvCluster extends SteveModel {
|
|
|
190
171
|
}
|
|
191
172
|
}
|
|
192
173
|
|
|
174
|
+
// If RKE1, then remove most of the actions
|
|
175
|
+
if (this.isRke1) {
|
|
176
|
+
all.forEach((action) => {
|
|
177
|
+
if (!action.divider && !RKE1_ALLOWED_ACTIONS.includes(action.action)) {
|
|
178
|
+
action.enabled = false;
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
193
183
|
// If we have a helper that wants to modify the available actions, let it do it
|
|
194
184
|
if (this.customProvisionerHelper?.availableActions) {
|
|
195
185
|
// Provider can either modify the provided list or return one of its own
|
|
@@ -813,13 +803,6 @@ export default class ProvCluster extends SteveModel {
|
|
|
813
803
|
this.$dispatch('promptRestore', [resource]);
|
|
814
804
|
}
|
|
815
805
|
|
|
816
|
-
saveAsRKETemplate(cluster = this) {
|
|
817
|
-
this.$dispatch('promptModal', {
|
|
818
|
-
componentProps: { cluster },
|
|
819
|
-
component: 'SaveAsRKETemplateDialog'
|
|
820
|
-
});
|
|
821
|
-
}
|
|
822
|
-
|
|
823
806
|
rotateCertificates(cluster = this) {
|
|
824
807
|
this.$dispatch('promptModal', {
|
|
825
808
|
componentProps: { cluster },
|
|
@@ -861,40 +844,11 @@ export default class ProvCluster extends SteveModel {
|
|
|
861
844
|
|
|
862
845
|
return {
|
|
863
846
|
displayName: `${ template.spec?.displayName }/${ revision.spec?.displayName }`,
|
|
864
|
-
upgrade: this.rkeTemplateUpgrade,
|
|
865
847
|
template,
|
|
866
848
|
revision,
|
|
867
849
|
};
|
|
868
850
|
}
|
|
869
851
|
|
|
870
|
-
get rkeTemplateUpgrade() {
|
|
871
|
-
if (!this.isRke1 || !this.mgmt) {
|
|
872
|
-
// Not an RKE! cluster or no management cluster available
|
|
873
|
-
return false;
|
|
874
|
-
}
|
|
875
|
-
|
|
876
|
-
if (!this.mgmt.spec?.clusterTemplateRevisionName) {
|
|
877
|
-
// Cluster does not use an RKE template
|
|
878
|
-
return false;
|
|
879
|
-
}
|
|
880
|
-
|
|
881
|
-
const clusterTemplateRevisionName = this.mgmt.spec.clusterTemplateRevisionName.replace(':', '/');
|
|
882
|
-
|
|
883
|
-
// Get all of the template revisions for this template
|
|
884
|
-
const revisions = this.$rootGetters['management/all'](MANAGEMENT.RKE_TEMPLATE_REVISION).filter((t) => t.spec.enabled && t.spec.clusterTemplateName === this.mgmt.spec.clusterTemplateName);
|
|
885
|
-
|
|
886
|
-
if (revisions.length <= 1) {
|
|
887
|
-
// Only one template revision
|
|
888
|
-
return false;
|
|
889
|
-
}
|
|
890
|
-
|
|
891
|
-
revisions.sort((a, b) => {
|
|
892
|
-
return parseInt(a.metadata.resourceVersion, 10) - parseInt(b.metadata.resourceVersion, 10);
|
|
893
|
-
}).reverse();
|
|
894
|
-
|
|
895
|
-
return revisions[0].id !== clusterTemplateRevisionName ? revisions[0].spec?.displayName : false;
|
|
896
|
-
}
|
|
897
|
-
|
|
898
852
|
get _stateObj() {
|
|
899
853
|
if (!this.isRke2) {
|
|
900
854
|
return this.mgmt?.stateObj || this.metadata?.state;
|
package/models/service.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import find from 'lodash/find';
|
|
2
2
|
import { POD } from '@shell/config/types';
|
|
3
3
|
import SteveModel from '@shell/plugins/steve/steve-class';
|
|
4
|
+
import { parse } from '@shell/utils/selector';
|
|
4
5
|
|
|
5
6
|
// i18n-uses servicesPage.serviceTypes.clusterIp.*, servicesPage.serviceTypes.externalName.*, servicesPage.serviceTypes.headless.*
|
|
6
7
|
// i18n-uses servicesPage.serviceTypes.loadBalancer.*, servicesPage.serviceTypes.nodePort.*
|
|
@@ -49,7 +50,7 @@ export const CLUSTERIP = (() => {
|
|
|
49
50
|
return clusterIp.id;
|
|
50
51
|
})();
|
|
51
52
|
|
|
52
|
-
export default class extends SteveModel {
|
|
53
|
+
export default class Service extends SteveModel {
|
|
53
54
|
get customValidationRules() {
|
|
54
55
|
return [
|
|
55
56
|
{
|
|
@@ -137,18 +138,32 @@ export default class extends SteveModel {
|
|
|
137
138
|
}
|
|
138
139
|
|
|
139
140
|
async fetchPods() {
|
|
140
|
-
if (this.podRelationship) {
|
|
141
|
-
//
|
|
142
|
-
|
|
143
|
-
type: POD,
|
|
144
|
-
selector: this.podRelationship.selector,
|
|
145
|
-
namespace: this.namespace
|
|
146
|
-
}, { root: true });
|
|
141
|
+
if (!this.podRelationship) {
|
|
142
|
+
// If empty or not present, the service is assumed to have an external process managing its endpoints
|
|
143
|
+
return [];
|
|
147
144
|
}
|
|
145
|
+
|
|
146
|
+
return await this.$dispatch('findLabelSelector', {
|
|
147
|
+
type: POD,
|
|
148
|
+
matching: {
|
|
149
|
+
namespace: this.metadata.namespace,
|
|
150
|
+
labelSelector: { matchExpressions: parse(this.podRelationship?.selector) },
|
|
151
|
+
}
|
|
152
|
+
});
|
|
148
153
|
}
|
|
149
154
|
|
|
155
|
+
/**
|
|
156
|
+
* This getter expects a superset of workload pods to have been fetched already
|
|
157
|
+
*
|
|
158
|
+
* It assumes fetchPods has been called and should be used instead of the response of fetchPods
|
|
159
|
+
* (findAll --> findLabelSelector world results won't trigger change detection)
|
|
160
|
+
*/
|
|
150
161
|
get pods() {
|
|
151
|
-
|
|
162
|
+
if (this.podRelationship?.selector) {
|
|
163
|
+
return this.$getters['matchingLabelSelector'](POD, { matchExpressions: parse(this.podRelationship?.selector) }, this.metadata.namespace);
|
|
164
|
+
} else {
|
|
165
|
+
return [];
|
|
166
|
+
}
|
|
152
167
|
}
|
|
153
168
|
|
|
154
169
|
get serviceType() {
|
package/models/workload.js
CHANGED
|
@@ -3,9 +3,10 @@ import { TIMESTAMP, CATTLE_PUBLIC_ENDPOINTS } from '@shell/config/labels-annotat
|
|
|
3
3
|
import { WORKLOAD_TYPES, SERVICE, POD } from '@shell/config/types';
|
|
4
4
|
import { get, set } from '@shell/utils/object';
|
|
5
5
|
import day from 'dayjs';
|
|
6
|
-
import { convertSelectorObj,
|
|
6
|
+
import { convertSelectorObj, parse } from '@shell/utils/selector';
|
|
7
7
|
import { SEPARATOR } from '@shell/config/workload';
|
|
8
8
|
import WorkloadService from '@shell/models/workload.service';
|
|
9
|
+
import { matching } from '@shell/utils/selector-typed';
|
|
9
10
|
|
|
10
11
|
export const defaultContainer = {
|
|
11
12
|
imagePullPolicy: 'Always',
|
|
@@ -18,6 +19,7 @@ export const defaultContainer = {
|
|
|
18
19
|
},
|
|
19
20
|
volumeMounts: []
|
|
20
21
|
};
|
|
22
|
+
|
|
21
23
|
export default class Workload extends WorkloadService {
|
|
22
24
|
// remove clone as yaml/edit as yaml until API supported
|
|
23
25
|
get _availableActions() {
|
|
@@ -205,22 +207,20 @@ export default class Workload extends WorkloadService {
|
|
|
205
207
|
return this.goToEdit({ sidecar: true });
|
|
206
208
|
}
|
|
207
209
|
|
|
208
|
-
get showPodRestarts() {
|
|
209
|
-
return true;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
210
|
get restartCount() {
|
|
213
|
-
|
|
211
|
+
return this.pods.reduce((total, pod) => {
|
|
212
|
+
const { status:{ containerStatuses = [] } } = pod;
|
|
214
213
|
|
|
215
|
-
|
|
214
|
+
if (containerStatuses.length) {
|
|
215
|
+
total += containerStatuses.reduce((tot, container) => {
|
|
216
|
+
tot += container.restartCount || 0;
|
|
216
217
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
sum += pod.status?.containerStatuses[0].restartCount || 0;
|
|
218
|
+
return tot;
|
|
219
|
+
}, 0);
|
|
220
220
|
}
|
|
221
|
-
});
|
|
222
221
|
|
|
223
|
-
|
|
222
|
+
return total;
|
|
223
|
+
}, 0);
|
|
224
224
|
}
|
|
225
225
|
|
|
226
226
|
get hasSidecars() {
|
|
@@ -334,6 +334,10 @@ export default class Workload extends WorkloadService {
|
|
|
334
334
|
const type = this._type ? this._type : this.type;
|
|
335
335
|
|
|
336
336
|
const detailItem = {
|
|
337
|
+
restarts: {
|
|
338
|
+
label: this.t('resourceDetail.masthead.restartCount'),
|
|
339
|
+
content: this.restartCount
|
|
340
|
+
},
|
|
337
341
|
endpoint: {
|
|
338
342
|
label: 'Endpoints',
|
|
339
343
|
content: this.endpoint,
|
|
@@ -400,10 +404,13 @@ export default class Workload extends WorkloadService {
|
|
|
400
404
|
});
|
|
401
405
|
}
|
|
402
406
|
|
|
403
|
-
out.push(
|
|
407
|
+
out.push({
|
|
404
408
|
label: 'Image',
|
|
405
409
|
content: this.imageNames,
|
|
406
410
|
formatter: 'PodImages'
|
|
411
|
+
}, {
|
|
412
|
+
label: detailItem.restarts.label,
|
|
413
|
+
content: detailItem.restarts.content,
|
|
407
414
|
});
|
|
408
415
|
|
|
409
416
|
switch (type) {
|
|
@@ -550,30 +557,56 @@ export default class Workload extends WorkloadService {
|
|
|
550
557
|
}
|
|
551
558
|
}
|
|
552
559
|
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
560
|
+
async fetchPods() {
|
|
561
|
+
if (this.podMatchExpression) {
|
|
562
|
+
return this.$dispatch('findLabelSelector', {
|
|
563
|
+
type: POD,
|
|
564
|
+
matching: {
|
|
565
|
+
namespace: this.metadata.namespace,
|
|
566
|
+
labelSelector: { matchExpressions: this.podMatchExpression },
|
|
567
|
+
},
|
|
568
|
+
});
|
|
569
|
+
}
|
|
556
570
|
|
|
557
|
-
|
|
558
|
-
|
|
571
|
+
return undefined;
|
|
572
|
+
}
|
|
559
573
|
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
574
|
+
/**
|
|
575
|
+
* This getter expects a superset of workload pods to have been fetched already
|
|
576
|
+
*
|
|
577
|
+
* It assumes fetchPods has been called and should be used instead of the response of fetchPods
|
|
578
|
+
* (findAll --> findLabelSelector world results won't trigger change detection)
|
|
579
|
+
*/
|
|
580
|
+
get pods() {
|
|
581
|
+
if (this.podMatchExpression) {
|
|
582
|
+
return this.$getters['matchingLabelSelector'](POD, { matchExpressions: this.podMatchExpression }, this.metadata.namespace);
|
|
564
583
|
} else {
|
|
565
584
|
return [];
|
|
566
585
|
}
|
|
567
586
|
}
|
|
568
587
|
|
|
569
|
-
|
|
588
|
+
/**
|
|
589
|
+
* Return a string version of a matchLabel expression
|
|
590
|
+
*/
|
|
591
|
+
get podSelector() {
|
|
592
|
+
const relationships = this.metadata?.relationships || [];
|
|
593
|
+
const selector = relationships.filter((relationship) => relationship.toType === POD)[0]?.selector;
|
|
594
|
+
|
|
595
|
+
return selector;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
get podMatchExpression() {
|
|
599
|
+
return this.podSelector ? parse(this.podSelector) : null;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
calcPodGauges(pods) {
|
|
570
603
|
const out = { };
|
|
571
604
|
|
|
572
|
-
if (!
|
|
605
|
+
if (!pods) {
|
|
573
606
|
return out;
|
|
574
607
|
}
|
|
575
608
|
|
|
576
|
-
|
|
609
|
+
pods.map((pod) => {
|
|
577
610
|
const { stateColor, stateDisplay } = pod;
|
|
578
611
|
|
|
579
612
|
if (out[stateDisplay]) {
|
|
@@ -589,6 +622,10 @@ export default class Workload extends WorkloadService {
|
|
|
589
622
|
return out;
|
|
590
623
|
}
|
|
591
624
|
|
|
625
|
+
get podGauges() {
|
|
626
|
+
return this.calcPodGauges(this.pods);
|
|
627
|
+
}
|
|
628
|
+
|
|
592
629
|
// Job Specific
|
|
593
630
|
get jobRelationships() {
|
|
594
631
|
if (this.type !== WORKLOAD_TYPES.CRON_JOB) {
|
|
@@ -664,13 +701,15 @@ export default class Workload extends WorkloadService {
|
|
|
664
701
|
}
|
|
665
702
|
|
|
666
703
|
async matchingPods() {
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
704
|
+
const matchInfo = await matching({
|
|
705
|
+
labelSelector: { matchExpressions: convertSelectorObj(this.spec.selector) },
|
|
706
|
+
type: POD,
|
|
707
|
+
$store: this.$store || { getters: this.$rootGetters, dispatch: (action, args) => this.$dispatch(action.split('/')[1], args) },
|
|
708
|
+
inStore: this.$rootGetters['currentProduct'].inStore,
|
|
709
|
+
namespace: this.metadata.namespace,
|
|
710
|
+
});
|
|
672
711
|
|
|
673
|
-
return
|
|
712
|
+
return matchInfo.matches;
|
|
674
713
|
}
|
|
675
714
|
|
|
676
715
|
cleanForSave(data) {
|
package/package.json
CHANGED
|
@@ -513,6 +513,13 @@ export default {
|
|
|
513
513
|
},
|
|
514
514
|
|
|
515
515
|
getAgentStatus(resources, opt = { checkDisconnected: false }) {
|
|
516
|
+
// If there is a single resource that is null, then there was an error fetching the status, which
|
|
517
|
+
// is most likely because the user does not have permission, so we should hide the agent status
|
|
518
|
+
// as we can not determine what it is
|
|
519
|
+
if (resources.length === 1 && resources[0] === null) {
|
|
520
|
+
return { state: STATES_ENUM.OFF };
|
|
521
|
+
}
|
|
522
|
+
|
|
516
523
|
if (resources.find((resource) => resource === 'loading')) {
|
|
517
524
|
return { state: STATES_ENUM.IN_PROGRESS };
|
|
518
525
|
}
|
|
@@ -1000,5 +1007,9 @@ export default {
|
|
|
1000
1007
|
color: var(--success)
|
|
1001
1008
|
}
|
|
1002
1009
|
}
|
|
1010
|
+
|
|
1011
|
+
&.off {
|
|
1012
|
+
display: none;
|
|
1013
|
+
}
|
|
1003
1014
|
}
|
|
1004
1015
|
</style>
|
|
@@ -12,9 +12,9 @@ export default {
|
|
|
12
12
|
|
|
13
13
|
async fetch() {
|
|
14
14
|
if ( this.$store.getters['cluster/schemaFor'](SERVICE) ) {
|
|
15
|
-
this.uiServices = await this.$store.dispatch('cluster/
|
|
15
|
+
this.uiServices = await this.$store.dispatch('cluster/findLabelSelector', {
|
|
16
16
|
type: SERVICE,
|
|
17
|
-
|
|
17
|
+
matching: { labelSelector: { matchLabels: { app: 'longhorn-ui' } } }
|
|
18
18
|
});
|
|
19
19
|
}
|
|
20
20
|
},
|
|
@@ -26,6 +26,7 @@ const DEFAULT_BANNER_SETTING = {
|
|
|
26
26
|
fontSize: '14px',
|
|
27
27
|
textDecoration: null,
|
|
28
28
|
text: null,
|
|
29
|
+
html: null,
|
|
29
30
|
},
|
|
30
31
|
bannerFooter: {
|
|
31
32
|
background: null,
|
|
@@ -35,7 +36,8 @@ const DEFAULT_BANNER_SETTING = {
|
|
|
35
36
|
fontStyle: null,
|
|
36
37
|
fontSize: '14px',
|
|
37
38
|
textDecoration: null,
|
|
38
|
-
text: null
|
|
39
|
+
text: null,
|
|
40
|
+
html: null,
|
|
39
41
|
},
|
|
40
42
|
bannerConsent: {
|
|
41
43
|
background: null,
|
|
@@ -47,6 +49,7 @@ const DEFAULT_BANNER_SETTING = {
|
|
|
47
49
|
textDecoration: null,
|
|
48
50
|
text: null,
|
|
49
51
|
button: null,
|
|
52
|
+
html: null,
|
|
50
53
|
},
|
|
51
54
|
showHeader: 'false',
|
|
52
55
|
showFooter: 'false',
|
|
@@ -71,6 +74,7 @@ export default {
|
|
|
71
74
|
Object.assign(this, hash);
|
|
72
75
|
|
|
73
76
|
this.uiBannerIndividual = getIndividualBanners(this.$store);
|
|
77
|
+
this.overlayDefaults(this.bannerVal);
|
|
74
78
|
},
|
|
75
79
|
|
|
76
80
|
data() {
|
|
@@ -127,12 +131,26 @@ export default {
|
|
|
127
131
|
this.bannerVal = this.checkOrUpdateLegacyUIBannerSetting(parsedBanner);
|
|
128
132
|
|
|
129
133
|
overlayIndividualBanners(this.bannerVal, this.uiBannerIndividual);
|
|
134
|
+
this.overlayDefaults(this.bannerVal);
|
|
130
135
|
} catch {}
|
|
131
136
|
}
|
|
132
137
|
}
|
|
133
138
|
},
|
|
134
139
|
|
|
135
140
|
methods: {
|
|
141
|
+
// We remove some of the defaults (e.g. font size) when we save as HTML
|
|
142
|
+
// so add them back in so that if the user switches back to text, they have those defaults
|
|
143
|
+
overlayDefaults(value) {
|
|
144
|
+
Object.keys(value).forEach((key) => {
|
|
145
|
+
if (key.startsWith('banner')) {
|
|
146
|
+
value[key] = {
|
|
147
|
+
...DEFAULT_BANNER_SETTING[key] || {},
|
|
148
|
+
...value[key]
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
},
|
|
153
|
+
|
|
136
154
|
checkOrUpdateLegacyUIBannerSetting(parsedBanner) {
|
|
137
155
|
const {
|
|
138
156
|
bannerHeader, bannerFooter, bannerConsent, banner, loginError
|
|
@@ -175,8 +193,44 @@ export default {
|
|
|
175
193
|
return parsedBanner;
|
|
176
194
|
},
|
|
177
195
|
|
|
196
|
+
tidy(val) {
|
|
197
|
+
Object.keys(val).forEach((k) => {
|
|
198
|
+
if (k.startsWith('banner')) {
|
|
199
|
+
const banner = val[k];
|
|
200
|
+
const isHtml = banner.isHtml === undefined ? !!banner.html : banner.isHtml;
|
|
201
|
+
|
|
202
|
+
if (isHtml) {
|
|
203
|
+
banner.html = banner.html?.trim(); // Remove any whitespace at start and end
|
|
204
|
+
delete banner.text;
|
|
205
|
+
|
|
206
|
+
// Remove all of the fields for text formatting (not needed for HTML)
|
|
207
|
+
delete banner.textAlignment;
|
|
208
|
+
delete banner.textDecoration;
|
|
209
|
+
delete banner.fontWeight;
|
|
210
|
+
delete banner.fontStyle;
|
|
211
|
+
delete banner.fontSize;
|
|
212
|
+
} else {
|
|
213
|
+
delete banner.html;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
delete banner.isHtml;
|
|
217
|
+
|
|
218
|
+
Object.keys(banner).forEach((f) => {
|
|
219
|
+
if (banner[f] === null) {
|
|
220
|
+
delete banner[f];
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
return val;
|
|
227
|
+
},
|
|
228
|
+
|
|
178
229
|
async save(btnCB) {
|
|
179
|
-
|
|
230
|
+
// Clone the value - we'll tidy it up before save, but want to keep the full value in case the save errors
|
|
231
|
+
const cpy = JSON.parse(JSON.stringify(this.bannerVal));
|
|
232
|
+
|
|
233
|
+
this.uiBannerSetting.value = JSON.stringify(this.tidy(cpy));
|
|
180
234
|
|
|
181
235
|
this.errors = [];
|
|
182
236
|
|
|
@@ -14,12 +14,10 @@ import paginationUtils from '@shell/utils/pagination-utils';
|
|
|
14
14
|
import Collapse from '@shell/components/Collapse';
|
|
15
15
|
|
|
16
16
|
const incompatible = {
|
|
17
|
-
incrementalLoading: ['forceNsFilterV2'
|
|
18
|
-
manualRefresh: ['forceNsFilterV2'
|
|
17
|
+
incrementalLoading: ['forceNsFilterV2'],
|
|
18
|
+
manualRefresh: ['forceNsFilterV2'],
|
|
19
19
|
forceNsFilterV2: ['incrementalLoading', 'manualRefresh'],
|
|
20
|
-
serverPagination: ['incrementalLoading', 'manualRefresh'],
|
|
21
20
|
};
|
|
22
|
-
|
|
23
21
|
const l10n = {
|
|
24
22
|
incrementalLoading: 'incrementalLoad',
|
|
25
23
|
manualRefresh: 'manualRefresh',
|
|
@@ -97,10 +95,6 @@ export default {
|
|
|
97
95
|
return this.$store.getters['features/get'](STEVE_CACHE);
|
|
98
96
|
},
|
|
99
97
|
|
|
100
|
-
steveCacheAndSSPEnabled() {
|
|
101
|
-
return this.steveCacheEnabled && this.value.serverPagination.enabled;
|
|
102
|
-
},
|
|
103
|
-
|
|
104
98
|
sspApplicableResources() {
|
|
105
99
|
const storeResources = [];
|
|
106
100
|
const stores = paginationUtils.getStoreSettings(this.value.serverPagination);
|
|
@@ -257,27 +251,14 @@ export default {
|
|
|
257
251
|
</h2>
|
|
258
252
|
<p>{{ t('performance.serverPagination.description') }}</p>
|
|
259
253
|
<Banner
|
|
260
|
-
v-if="!steveCacheEnabled"
|
|
261
|
-
v-clean-html="t(`performance.serverPagination.featureFlag`, { ffUrl }, true)"
|
|
262
254
|
color="warning"
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
label-key="performance.serverPagination.experimental"
|
|
267
|
-
/>
|
|
268
|
-
<Checkbox
|
|
269
|
-
v-model:value="value.serverPagination.enabled"
|
|
270
|
-
:mode="mode"
|
|
271
|
-
:label="t('performance.serverPagination.checkboxLabel')"
|
|
272
|
-
class="mt-10 mb-10"
|
|
273
|
-
:primary="true"
|
|
274
|
-
:disabled="!steveCacheEnabled"
|
|
275
|
-
@update:value="compatibleWarning('serverPagination', $event)"
|
|
276
|
-
/>
|
|
255
|
+
>
|
|
256
|
+
<div v-clean-html="t(`performance.serverPagination.featureFlag`, { ffUrl }, true)" />
|
|
257
|
+
</Banner>
|
|
277
258
|
<Collapse
|
|
278
259
|
:title="t('performance.serverPagination.applicable')"
|
|
279
|
-
:open="
|
|
280
|
-
:isDisabled="!
|
|
260
|
+
:open="steveCacheEnabled && ssPApplicableTypesOpen"
|
|
261
|
+
:isDisabled="!steveCacheEnabled"
|
|
281
262
|
@update:open="ssPApplicableTypesOpen = !ssPApplicableTypesOpen"
|
|
282
263
|
>
|
|
283
264
|
<p
|