@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.
- package/assets/images/providers/oci-open-containers.svg +22 -0
- package/assets/images/providers/traefik.png +0 -0
- package/assets/styles/themes/_dark.scss +2 -0
- package/assets/styles/themes/_light.scss +2 -0
- package/assets/styles/themes/_modern.scss +6 -0
- package/assets/translations/en-us.yaml +129 -25
- package/components/CruResource.vue +3 -1
- package/components/ExplorerProjectsNamespaces.vue +12 -12
- package/components/IconOrSvg.vue +61 -42
- package/components/Resource/Detail/Card/StatusCard/__tests__/StatusCard.test.ts +109 -0
- package/components/Resource/Detail/Card/StatusCard/index.vue +21 -4
- package/components/Resource/Detail/Metadata/IdentifyingInformation/__tests__/identifying-fields.test.ts +19 -2
- package/components/Resource/Detail/Metadata/IdentifyingInformation/identifying-fields.ts +19 -11
- package/components/Resource/Detail/ResourcePopover/__tests__/index.test.ts +12 -0
- package/components/Resource/Detail/ResourcePopover/index.vue +2 -0
- package/components/Resource/Detail/ResourceRow.vue +2 -2
- package/components/ResourceList/index.vue +7 -4
- package/components/SortableTable/index.vue +2 -2
- package/components/Window/ContainerLogs.vue +48 -37
- package/components/fleet/FleetClusterTargets/TargetsList.vue +2 -2
- package/components/fleet/FleetClusterTargets/index.vue +6 -1
- package/components/fleet/GitRepoAdvancedTab.vue +333 -0
- package/components/fleet/GitRepoMetadataTab.vue +43 -0
- package/components/fleet/GitRepoRepositoryTab.vue +101 -0
- package/components/fleet/GitRepoTargetTab.vue +77 -0
- package/components/fleet/HelmOpAdvancedTab.vue +247 -0
- package/components/fleet/HelmOpChartTab.vue +158 -0
- package/components/fleet/HelmOpMetadataTab.vue +46 -0
- package/components/fleet/HelmOpTargetTab.vue +84 -0
- package/components/fleet/HelmOpValuesTab.vue +147 -0
- package/components/fleet/__tests__/FleetClusterTargets.test.ts +119 -70
- package/components/form/BannerSettings.vue +2 -2
- package/components/form/NodeScheduling.vue +81 -7
- package/components/form/NotificationSettings.vue +2 -2
- package/components/form/PodAffinity.vue +1 -36
- package/components/form/ResourceLabeledSelect.vue +8 -4
- package/components/form/ResourceQuota/Namespace.vue +30 -9
- package/components/form/ResourceQuota/NamespaceRow.vue +25 -7
- package/components/form/ResourceQuota/Project.vue +140 -82
- package/components/form/ResourceQuota/ResourceQuotaEntry.vue +145 -0
- package/components/form/ResourceQuota/__tests__/Namespace.test.ts +307 -0
- package/components/form/ResourceQuota/__tests__/NamespaceRow.test.ts +281 -0
- package/components/form/ResourceQuota/__tests__/Project.test.ts +274 -27
- package/components/form/ResourceQuota/__tests__/ResourceQuotaEntry.test.ts +215 -0
- package/components/form/SchedulingCustomization.vue +14 -6
- package/components/form/SelectOrCreateAuthSecret.vue +107 -18
- package/components/form/__tests__/NodeScheduling.test.ts +12 -9
- package/components/form/__tests__/PodAffinity.test.ts +21 -2
- package/components/form/__tests__/SchedulingCustomization.test.ts +240 -0
- package/components/formatter/ClusterLink.vue +8 -0
- package/components/formatter/SecretOrigin.vue +79 -0
- package/config/labels-annotations.js +7 -6
- package/config/pagination-table-headers.js +6 -4
- package/config/product/explorer.js +1 -11
- package/config/product/manager.js +0 -1
- package/config/query-params.js +3 -0
- package/config/settings.ts +15 -2
- package/config/table-headers.js +21 -17
- package/config/types.js +23 -8
- package/detail/fleet.cattle.io.cluster.vue +1 -1
- package/detail/workload/index.vue +11 -16
- package/dialog/DeactivateDriverDialog.vue +1 -1
- package/dialog/FeatureFlagListDialog.vue +1 -1
- package/dialog/Ipv6NetworkingDialog.vue +156 -0
- package/dialog/ScalePoolDownDialog.vue +2 -2
- package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +1 -1
- package/edit/__tests__/fleet.cattle.io.helmop.test.ts +1 -0
- package/edit/__tests__/management.cattle.io.project.test.js +56 -128
- package/edit/auth/oidc.vue +1 -1
- package/edit/catalog.cattle.io.clusterrepo.vue +155 -25
- package/edit/fleet.cattle.io.gitrepo.vue +153 -283
- package/edit/fleet.cattle.io.helmop.vue +190 -332
- package/edit/management.cattle.io.project.vue +5 -42
- package/edit/management.cattle.io.setting.vue +6 -0
- package/edit/monitoring.coreos.com.alertmanagerconfig/__tests__/auth.spec.ts +145 -0
- package/edit/monitoring.coreos.com.alertmanagerconfig/__tests__/index.test.ts +202 -0
- package/edit/monitoring.coreos.com.alertmanagerconfig/__tests__/tls.spec.ts +226 -0
- package/edit/monitoring.coreos.com.alertmanagerconfig/auth.vue +24 -21
- package/edit/monitoring.coreos.com.alertmanagerconfig/types/__tests__/opsgenie.spec.ts +157 -0
- package/edit/monitoring.coreos.com.alertmanagerconfig/types/__tests__/pagerduty.spec.ts +132 -0
- package/edit/monitoring.coreos.com.alertmanagerconfig/types/__tests__/slack.spec.ts +108 -0
- package/edit/monitoring.coreos.com.alertmanagerconfig/types/pagerduty.vue +2 -1
- package/edit/monitoring.coreos.com.receiver/__tests__/auth.spec.ts +165 -0
- package/edit/monitoring.coreos.com.receiver/__tests__/index.test.ts +153 -0
- package/edit/monitoring.coreos.com.receiver/__tests__/tls.spec.ts +115 -0
- package/edit/monitoring.coreos.com.receiver/types/__tests__/email.spec.ts +86 -0
- package/edit/monitoring.coreos.com.receiver/types/__tests__/opsgenie.spec.ts +209 -0
- package/edit/monitoring.coreos.com.receiver/types/__tests__/pagerduty.spec.ts +105 -0
- package/edit/monitoring.coreos.com.receiver/types/__tests__/slack.spec.ts +92 -0
- package/edit/monitoring.coreos.com.receiver/types/__tests__/webhook.spec.ts +131 -0
- package/edit/provisioning.cattle.io.cluster/__tests__/Basics.test.ts +55 -24
- package/edit/provisioning.cattle.io.cluster/__tests__/Networking.test.ts +1 -103
- package/edit/provisioning.cattle.io.cluster/__tests__/index.test.ts +13 -1
- package/edit/provisioning.cattle.io.cluster/__tests__/rke2-fleet-cluster-agent.test.ts +283 -0
- package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +65 -49
- package/edit/provisioning.cattle.io.cluster/ingress/IngressCards.vue +114 -0
- package/edit/provisioning.cattle.io.cluster/ingress/IngressConfiguration.vue +158 -0
- package/edit/provisioning.cattle.io.cluster/rke2.vue +167 -69
- package/edit/provisioning.cattle.io.cluster/shared.ts +36 -1
- package/edit/provisioning.cattle.io.cluster/tabs/AgentConfiguration.vue +2 -1
- package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +70 -7
- package/edit/provisioning.cattle.io.cluster/tabs/Ingress.vue +343 -0
- package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +2 -1
- package/edit/provisioning.cattle.io.cluster/tabs/etcd/__tests__/S3Config.test.ts +13 -1
- package/edit/provisioning.cattle.io.cluster/tabs/networking/index.vue +10 -44
- package/edit/secret/index.vue +1 -1
- package/edit/token.vue +68 -29
- package/edit/workload/__tests__/index.test.ts +2 -37
- package/edit/workload/index.vue +6 -2
- package/edit/workload/mixins/workload.js +0 -32
- package/list/__tests__/management.cattle.io.setting.test.ts +198 -0
- package/list/management.cattle.io.setting.vue +13 -0
- package/list/provisioning.cattle.io.cluster.vue +50 -1
- package/list/secret.vue +4 -9
- package/list/service.vue +6 -8
- package/machine-config/amazonec2.vue +11 -4
- package/machine-config/components/EC2Networking.vue +46 -30
- package/machine-config/components/__tests__/EC2Networking.test.ts +7 -7
- package/machine-config/components/__tests__/utils/vpcSubnetMockData.js +0 -9
- package/machine-config/digitalocean.vue +3 -3
- package/models/__tests__/chart.test.ts +2 -2
- package/models/__tests__/namespace.test.ts +11 -0
- package/models/__tests__/provisioning.cattle.io.cluster.test.ts +96 -0
- package/models/__tests__/workload.test.ts +42 -1
- package/models/catalog.cattle.io.clusterrepo.js +30 -4
- package/models/chart.js +3 -3
- package/models/ext.cattle.io.token.js +48 -0
- package/models/kontainerdriver.js +2 -2
- package/models/namespace.js +7 -1
- package/models/nodedriver.js +2 -2
- package/models/provisioning.cattle.io.cluster.js +28 -7
- package/models/secret.js +0 -17
- package/models/service.js +44 -1
- package/models/token.js +4 -0
- package/models/workload.js +12 -6
- package/package.json +1 -1
- package/pages/account/index.vue +96 -67
- package/pages/auth/setup.vue +5 -14
- package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +45 -18
- package/pages/c/_cluster/apps/charts/__tests__/install.test.ts +4 -1
- package/pages/c/_cluster/apps/charts/index.vue +82 -3
- package/pages/c/_cluster/apps/charts/install.vue +317 -42
- package/pages/c/_cluster/explorer/tools/index.vue +1 -1
- package/pages/c/_cluster/manager/cloudCredential/index.vue +1 -1
- package/pages/c/_cluster/manager/drivers/kontainerDriver/index.vue +5 -4
- package/pages/c/_cluster/settings/index.vue +3 -1
- package/pages/c/_cluster/uiplugins/index.vue +1 -1
- package/plugins/dashboard-store/__tests__/getters.test.ts +108 -0
- package/plugins/dashboard-store/__tests__/resource-class.test.ts +27 -0
- package/plugins/dashboard-store/actions.js +3 -8
- package/plugins/dashboard-store/getters.js +7 -5
- package/plugins/dashboard-store/mutations.js +4 -1
- package/plugins/dashboard-store/resource-class.js +3 -3
- package/plugins/steve/__tests__/steve-class.test.ts +102 -141
- package/plugins/steve/steve-class.js +12 -3
- package/plugins/steve/steve-pagination-utils.ts +6 -2
- package/rancher-components/RcIcon/types.ts +2 -0
- package/rancher-components/RcItemCard/RcItemCard.vue +72 -20
- package/store/prefs.js +3 -0
- package/types/aws-sdk.d.ts +121 -0
- package/types/resources/node.ts +15 -0
- package/types/shell/index.d.ts +537 -506
- package/types/store/pagination.types.ts +5 -5
- package/utils/__tests__/array.test.ts +1 -29
- package/utils/__tests__/cluster-agent-configuration.test.ts +203 -0
- package/utils/array.ts +0 -11
- package/utils/aws.ts +21 -0
- package/utils/cluster.js +22 -2
- package/utils/selector-typed.ts +1 -1
- package/utils/svg-filter.js +4 -3
- package/components/__tests__/ProjectRow.test.ts +0 -206
- 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/${
|
|
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/${
|
|
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 });
|
package/models/namespace.js
CHANGED
|
@@ -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
|
-
|
|
274
|
+
const currentProduct = this.$rootGetters['currentProduct'];
|
|
275
|
+
|
|
276
|
+
return currentProduct ? !!currentProduct.hideNamespaceLocation : true;
|
|
271
277
|
}
|
|
272
278
|
|
|
273
279
|
get glance() {
|
package/models/nodedriver.js
CHANGED
|
@@ -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/${
|
|
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/${
|
|
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
|
|
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 &&
|
|
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
package/models/workload.js
CHANGED
|
@@ -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)
|
|
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:
|
|
768
|
-
resources:
|
|
769
|
-
showScaling:
|
|
770
|
-
onIncrease:
|
|
771
|
-
onDecrease:
|
|
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
package/pages/account/index.vue
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
32
|
+
this.normanTokenSchema = this.$store.getters[`rancher/schemaFor`](NORMAN.TOKEN);
|
|
33
|
+
this.steveTokenSchema = this.$store.getters[`management/schemaFor`](EXT.TOKEN);
|
|
28
34
|
|
|
29
|
-
|
|
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.
|
|
35
|
-
|
|
37
|
+
if (this.normanTokenSchema) {
|
|
38
|
+
hashedRequests.normanTokens = this.$store.dispatch('rancher/findAll', { type: NORMAN.TOKEN });
|
|
39
|
+
}
|
|
36
40
|
|
|
37
|
-
|
|
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
|
-
|
|
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
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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
|
-
|
|
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.
|
|
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="
|
|
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="
|
|
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="
|
|
247
|
+
:schema="steveTokenSchema"
|
|
219
248
|
:rows="apiKeys"
|
|
220
249
|
:headers="apiKeyheaders"
|
|
221
250
|
key-field="id"
|
package/pages/auth/setup.vue
CHANGED
|
@@ -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'] =
|
|
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
|
},
|