@rancher/shell 3.0.9-rc.5 → 3.0.9

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 (172) hide show
  1. package/assets/images/providers/oci-open-containers.svg +22 -0
  2. package/assets/images/providers/traefik.png +0 -0
  3. package/assets/styles/themes/_dark.scss +2 -0
  4. package/assets/styles/themes/_light.scss +2 -0
  5. package/assets/styles/themes/_modern.scss +6 -0
  6. package/assets/translations/en-us.yaml +129 -25
  7. package/components/CruResource.vue +3 -1
  8. package/components/ExplorerProjectsNamespaces.vue +12 -12
  9. package/components/IconOrSvg.vue +61 -42
  10. package/components/Resource/Detail/Card/StatusCard/__tests__/StatusCard.test.ts +109 -0
  11. package/components/Resource/Detail/Card/StatusCard/index.vue +21 -4
  12. package/components/Resource/Detail/Metadata/IdentifyingInformation/__tests__/identifying-fields.test.ts +19 -2
  13. package/components/Resource/Detail/Metadata/IdentifyingInformation/identifying-fields.ts +19 -11
  14. package/components/Resource/Detail/ResourcePopover/__tests__/index.test.ts +12 -0
  15. package/components/Resource/Detail/ResourcePopover/index.vue +2 -0
  16. package/components/Resource/Detail/ResourceRow.vue +2 -2
  17. package/components/ResourceList/index.vue +7 -4
  18. package/components/SortableTable/index.vue +2 -2
  19. package/components/Window/ContainerLogs.vue +48 -37
  20. package/components/fleet/FleetClusterTargets/TargetsList.vue +2 -2
  21. package/components/fleet/FleetClusterTargets/index.vue +6 -1
  22. package/components/fleet/GitRepoAdvancedTab.vue +333 -0
  23. package/components/fleet/GitRepoMetadataTab.vue +43 -0
  24. package/components/fleet/GitRepoRepositoryTab.vue +101 -0
  25. package/components/fleet/GitRepoTargetTab.vue +77 -0
  26. package/components/fleet/HelmOpAdvancedTab.vue +247 -0
  27. package/components/fleet/HelmOpChartTab.vue +158 -0
  28. package/components/fleet/HelmOpMetadataTab.vue +46 -0
  29. package/components/fleet/HelmOpTargetTab.vue +84 -0
  30. package/components/fleet/HelmOpValuesTab.vue +147 -0
  31. package/components/fleet/__tests__/FleetClusterTargets.test.ts +119 -70
  32. package/components/form/BannerSettings.vue +2 -2
  33. package/components/form/NodeScheduling.vue +81 -7
  34. package/components/form/NotificationSettings.vue +2 -2
  35. package/components/form/PodAffinity.vue +1 -36
  36. package/components/form/ResourceLabeledSelect.vue +8 -4
  37. package/components/form/ResourceQuota/Namespace.vue +30 -9
  38. package/components/form/ResourceQuota/NamespaceRow.vue +25 -7
  39. package/components/form/ResourceQuota/Project.vue +140 -82
  40. package/components/form/ResourceQuota/ResourceQuotaEntry.vue +145 -0
  41. package/components/form/ResourceQuota/__tests__/Namespace.test.ts +307 -0
  42. package/components/form/ResourceQuota/__tests__/NamespaceRow.test.ts +281 -0
  43. package/components/form/ResourceQuota/__tests__/Project.test.ts +274 -27
  44. package/components/form/ResourceQuota/__tests__/ResourceQuotaEntry.test.ts +215 -0
  45. package/components/form/SchedulingCustomization.vue +14 -6
  46. package/components/form/SelectOrCreateAuthSecret.vue +107 -18
  47. package/components/form/__tests__/NodeScheduling.test.ts +12 -9
  48. package/components/form/__tests__/PodAffinity.test.ts +21 -2
  49. package/components/form/__tests__/SchedulingCustomization.test.ts +240 -0
  50. package/components/formatter/ClusterLink.vue +8 -0
  51. package/components/formatter/SecretOrigin.vue +79 -0
  52. package/config/labels-annotations.js +7 -6
  53. package/config/pagination-table-headers.js +6 -4
  54. package/config/product/explorer.js +1 -11
  55. package/config/product/manager.js +0 -1
  56. package/config/query-params.js +3 -0
  57. package/config/settings.ts +15 -2
  58. package/config/table-headers.js +21 -17
  59. package/config/types.js +23 -8
  60. package/detail/fleet.cattle.io.cluster.vue +1 -1
  61. package/detail/workload/index.vue +11 -16
  62. package/dialog/DeactivateDriverDialog.vue +1 -1
  63. package/dialog/FeatureFlagListDialog.vue +1 -1
  64. package/dialog/Ipv6NetworkingDialog.vue +156 -0
  65. package/dialog/ScalePoolDownDialog.vue +2 -2
  66. package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +1 -1
  67. package/edit/__tests__/fleet.cattle.io.helmop.test.ts +1 -0
  68. package/edit/__tests__/management.cattle.io.project.test.js +56 -128
  69. package/edit/auth/oidc.vue +1 -1
  70. package/edit/catalog.cattle.io.clusterrepo.vue +155 -25
  71. package/edit/fleet.cattle.io.gitrepo.vue +153 -283
  72. package/edit/fleet.cattle.io.helmop.vue +190 -332
  73. package/edit/management.cattle.io.project.vue +5 -42
  74. package/edit/management.cattle.io.setting.vue +6 -0
  75. package/edit/monitoring.coreos.com.alertmanagerconfig/__tests__/auth.spec.ts +145 -0
  76. package/edit/monitoring.coreos.com.alertmanagerconfig/__tests__/index.test.ts +202 -0
  77. package/edit/monitoring.coreos.com.alertmanagerconfig/__tests__/tls.spec.ts +226 -0
  78. package/edit/monitoring.coreos.com.alertmanagerconfig/auth.vue +24 -21
  79. package/edit/monitoring.coreos.com.alertmanagerconfig/types/__tests__/opsgenie.spec.ts +157 -0
  80. package/edit/monitoring.coreos.com.alertmanagerconfig/types/__tests__/pagerduty.spec.ts +132 -0
  81. package/edit/monitoring.coreos.com.alertmanagerconfig/types/__tests__/slack.spec.ts +108 -0
  82. package/edit/monitoring.coreos.com.alertmanagerconfig/types/pagerduty.vue +2 -1
  83. package/edit/monitoring.coreos.com.receiver/__tests__/auth.spec.ts +165 -0
  84. package/edit/monitoring.coreos.com.receiver/__tests__/index.test.ts +153 -0
  85. package/edit/monitoring.coreos.com.receiver/__tests__/tls.spec.ts +115 -0
  86. package/edit/monitoring.coreos.com.receiver/types/__tests__/email.spec.ts +86 -0
  87. package/edit/monitoring.coreos.com.receiver/types/__tests__/opsgenie.spec.ts +209 -0
  88. package/edit/monitoring.coreos.com.receiver/types/__tests__/pagerduty.spec.ts +105 -0
  89. package/edit/monitoring.coreos.com.receiver/types/__tests__/slack.spec.ts +92 -0
  90. package/edit/monitoring.coreos.com.receiver/types/__tests__/webhook.spec.ts +131 -0
  91. package/edit/provisioning.cattle.io.cluster/__tests__/Basics.test.ts +55 -24
  92. package/edit/provisioning.cattle.io.cluster/__tests__/Networking.test.ts +1 -103
  93. package/edit/provisioning.cattle.io.cluster/__tests__/index.test.ts +13 -1
  94. package/edit/provisioning.cattle.io.cluster/__tests__/rke2-fleet-cluster-agent.test.ts +283 -0
  95. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +65 -49
  96. package/edit/provisioning.cattle.io.cluster/ingress/IngressCards.vue +114 -0
  97. package/edit/provisioning.cattle.io.cluster/ingress/IngressConfiguration.vue +158 -0
  98. package/edit/provisioning.cattle.io.cluster/rke2.vue +167 -69
  99. package/edit/provisioning.cattle.io.cluster/shared.ts +36 -1
  100. package/edit/provisioning.cattle.io.cluster/tabs/AgentConfiguration.vue +2 -1
  101. package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +70 -7
  102. package/edit/provisioning.cattle.io.cluster/tabs/Ingress.vue +343 -0
  103. package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +2 -1
  104. package/edit/provisioning.cattle.io.cluster/tabs/etcd/__tests__/S3Config.test.ts +13 -1
  105. package/edit/provisioning.cattle.io.cluster/tabs/networking/index.vue +10 -44
  106. package/edit/secret/index.vue +1 -1
  107. package/edit/token.vue +68 -29
  108. package/edit/workload/__tests__/index.test.ts +2 -37
  109. package/edit/workload/index.vue +6 -2
  110. package/edit/workload/mixins/workload.js +0 -32
  111. package/list/__tests__/management.cattle.io.setting.test.ts +198 -0
  112. package/list/management.cattle.io.setting.vue +13 -0
  113. package/list/provisioning.cattle.io.cluster.vue +50 -1
  114. package/list/secret.vue +4 -9
  115. package/list/service.vue +6 -8
  116. package/machine-config/amazonec2.vue +11 -4
  117. package/machine-config/components/EC2Networking.vue +46 -30
  118. package/machine-config/components/__tests__/EC2Networking.test.ts +7 -7
  119. package/machine-config/components/__tests__/utils/vpcSubnetMockData.js +0 -9
  120. package/machine-config/digitalocean.vue +3 -3
  121. package/models/__tests__/chart.test.ts +2 -2
  122. package/models/__tests__/namespace.test.ts +11 -0
  123. package/models/__tests__/provisioning.cattle.io.cluster.test.ts +96 -0
  124. package/models/__tests__/workload.test.ts +42 -1
  125. package/models/catalog.cattle.io.clusterrepo.js +30 -4
  126. package/models/chart.js +3 -3
  127. package/models/ext.cattle.io.token.js +48 -0
  128. package/models/kontainerdriver.js +2 -2
  129. package/models/namespace.js +7 -1
  130. package/models/nodedriver.js +2 -2
  131. package/models/provisioning.cattle.io.cluster.js +28 -7
  132. package/models/secret.js +0 -17
  133. package/models/service.js +44 -1
  134. package/models/token.js +4 -0
  135. package/models/workload.js +12 -6
  136. package/package.json +1 -1
  137. package/pages/account/index.vue +96 -67
  138. package/pages/auth/setup.vue +5 -14
  139. package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +45 -18
  140. package/pages/c/_cluster/apps/charts/__tests__/install.test.ts +4 -1
  141. package/pages/c/_cluster/apps/charts/index.vue +82 -3
  142. package/pages/c/_cluster/apps/charts/install.vue +317 -42
  143. package/pages/c/_cluster/explorer/tools/index.vue +1 -1
  144. package/pages/c/_cluster/manager/cloudCredential/index.vue +1 -1
  145. package/pages/c/_cluster/manager/drivers/kontainerDriver/index.vue +5 -4
  146. package/pages/c/_cluster/settings/index.vue +3 -1
  147. package/pages/c/_cluster/uiplugins/index.vue +1 -1
  148. package/plugins/dashboard-store/__tests__/getters.test.ts +108 -0
  149. package/plugins/dashboard-store/__tests__/resource-class.test.ts +27 -0
  150. package/plugins/dashboard-store/actions.js +3 -8
  151. package/plugins/dashboard-store/getters.js +7 -5
  152. package/plugins/dashboard-store/mutations.js +4 -1
  153. package/plugins/dashboard-store/resource-class.js +3 -3
  154. package/plugins/steve/__tests__/steve-class.test.ts +102 -141
  155. package/plugins/steve/steve-class.js +12 -3
  156. package/plugins/steve/steve-pagination-utils.ts +6 -2
  157. package/rancher-components/RcIcon/types.ts +2 -0
  158. package/rancher-components/RcItemCard/RcItemCard.vue +72 -20
  159. package/store/prefs.js +3 -0
  160. package/types/aws-sdk.d.ts +121 -0
  161. package/types/resources/node.ts +15 -0
  162. package/types/shell/index.d.ts +537 -506
  163. package/types/store/pagination.types.ts +5 -5
  164. package/utils/__tests__/array.test.ts +1 -29
  165. package/utils/__tests__/cluster-agent-configuration.test.ts +203 -0
  166. package/utils/array.ts +0 -11
  167. package/utils/aws.ts +21 -0
  168. package/utils/cluster.js +22 -2
  169. package/utils/selector-typed.ts +1 -1
  170. package/utils/svg-filter.js +4 -3
  171. package/components/__tests__/ProjectRow.test.ts +0 -206
  172. package/components/form/ResourceQuota/ProjectRow.vue +0 -277
