@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.
Files changed (113) hide show
  1. package/assets/data/aws-regions.json +2 -0
  2. package/assets/styles/global/_layout.scss +0 -1
  3. package/assets/translations/en-us.yaml +61 -19
  4. package/assets/translations/zh-hans.yaml +0 -10
  5. package/chart/monitoring/index.vue +1 -1
  6. package/components/AsyncButton.vue +2 -0
  7. package/components/CodeMirror.vue +3 -3
  8. package/components/CruResource.vue +103 -15
  9. package/components/ExplorerProjectsNamespaces.vue +7 -2
  10. package/components/FixedBanner.vue +19 -5
  11. package/components/PaginatedResourceTable.vue +7 -0
  12. package/components/ResourceDetail/Masthead.vue +0 -1
  13. package/components/ResourceList/index.vue +2 -1
  14. package/components/SlideInPanelManager.vue +1 -2
  15. package/components/SortableTable/selection.js +1 -1
  16. package/components/Tabbed/index.vue +6 -0
  17. package/components/__tests__/AsyncButton.test.ts +39 -0
  18. package/components/__tests__/CruResource.test.ts +63 -0
  19. package/components/__tests__/PromptModal.test.ts +0 -2
  20. package/components/form/ArrayList.vue +134 -118
  21. package/components/form/BannerSettings.vue +145 -96
  22. package/components/form/KeyValue.vue +10 -7
  23. package/components/form/LabeledSelect.vue +9 -2
  24. package/components/form/MatchExpressions.vue +5 -1
  25. package/components/form/NameNsDescription.vue +1 -1
  26. package/components/form/ResourceSelector.vue +26 -23
  27. package/components/form/ResourceTabs/index.vue +2 -1
  28. package/components/form/Select.vue +9 -2
  29. package/components/form/UnitInput.vue +13 -0
  30. package/components/form/__tests__/ArrayList.test.ts +32 -0
  31. package/components/form/__tests__/KeyValue.test.ts +36 -0
  32. package/components/form/__tests__/LabeledSelect.test.ts +33 -0
  33. package/components/form/__tests__/Select.test.ts +34 -1
  34. package/components/form/__tests__/UnitInput.test.ts +23 -1
  35. package/components/formatter/ClusterLink.vue +5 -8
  36. package/components/formatter/Description.vue +30 -0
  37. package/components/formatter/__tests__/ClusterLink.test.ts +2 -32
  38. package/components/nav/NamespaceFilter.vue +1 -1
  39. package/components/nav/WindowManager/index.vue +1 -0
  40. package/config/product/explorer.js +16 -13
  41. package/config/product/manager.js +1 -28
  42. package/config/settings.ts +11 -13
  43. package/config/table-headers.js +7 -5
  44. package/detail/catalog.cattle.io.app.vue +0 -1
  45. package/detail/provisioning.cattle.io.cluster.vue +13 -3
  46. package/detail/service.vue +0 -1
  47. package/detail/workload/index.vue +21 -34
  48. package/dialog/ExtensionCatalogUninstallDialog.vue +14 -8
  49. package/edit/__tests__/service.test.ts +2 -1
  50. package/edit/networking.k8s.io.networkpolicy/PolicyRule.vue +3 -14
  51. package/edit/networking.k8s.io.networkpolicy/PolicyRuleTarget.vue +57 -62
  52. package/edit/networking.k8s.io.networkpolicy/PolicyRules.vue +3 -14
  53. package/edit/networking.k8s.io.networkpolicy/__tests__/PolicyRuleTarget.test.ts +72 -41
  54. package/edit/networking.k8s.io.networkpolicy/__tests__/utils/mock.json +17 -1
  55. package/edit/networking.k8s.io.networkpolicy/index.vue +18 -30
  56. package/edit/provisioning.cattle.io.cluster/index.vue +21 -73
  57. package/edit/service.vue +13 -28
  58. package/list/workload.vue +6 -1
  59. package/mixins/resource-fetch-api-pagination.js +55 -43
  60. package/mixins/resource-fetch.js +14 -5
  61. package/models/__tests__/workload.test.ts +1 -0
  62. package/models/cluster/node.js +1 -0
  63. package/models/cluster.js +32 -2
  64. package/models/management.cattle.io.cluster.js +0 -20
  65. package/models/management.cattle.io.node.js +7 -22
  66. package/models/management.cattle.io.nodepool.js +12 -0
  67. package/models/namespace.js +5 -0
  68. package/models/provisioning.cattle.io.cluster.js +18 -64
  69. package/models/service.js +24 -9
  70. package/models/workload.js +70 -31
  71. package/package.json +1 -1
  72. package/pages/c/_cluster/apps/charts/install.vue +0 -1
  73. package/pages/c/_cluster/explorer/index.vue +11 -0
  74. package/pages/c/_cluster/longhorn/index.vue +2 -2
  75. package/pages/c/_cluster/settings/banners.vue +56 -2
  76. package/pages/c/_cluster/settings/performance.vue +7 -26
  77. package/pages/home.vue +11 -52
  78. package/plugins/clean-html.js +2 -0
  79. package/plugins/dashboard-store/__tests__/actions.test.ts +4 -1
  80. package/plugins/dashboard-store/actions.js +122 -21
  81. package/plugins/dashboard-store/getters.js +74 -3
  82. package/plugins/dashboard-store/mutations.js +10 -5
  83. package/plugins/dashboard-store/resource-class.js +23 -3
  84. package/plugins/steve/__tests__/getters.test.ts +18 -11
  85. package/plugins/steve/__tests__/steve-class.test.ts +1 -0
  86. package/plugins/steve/actions.js +34 -12
  87. package/plugins/steve/getters.js +39 -10
  88. package/plugins/steve/steve-class.js +5 -0
  89. package/plugins/steve/steve-pagination-utils.ts +199 -37
  90. package/plugins/steve/worker/web-worker.advanced.js +3 -1
  91. package/rancher-components/Banner/Banner.test.ts +51 -3
  92. package/rancher-components/Banner/Banner.vue +28 -6
  93. package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +5 -1
  94. package/rancher-components/Form/ToggleSwitch/ToggleSwitch.vue +21 -1
  95. package/store/features.js +0 -1
  96. package/store/type-map.utils.ts +45 -2
  97. package/types/fleet.d.ts +1 -1
  98. package/types/kube/kube-api.ts +22 -0
  99. package/types/resources/settings.d.ts +0 -4
  100. package/types/shell/index.d.ts +346 -289
  101. package/types/store/dashboard-store.types.ts +24 -1
  102. package/types/store/pagination.types.ts +19 -2
  103. package/utils/cluster.js +24 -20
  104. package/utils/grafana.js +1 -0
  105. package/utils/object.js +0 -12
  106. package/utils/pagination-utils.ts +6 -2
  107. package/utils/perf-setting.utils.ts +28 -0
  108. package/utils/selector-typed.ts +205 -0
  109. package/utils/selector.js +29 -6
  110. package/utils/uiplugins.ts +10 -6
  111. package/utils/v-sphere.ts +5 -1
  112. package/components/formatter/RKETemplateName.vue +0 -37
  113. 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
  }