@@ -0,0 +1,48 @@
1
+ import SteveModel from '@shell/plugins/steve/steve-class';
2
+
3
+ export default class SteveToken extends SteveModel {
4
+ // for now, we are only showing the new tokens in the UI. Later we will be able to edit a few of it's fields
5
+ get _availableActions() {
6
+ return super._availableActions.filter((a) => ['viewInApi', 'promptRemove'].includes(a.action));
7
+ }
8
+
9
+ cleanForSave(data) {
10
+ const val = super.cleanForSave(data);
11
+
12
+ delete val.type;
13
+
14
+ return val;
15
+ }
16
+
17
+ get isDeprecated() {
18
+ return undefined;
19
+ }
20
+
21
+ get state() {
22
+ return this.isExpired ? 'expired' : !this.spec?.enabled ? 'inactive' : 'active';
23
+ }
24
+
25
+ get isExpired() {
26
+ return this.status?.expired;
27
+ }
28
+
29
+ get expiresAt() {
30
+ return this.status?.expiresAt || '';
31
+ }
32
+
33
+ get lastUsedAt() {
34
+ return this.status?.lastUsedAt || '';
35
+ }
36
+
37
+ get description() {
38
+ return this.spec?.description || '';
39
+ }
40
+
41
+ get clusterId() {
42
+ return this.spec?.clusterName || '';
43
+ }
44
+
45
+ get created() {
46
+ return this.metadata?.creationTimestamp;
47
+ }
48
+ }
@@ -70,7 +70,7 @@ export default class KontainerDriver extends Driver {
70
70
 
71
71
  activate() {
72
72
  return this.$dispatch('rancher/request', {
73
- url: `v3/kontainerDrivers/${ escape(this.id) }?action=activate`,
73
+ url: `v3/kontainerDrivers/${ encodeURIComponent(this.id) }?action=activate`,
74
74
  method: 'post',
75
75
  }, { root: true }).catch((err) => {
76
76
  this.$dispatch('growl/fromError', { title: this.t('drivers.error.activate', { name: this.nameDisplay }), err }, { root: true });
@@ -79,7 +79,7 @@ export default class KontainerDriver extends Driver {
79
79
 
80
80
  async activateBulk(resources) {
81
81
  await Promise.all(resources.map((resource) => this.$dispatch('rancher/request', {
82
- url: `v3/kontainerDrivers/${ escape(resource.id) }?action=activate`,
82
+ url: `v3/kontainerDrivers/${ encodeURIComponent(resource.id) }?action=activate`,
83
83
  method: 'post',
84
84
  }, { root: true }).catch((err) => {
85
85
  this.$dispatch('growl/fromError', { title: this.t('drivers.error.activate', { name: resource.nameDisplay }), err }, { root: true });
@@ -134,6 +134,10 @@ export default class Namespace extends SteveModel {
134
134
  return project;
135
135
  }
136
136
 
137
+ get projectNameDisplay() {
138
+ return this.project?.nameDisplay || '';
139
+ }
140
+
137
141
  get groupById() {
138
142
  const projectId = this.project?.id;
139
143
 
@@ -267,7 +271,9 @@ export default class Namespace extends SteveModel {
267
271
  }
268
272
 
269
273
  get hideDetailLocation() {
270
- return !!this.$rootGetters['currentProduct'].hideNamespaceLocation;
274
+ const currentProduct = this.$rootGetters['currentProduct'];
275
+
276
+ return currentProduct ? !!currentProduct.hideNamespaceLocation : true;
271
277
  }
272
278
 
273
279
  get glance() {
@@ -78,7 +78,7 @@ export default class NodeDriver extends Driver {
78
78
 
79
79
  activate() {
80
80
  return this.$dispatch('rancher/request', {
81
- url: `v3/nodeDrivers/${ escape(this.id) }?action=activate`,
81
+ url: `v3/nodeDrivers/${ encodeURIComponent(this.id) }?action=activate`,
82
82
  method: 'post',
83
83
  }, { root: true }).catch((err) => {
84
84
  this.$dispatch('growl/fromError', { title: this.t('drivers.error.activate', { name: this.nameDisplay }), err }, { root: true });
@@ -87,7 +87,7 @@ export default class NodeDriver extends Driver {
87
87
 
88
88
  async activateBulk(resources) {
89
89
  await Promise.all(resources.map((resource) => this.$dispatch('rancher/request', {
90
- url: `v3/nodeDrivers/${ escape(resource.id) }?action=activate`,
90
+ url: `v3/nodeDrivers/${ encodeURIComponent(resource.id) }?action=activate`,
91
91
  method: 'post',
92
92
  }, { root: true }).catch((err) => {
93
93
  this.$dispatch('growl/fromError', { title: this.t('drivers.error.activate', { name: resource.nameDisplay }), err }, { root: true });
@@ -101,14 +101,18 @@ export default class ProvCluster extends SteveModel {
101
101
  }
102
102
 
103
103
  get canEdit() {
104
- // If the cluster is a KEV1 cluster or Harvester cluster then prevent edit
105
- if (this.isKev1 || this.isHarvester) {
104
+ // If the cluster is a KEV1 cluster, Harvester cluster, or v2 provisioning cluster that uses upstream capi infrastructure providers, then prevent edit
105
+ if (this.isKev1 || this.isHarvester || this.isCapiHybrid) {
106
106
  return false;
107
107
  }
108
108
 
109
109
  return super.canEdit;
110
110
  }
111
111
 
112
+ get canCustomEdit() {
113
+ return !this.isCapiHybrid && super.canCustomEdit;
114
+ }
115
+
112
116
  get _availableActions() {
113
117
  const out = super._availableActions;
114
118
  const isLocal = this.mgmt?.isLocal;
@@ -123,9 +127,9 @@ export default class ProvCluster extends SteveModel {
123
127
  }
124
128
  const ready = this.mgmt?.isReady;
125
129
 
126
- const canEditRKE2cluster = this.isRke2 && ready && this.canUpdate;
130
+ const canEditRKE2cluster = this.isRke2 && ready && this.canUpdate && !this.isCapiHybrid;
127
131
 
128
- const canSnapshot = ready && this.isRke2 && this.canUpdate;
132
+ const canSnapshot = ready && this.isRke2 && this.canUpdate && !this.isCapiHybrid;
129
133
 
130
134
  const actions = [
131
135
  // Note: Actions are not supported in the Steve API, so we check
@@ -388,6 +392,19 @@ export default class ProvCluster extends SteveModel {
388
392
  return !!this.mgmt?.isHarvester;
389
393
  }
390
394
 
395
+ // identify v2 provisioning clusters created using upstream capi infrastructure providers instead of rancher/machine
396
+ get isCapiHybrid() {
397
+ if (!this.isRke2) {
398
+ return false;
399
+ }
400
+
401
+ const machineReferences = (this.spec?.rkeConfig?.machinePools || []).map((pool) => pool.machineConfigRef);
402
+
403
+ const capiMachines = machineReferences.find((r) => r?.apiVersion?.includes('cluster.x-k8s.io'));
404
+
405
+ return !!capiMachines;
406
+ }
407
+
391
408
  get mgmtClusterId() {
392
409
  // when a cluster is created `this` instance isn't immediately updated with `status.clusterName`
393
410
  // Workaround - Get fresh copy from the store
@@ -536,7 +553,7 @@ export default class ProvCluster extends SteveModel {
536
553
 
537
554
  if (this.isHarvester) {
538
555
  return HARVESTER;
539
- } else if ( this.isImported ) {
556
+ } else if ( this.isImported || this.isCapiHybrid ) {
540
557
  return null;
541
558
  } else if ( this.isRke2 ) {
542
559
  const kind = this.spec?.rkeConfig?.machinePools?.[0]?.machineConfigRef?.kind?.toLowerCase();
@@ -880,7 +897,11 @@ export default class ProvCluster extends SteveModel {
880
897
 
881
898
  const cni = this.spec?.rkeConfig?.machineGlobalConfig?.cni;
882
899
 
883
- if ( cni && cni !== 'calico' ) {
900
+ if ( cni === 'flannel' && compare(this.kubernetesVersion, 'v1.29.2') < 0 ) {
901
+ return false;
902
+ }
903
+
904
+ if ( cni && cni !== 'calico' && cni !== 'flannel' ) {
884
905
  return false;
885
906
  }
886
907
 
@@ -1049,7 +1070,7 @@ export default class ProvCluster extends SteveModel {
1049
1070
  }
1050
1071
 
1051
1072
  get disableResourceDetailDrawerConfigTab() {
1052
- return !!this.isHarvester;
1073
+ return !!this.isHarvester || this.isCapiHybrid;
1053
1074
  }
1054
1075
 
1055
1076
  get fullDetailPageOverride() {
package/models/secret.js CHANGED
@@ -550,23 +550,6 @@ export default class Secret extends SteveModel {
550
550
  return this.$rootGetters[`${ STORE.MANAGEMENT }/byId`](MANAGEMENT.PROJECT, `${ this.projectScopedClusterId }/${ this.projectScopedProjectId }`);
551
551
  }
552
552
 
553
- get clusterAndProjectLabel() {
554
- if (!this.isProjectScoped) {
555
- return '';
556
- }
557
- const clusterName = this.projectCluster?.nameDisplay;
558
- // project is going to be empty if upstream and trying to show pss from downstream clusters
559
- // we only ever have the current clusters projects. if we change this (fetch them in the list)
560
- // we wipe out the header ns filter projects
561
- const projectName = this.project?.nameDisplay || this.projectScopedProjectId;
562
-
563
- if (clusterName && projectName) {
564
- return `${ projectName } (${ clusterName })`;
565
- }
566
-
567
- return '';
568
- }
569
-
570
553
  get detailLocation() {
571
554
  if (this.isProjectScoped) {
572
555
  const id = this.id?.replace(/.*\//, '');
package/models/service.js CHANGED
@@ -1,7 +1,8 @@
1
1
  import find from 'lodash/find';
2
- import { POD } from '@shell/config/types';
2
+ import { NODE, POD } from '@shell/config/types';
3
3
  import SteveModel from '@shell/plugins/steve/steve-class';
4
4
  import { parse } from '@shell/utils/selector';
5
+ import { PaginationFilterEquality, PaginationParamFilter } from '@shell/types/store/pagination.types';
5
6
 
6
7
  // i18n-uses servicesPage.serviceTypes.clusterIp.*, servicesPage.serviceTypes.externalName.*, servicesPage.serviceTypes.headless.*
7
8
  // i18n-uses servicesPage.serviceTypes.loadBalancer.*, servicesPage.serviceTypes.nodePort.*
@@ -50,6 +51,48 @@ export const CLUSTERIP = (() => {
50
51
  return clusterIp.id;
51
52
  })();
52
53
 
54
+ /**
55
+ * Use to populate the cache with a node that can be used in Service's target value
56
+ *
57
+ * Service's Target Cell --> shell/components/formatter/ServiceTargets.vue --> shell/components/formatter/Endpoints.vue --> Picks the first one that has a model's externalIp
58
+ */
59
+ export const fetchNodesForServiceTargets = async({
60
+ $store,
61
+ inStore,
62
+ }) => {
63
+ try {
64
+ const schema = $store.getters[`${ inStore }/schemaFor`](NODE);
65
+
66
+ if (schema) {
67
+ if ($store.getters[`${ inStore }/paginationEnabled`](NODE)) {
68
+ // Of type @ActionFindPageArgs
69
+ const findPageArgs = {
70
+ pagination: {
71
+ pageSize: 1,
72
+ filters: [
73
+ PaginationParamFilter.createSingleField({
74
+ field: 'status.addresses.type',
75
+ value: 'ExternalIP',
76
+ equality: PaginationFilterEquality.CONTAINS
77
+ }),
78
+ ],
79
+ },
80
+ };
81
+
82
+ return $store.dispatch(`${ inStore }/findPage`, { type: NODE, opt: findPageArgs });
83
+ } else {
84
+ return $store.dispatch(`${ inStore }/findAll`, { type: NODE });
85
+ }
86
+ } else {
87
+ return [];
88
+ }
89
+ } catch (err) {
90
+ console.warn('Failed to fetch nodes for service targets:', err); // eslint-disable-line no-console
91
+
92
+ return [];
93
+ }
94
+ };
95
+
53
96
  export default class Service extends SteveModel {
54
97
  get customValidationRules() {
55
98
  return [
package/models/token.js CHANGED
@@ -16,4 +16,8 @@ export default class extends NormanModel {
16
16
 
17
17
  return expiry.isBefore(day());
18
18
  }
19
+
20
+ get isDeprecated() {
21
+ return true;
22
+ }
19
23
  }
@@ -755,20 +755,26 @@ export default class Workload extends WorkloadService {
755
755
  get podsCard() {
756
756
  const supportedTypes = [WORKLOAD_TYPES.DEPLOYMENT, WORKLOAD_TYPES.DAEMON_SET, WORKLOAD_TYPES.JOB, WORKLOAD_TYPES.STATEFUL_SET];
757
757
 
758
- if (!supportedTypes.includes(this.type) || (this.pods?.length || 0) <= 0) {
758
+ if (!supportedTypes.includes(this.type)) {
759
759
  return null;
760
760
  }
761
761
 
762
762
  const scalingTypes = [WORKLOAD_TYPES.DEPLOYMENT, WORKLOAD_TYPES.STATEFUL_SET];
763
+ const canScale = this.canUpdate && scalingTypes.includes(this.type);
764
+
765
+ if (!this.pods || (this.pods.length === 0 && !canScale)) {
766
+ return null;
767
+ }
763
768
 
764
769
  return {
765
770
  component: markRaw(defineAsyncComponent(() => import('@shell/components/Resource/Detail/Card/StatusCard/index.vue'))),
766
771
  props: {
767
- title: this.t('component.resource.detail.card.podsCard.title'),
768
- resources: this.pods,
769
- showScaling: this.canUpdate && scalingTypes.includes(this.type),
770
- onIncrease: () => this.scale(true),
771
- onDecrease: () => this.scale(false)
772
+ title: this.t('component.resource.detail.card.podsCard.title'),
773
+ resources: this.pods,
774
+ showScaling: canScale,
775
+ onIncrease: () => this.scale(true),
776
+ onDecrease: () => this.scale(false),
777
+ noResourcesMessage: this.t('component.resource.detail.card.podsCard.noPods')
772
778
  }
773
779
  };
774
780
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rancher/shell",
3
- "version": "3.0.9-rc.5",
3
+ "version": "3.0.9",
4
4
  "description": "Rancher Dashboard Shell",
5
5
  "repository": "https://github.com/rancher/dashboard",
6
6
  "license": "Apache-2.0",
@@ -9,94 +9,107 @@ import { mapGetters } from 'vuex';
9
9
 
10
10
  import { Banner } from '@components/Banner';
11
11
  import ResourceTable from '@shell/components/ResourceTable';
12
- import CopyToClipboardText from '@shell/components/CopyToClipboardText';
13
12
  import TabTitle from '@shell/components/TabTitle';
14
13
 
15
- const API_ENDPOINT = '/v3';
14
+ import { allHash } from '@shell/utils/promise';
15
+
16
+ import {
17
+ ACCESS_KEY, DESCRIPTION, EXPIRES, EXPIRY_STATE,
18
+ LAST_USED, AGE_NORMAN, SCOPE_NORMAN, NORMAN_KEY_DEPRECATION
19
+ } from '@shell/config/table-headers';
20
+ import { FilterArgs, PaginationParamFilter } from '@shell/types/store/pagination.types';
16
21
 
17
22
  export default {
18
23
  components: {
19
- CopyToClipboardText, BackLink, Banner, Loading, ResourceTable, Principal, TabTitle
24
+ BackLink, Banner, Loading, ResourceTable, Principal, TabTitle
20
25
  },
21
26
  mixins: [BackRoute],
22
27
  async fetch() {
28
+ const hashedRequests = {};
29
+
23
30
  this.canChangePassword = await this.calcCanChangePassword();
24
31
 
25
- if (this.apiKeySchema) {
26
- this.rows = await this.$store.dispatch('rancher/findAll', { type: NORMAN.TOKEN });
27
- }
32
+ this.normanTokenSchema = this.$store.getters[`rancher/schemaFor`](NORMAN.TOKEN);
33
+ this.steveTokenSchema = this.$store.getters[`management/schemaFor`](EXT.TOKEN);
28
34
 
29
- // Get all settings - the API host setting may not be set, so this avoids a 404 request if we look for the specific setting
30
- const allSettings = await this.$store.dispatch('management/findAll', { type: MANAGEMENT.SETTING });
31
- const apiHostSetting = allSettings.find((i) => i.id === SETTING.API_HOST);
32
- const serverUrlSetting = allSettings.find((i) => i.id === SETTING.SERVER_URL);
35
+ const selfUser = await this.$store.dispatch('auth/getSelfUser');
33
36
 
34
- this.apiHostSetting = apiHostSetting?.value;
35
- this.serverUrlSetting = serverUrlSetting?.value;
37
+ if (this.normanTokenSchema) {
38
+ hashedRequests.normanTokens = this.$store.dispatch('rancher/findAll', { type: NORMAN.TOKEN });
39
+ }
36
40
 
37
- const selfUser = await this.$store.dispatch('auth/getSelfUser');
41
+ if (this.steveTokenSchema) {
42
+ this.filterByUserTokens = this.$store.getters[`management/paginationEnabled`](EXT.TOKEN);
43
+
44
+ if (this.filterByUserTokens && selfUser?.status?.userID) {
45
+ // Only get associated with the current user
46
+ const opt = { // Of type ActionFindPageArgs
47
+ pagination: new FilterArgs({
48
+ filters: PaginationParamFilter.createSingleField({
49
+ field: 'metadata.fields.1',
50
+ value: selfUser.status?.userID,
51
+ })
52
+ })
53
+ };
54
+
55
+ hashedRequests.steveTokens = this.$store.dispatch(`management/findPage`, { type: EXT.TOKEN, opt });
56
+ } else {
57
+ hashedRequests.steveTokens = this.$store.dispatch('management/findAll', { type: EXT.TOKEN });
58
+ }
59
+ }
38
60
 
39
61
  if (selfUser?.canGetUser && selfUser.status?.userID) {
40
62
  // Fetch the user info for ChangePassword (ChangePasswordDialog needs the user info for the user whose password is being changed)
41
- this.user = await this.$store.dispatch('management/find', {
63
+ hashedRequests.user = this.$store.dispatch('management/find', {
42
64
  type: MANAGEMENT.USER,
43
65
  id: selfUser.status?.userID
44
66
  });
45
- } else {
46
- throw new Error(this.t('changePassword.errors.cannotFetchSelf'));
47
67
  }
68
+
69
+ // Get all settings - the API host setting may not be set, so this avoids a 404 request if we look for the specific setting
70
+ hashedRequests.allSettings = this.$store.dispatch('management/findAll', { type: MANAGEMENT.SETTING });
71
+
72
+ const {
73
+ normanTokens, steveTokens, allSettings, user
74
+ } = await allHash(hashedRequests);
75
+
76
+ this.normanTokens = normanTokens;
77
+ this.steveTokens = steveTokens;
78
+ this.user = user;
79
+
80
+ const apiHostSetting = allSettings.find((i) => i.id === SETTING.API_HOST);
81
+ const serverUrlSetting = allSettings.find((i) => i.id === SETTING.SERVER_URL);
82
+
83
+ this.apiHostSetting = apiHostSetting?.value;
84
+ this.serverUrlSetting = serverUrlSetting?.value;
48
85
  },
49
86
  data() {
50
87
  return {
88
+ normanTokenSchema: undefined,
89
+ steveTokenSchema: undefined,
51
90
  apiHostSetting: null,
52
91
  serverUrlSetting: null,
53
92
  rows: null,
54
93
  canChangePassword: false,
55
- user: null
94
+ user: null,
95
+ normanTokens: null,
96
+ steveTokens: null,
56
97
  };
57
98
  },
58
99
  computed: {
59
100
  ...mapGetters({ t: 'i18n/t' }),
60
101
 
61
102
  apiKeyheaders() {
62
- return this.apiKeySchema ? this.$store.getters['type-map/headersFor'](this.apiKeySchema) : [];
63
- },
64
-
65
- // Port of Ember code for API Url - see: https://github.com/rancher/ui/blob/8e07c492673171731f3b26af14c978bc103d1828/lib/shared/addon/endpoint/service.js#L58
66
- apiUrlBase() {
67
- let setting = this.apiHostSetting;
68
-
69
- if (setting && setting.indexOf('http') !== 0) {
70
- setting = `http://${ setting }`;
71
- }
72
-
73
- // Use Server Setting URL if the api host setting is not set
74
- let url = setting || this.serverUrlSetting;
75
-
76
- // If the URL is relative, add on the current base URL from the browser
77
- if ( url.indexOf('http') !== 0 ) {
78
- url = `${ window.location.origin }/${ url.replace(/^\/+/, '') }`;
79
- }
80
-
81
- // URL must end in a single slash
82
- url = `${ url.replace(/\/+$/, '') }/`;
83
-
84
- return url;
85
- },
86
-
87
- apiUrl() {
88
- const base = this.apiUrlBase;
89
- const path = API_ENDPOINT.replace(/^\/+/, '');
90
-
91
- return `${ base }${ path }`;
92
- },
93
-
94
- apiKeySchema() {
95
- try {
96
- return this.$store.getters[`rancher/schemaFor`](NORMAN.TOKEN);
97
- } catch (e) {}
98
-
99
- return null;
103
+ return [
104
+ EXPIRY_STATE,
105
+ ACCESS_KEY,
106
+ DESCRIPTION,
107
+ SCOPE_NORMAN,
108
+ NORMAN_KEY_DEPRECATION,
109
+ LAST_USED,
110
+ EXPIRES,
111
+ AGE_NORMAN
112
+ ];
100
113
  },
101
114
 
102
115
  principal() {
@@ -110,7 +123,7 @@ export default {
110
123
  return principal || {};
111
124
  },
112
125
 
113
- apiKeys() {
126
+ filteredNormanTokens() {
114
127
  // Filter out tokens that are not API Keys and are not expired UI Sessions
115
128
  const isApiKey = (key) => {
116
129
  const labels = key.labels;
@@ -120,7 +133,24 @@ export default {
120
133
  return ( !expired || !labels || !labels['ui-session'] ) && !current;
121
134
  };
122
135
 
123
- return !this.rows ? [] : this.rows.filter(isApiKey);
136
+ return !this.normanTokens ? [] : this.normanTokens.filter(isApiKey);
137
+ },
138
+
139
+ filteredNewTokens() {
140
+ // Filter out tokens that are not API Keys and are not expired UI Sessions
141
+ const isApiKey = (key) => {
142
+ const labels = key.metadata?.labels;
143
+ const expired = key.status?.expired;
144
+ const current = key.status?.current;
145
+
146
+ return ( !expired || !labels || !labels['ui-session'] ) && !current;
147
+ };
148
+
149
+ return !this.steveTokens ? [] : this.steveTokens.filter(isApiKey);
150
+ },
151
+
152
+ apiKeys() {
153
+ return (this.filteredNormanTokens || []).concat(this.filteredNewTokens || []);
124
154
  }
125
155
  },
126
156
 
@@ -191,16 +221,9 @@ export default {
191
221
  <div class="keys-header">
192
222
  <div>
193
223
  <h2 v-t="'accountAndKeys.apiKeys.title'" />
194
- <div class="api-url">
195
- <span>{{ t("accountAndKeys.apiKeys.apiEndpoint") }}</span>
196
- <CopyToClipboardText
197
- :aria-label="t('accountAndKeys.apiKeys.copyApiEnpoint')"
198
- :text="apiUrl"
199
- />
200
- </div>
201
224
  </div>
202
225
  <button
203
- v-if="apiKeySchema"
226
+ v-if="steveTokenSchema"
204
227
  role="button"
205
228
  :aria-label="t('accountAndKeys.apiKeys.add.label')"
206
229
  class="btn role-primary add mb-20"
@@ -211,11 +234,17 @@ export default {
211
234
  </button>
212
235
  </div>
213
236
  <div
214
- v-if="apiKeySchema"
237
+ v-if="steveTokenSchema"
215
238
  class="keys"
216
239
  >
240
+ <Banner
241
+ v-if="filteredNormanTokens.length"
242
+ color="warning"
243
+ class="mb-20"
244
+ :label="t('accountAndKeys.apiKeys.normanTokenDeprecation')"
245
+ />
217
246
  <ResourceTable
218
- :schema="apiKeySchema"
247
+ :schema="steveTokenSchema"
219
248
  :rows="apiKeys"
220
249
  :headers="apiKeyheaders"
221
250
  key-field="id"
@@ -5,7 +5,6 @@ import CopyToClipboard from '@shell/components/CopyToClipboard';
5
5
  import AsyncButton from '@shell/components/AsyncButton';
6
6
  import { LOGGED_OUT, SETUP } from '@shell/config/query-params';
7
7
  import { NORMAN, MANAGEMENT, EXT } from '@shell/config/types';
8
- import { findBy } from '@shell/utils/array';
9
8
  import { Checkbox } from '@components/Form/Checkbox';
10
9
  import { getVendor, getProduct, setVendor } from '@shell/config/private-label';
11
10
  import { RadioGroup } from '@components/Form/Radio';
@@ -67,7 +66,6 @@ export default {
67
66
  serverUrl: null,
68
67
  mcmEnabled: null,
69
68
  eula: false,
70
- principals: null,
71
69
  errors: []
72
70
  };
73
71
  },
@@ -121,11 +119,7 @@ export default {
121
119
 
122
120
  const productName = plSetting.default;
123
121
 
124
- const principals = await this.$store.dispatch('rancher/findAll', { type: NORMAN.PRINCIPAL, opt: { url: '/v3/principals' } });
125
- const me = findBy(principals, 'me', true);
126
-
127
122
  const current = this.$route.query[SETUP] || this.$store.getters['auth/initialPass'];
128
- const user = this.$store.getters['auth/user'] ?? {};
129
123
 
130
124
  const mcmFeature = await this.$store.dispatch('management/find', {
131
125
  type: MANAGEMENT.FEATURE, id: 'multi-cluster-management', opt: { url: `/v1/${ MANAGEMENT.FEATURE }/multi-cluster-management` }
@@ -144,16 +138,19 @@ export default {
144
138
  const isFirstLogin = await calcIsFirstLogin(this.$store);
145
139
  const mustChangePassword = await calcMustChangePassword(this.$store);
146
140
 
141
+ // user getter must be after "calcMustChangePassword" where all the user info is loaded
142
+ // via the "auth/getUser" action
143
+ const user = this.$store.getters['auth/user'] ?? {};
144
+
147
145
  this['productName'] = productName;
148
146
  this['haveCurrent'] = !!current;
149
- this['username'] = me?.loginName || 'admin';
147
+ this['username'] = user?.username || 'admin';
150
148
  this['isFirstLogin'] = isFirstLogin;
151
149
  this['mustChangePassword'] = mustChangePassword;
152
150
  this['current'] = current;
153
151
  this['user'] = user;
154
152
  this['serverUrl'] = serverUrl;
155
153
  this['mcmEnabled'] = mcmEnabled;
156
- this['principals'] = principals;
157
154
  },
158
155
 
159
156
  computed: {
@@ -181,12 +178,6 @@ export default {
181
178
  return true;
182
179
  },
183
180
 
184
- me() {
185
- const out = findBy(this.principals, 'me', true);
186
-
187
- return out;
188
- },
189
-
190
181
  showLocalhostWarning() {
191
182
  return isLocalhost(this.serverUrl);
192
183
  },