@@ -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 || (this.isRke1 && this.mgmt?.hasAction('rotateEncryptionKey') && ready)
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
- // Used in conjunction with `matches/match/label selectors`. Requires https://github.com/rancher/dashboard/issues/10417 to fix
142
- await this.$dispatch('cluster/findMatching', {
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
- return this.podRelationship ? this.$getters.matching( POD, this.podRelationship.selector, this.namespace ) : [];
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() {
@@ -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, matching, matches } from '@shell/utils/selector';
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
- const pods = this.pods;
211
+ return this.pods.reduce((total, pod) => {
212
+ const { status:{ containerStatuses = [] } } = pod;
214
213
 
215
- let sum = 0;
214
+ if (containerStatuses.length) {
215
+ total += containerStatuses.reduce((tot, container) => {
216
+ tot += container.restartCount || 0;
216
217
 
217
- pods.forEach((pod) => {
218
- if (pod.status.containerStatuses) {
219
- sum += pod.status?.containerStatuses[0].restartCount || 0;
218
+ return tot;
219
+ }, 0);
220
220
  }
221
- });
222
221
 
223
- return sum;
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
- get pods() {
554
- const relationships = this.metadata?.relationships || [];
555
- const podRelationship = relationships.filter((relationship) => relationship.toType === POD)[0];
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
- if (podRelationship) {
558
- const pods = this.$getters['podsByNamespace'](this.metadata.namespace);
571
+ return undefined;
572
+ }
559
573
 
560
- // Used in conjunction with `matches/match/label selectors`. Requires https://github.com/rancher/dashboard/issues/10417 to fix
561
- return pods.filter((obj) => {
562
- return matches(obj, podRelationship.selector);
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
- get podGauges() {
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 (!this.pods) {
605
+ if (!pods) {
573
606
  return out;
574
607
  }
575
608
 
576
- this.pods.map((pod) => {
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
- // Used in conjunction with `matches/match/label selectors`. Requires https://github.com/rancher/dashboard/issues/10417 to fix
668
- const all = await this.$dispatch('findAll', { type: POD });
669
- const allInNamespace = all.filter((pod) => pod.metadata.namespace === this.metadata.namespace);
670
-
671
- const selector = convertSelectorObj(this.spec.selector);
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 matching(allInNamespace, selector);
712
+ return matchInfo.matches;
674
713
  }
675
714
 
676
715
  cleanForSave(data) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rancher/shell",
3
- "version": "3.0.5-rc.2",
3
+ "version": "3.0.5-rc.3",
4
4
  "description": "Rancher Dashboard Shell",
5
5
  "repository": "https://github.com/rancherlabs/dashboard",
6
6
  "license": "Apache-2.0",
@@ -333,7 +333,6 @@ export default {
333
333
  this.chartValues = mergeWithReplace(
334
334
  merge({}, this.versionInfo?.values || {}),
335
335
  userValues,
336
- { replaceObjectProps: true }
337
336
  );
338
337
 
339
338
  if (this.showCustomRegistry) {
@@ -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/findMatching', {
15
+ this.uiServices = await this.$store.dispatch('cluster/findLabelSelector', {
16
16
  type: SERVICE,
17
- selector: 'app=longhorn-ui'
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
- this.uiBannerSetting.value = JSON.stringify(this.bannerVal);
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', 'serverPagination'],
18
- manualRefresh: ['forceNsFilterV2', 'serverPagination'],
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
- <Banner
265
- color="error"
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="steveCacheAndSSPEnabled && ssPApplicableTypesOpen"
280
- :isDisabled="!steveCacheAndSSPEnabled"
260
+ :open="steveCacheEnabled && ssPApplicableTypesOpen"
261
+ :isDisabled="!steveCacheEnabled"
281
262
  @update:open="ssPApplicableTypesOpen = !ssPApplicableTypesOpen"
282
263
  >
283
264
  <p