@rancher/shell 0.3.24 → 0.3.25
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/styles/themes/_light.scss +1 -1
- package/assets/translations/en-us.yaml +29 -7
- package/assets/translations/zh-hans.yaml +1 -1
- package/components/ClusterIconMenu.vue +143 -0
- package/components/CruResource.vue +7 -1
- package/components/ExplorerProjectsNamespaces.vue +11 -1
- package/components/FixedBanner.vue +17 -1
- package/components/Markdown.vue +1 -1
- package/components/Questions/__tests__/Yaml.test.ts +3 -2
- package/components/SortableTable/index.vue +3 -2
- package/components/auth/RoleDetailEdit.vue +15 -2
- package/components/auth/login/saml.vue +12 -1
- package/components/form/LabeledSelect.vue +12 -5
- package/components/form/Members/ClusterPermissionsEditor.vue +1 -1
- package/components/form/Members/MembershipEditor.vue +6 -1
- package/components/form/__tests__/KeyValue.test.ts +6 -3
- package/components/form/__tests__/LabeledSelect.test.ts +18 -0
- package/components/formatter/PodsUsage.vue +11 -36
- package/components/formatter/PrincipalGroupBindings.vue +8 -5
- package/components/formatter/__tests__/PodsUsage.test.ts +36 -19
- package/components/nav/Group.vue +25 -27
- package/components/nav/Header.vue +12 -5
- package/components/nav/Pinned.vue +47 -0
- package/components/nav/TopLevelMenu.vue +233 -60
- package/components/nav/Type.vue +57 -3
- package/config/home-links.js +1 -1
- package/config/product/istio.js +15 -5
- package/config/router.js +3 -9
- package/config/table-headers.js +5 -6
- package/config/uiplugins.js +1 -0
- package/core/plugin-helpers.js +3 -0
- package/core/types.ts +6 -1
- package/creators/app/files/.vscode/settings.json +0 -1
- package/detail/__tests__/autoscaling.horizontalpodautoscaler.test.ts +118 -0
- package/detail/autoscaling.horizontalpodautoscaler/index.vue +4 -4
- package/detail/provisioning.cattle.io.cluster.vue +7 -5
- package/edit/__tests__/management.cattle.io.clusterroletemplatebinding.test.ts +58 -0
- package/edit/__tests__/namespace.test.ts +5 -3
- package/edit/management.cattle.io.clusterroletemplatebinding.vue +3 -11
- package/edit/namespace.vue +8 -4
- package/edit/provisioning.cattle.io.cluster/Basics.vue +662 -0
- package/edit/provisioning.cattle.io.cluster/CustomCommand.vue +6 -0
- package/edit/provisioning.cattle.io.cluster/DrainOptions.vue +13 -8
- package/edit/provisioning.cattle.io.cluster/MachinePool.vue +11 -2
- package/edit/provisioning.cattle.io.cluster/MemberRoles.vue +40 -0
- package/edit/provisioning.cattle.io.cluster/__tests__/Basics.tests.ts +237 -0
- package/edit/provisioning.cattle.io.cluster/__tests__/CustomCommand.tests.ts +71 -23
- package/edit/provisioning.cattle.io.cluster/__tests__/DrainOptions.test.ts +52 -0
- package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +65 -142
- package/edit/provisioning.cattle.io.cluster/rke2.vue +194 -598
- package/edit/workload/storage/__tests__/Storage.test.ts +2 -2
- package/edit/workload/storage/persistentVolumeClaim/__tests__/persistentvolumeclaim.test.ts +36 -0
- package/edit/workload/storage/persistentVolumeClaim/persistentvolumeclaim.vue +15 -7
- package/initialize/index.js +5 -5
- package/layouts/default.vue +6 -6
- package/layouts/home.vue +6 -2
- package/layouts/plain.vue +9 -2
- package/list/fleet.cattle.io.cluster.vue +2 -2
- package/list/management.cattle.io.feature.vue +1 -1
- package/machine-config/vmwarevsphere.vue +48 -7
- package/mixins/brand.js +0 -8
- package/mixins/child-hook.js +2 -2
- package/mixins/create-edit-view/impl.js +3 -3
- package/models/__tests__/management.cattle.io.node.ts +96 -0
- package/models/__tests__/node.ts +74 -0
- package/models/cluster/node.js +6 -5
- package/models/cluster.x-k8s.io.machinedeployment.js +2 -2
- package/models/management.cattle.io.cluster.js +22 -1
- package/models/management.cattle.io.clusterroletemplatebinding.js +3 -3
- package/models/management.cattle.io.globalrole.js +17 -2
- package/models/management.cattle.io.node.js +6 -4
- package/models/management.cattle.io.projectroletemplatebinding.js +3 -3
- package/models/management.cattle.io.roletemplate.js +17 -2
- package/package.json +2 -6
- package/pages/about.vue +2 -0
- package/pages/auth/setup.vue +5 -4
- package/pages/c/_cluster/monitoring/index.vue +8 -3
- package/pages/c/_cluster/uiplugins/CatalogList/CatalogLoadDialog.vue +9 -66
- package/pages/c/_cluster/uiplugins/CatalogList/CatalogUninstallDialog.vue +182 -0
- package/pages/c/_cluster/uiplugins/CatalogList/index.vue +15 -32
- package/pages/c/_cluster/uiplugins/UninstallDialog.vue +8 -46
- package/pages/c/_cluster/uiplugins/index.vue +64 -64
- package/pages/diagnostic.vue +0 -39
- package/pages/home.vue +1 -1
- package/plugins/dashboard-store/normalize.js +4 -4
- package/plugins/int-number.js +5 -2
- package/plugins/positive-int-number.js +19 -0
- package/plugins/steve/__tests__/getters.spec.ts +15 -0
- package/plugins/steve/getters.js +22 -10
- package/rancher-components/Form/LabeledInput/LabeledInput.vue +0 -8
- package/rancher-components/Form/Radio/RadioButton.test.ts +3 -7
- package/store/index.js +4 -0
- package/store/prefs.js +1 -0
- package/types/shell/index.d.ts +13 -4
- package/utils/__tests__/cluster.test.ts +55 -0
- package/utils/__tests__/object.test.ts +21 -2
- package/utils/cluster.js +47 -1
- package/utils/object.js +12 -5
- package/utils/validators/formRules/__tests__/index.test.ts +13 -1
- package/utils/validators/formRules/index.ts +4 -0
- package/utils/validators/role-template.js +9 -1
- package/utils/version.js +1 -1
- package/yarn-error.log +16 -16
- package/components/ClusterProviderIconMenu.vue +0 -161
- package/content/docs/en-us/getting-started.md +0 -224
- package/content/docs/en-us/whats-new.md +0 -29
- package/content/docs/zh-hans/getting-started.md +0 -224
- package/content/docs/zh-hans/whats-new.md +0 -28
- package/pages/docs/_doc.vue +0 -345
- package/pages/docs/toc.js +0 -27
- package/plugins/console.js +0 -34
|
@@ -320,7 +320,7 @@ BODY, .theme-light {
|
|
|
320
320
|
--header-height : 55px;
|
|
321
321
|
--header-border : #{$medium};
|
|
322
322
|
--header-border-size : 1px;
|
|
323
|
-
--nav-width :
|
|
323
|
+
--nav-width : 250px;
|
|
324
324
|
--nav-bg : #{$lightest};
|
|
325
325
|
--nav-active : #{$light};
|
|
326
326
|
--nav-hover : #{$medium};
|
|
@@ -760,7 +760,7 @@ backupRestoreOperator:
|
|
|
760
760
|
warning: 'This {type} does not have its reclaim policy set to "Retain". Your backups may be lost if the volume is changed or becomes unbound.'
|
|
761
761
|
encryption: Encryption
|
|
762
762
|
encryptionConfigName:
|
|
763
|
-
backuptip: 'Any secret in the <code>cattle-
|
|
763
|
+
backuptip: 'Any secret in the <code>cattle-resources-system</code> namespace that has an <code>encryption-provider-config.yaml</code> key. <br/>The contents of this file are necessary to perform a restore from this backup, and are not stored by Rancher Backup.'
|
|
764
764
|
label: Encryption Config Secret
|
|
765
765
|
options:
|
|
766
766
|
none: Store the contents of the backup unencrypted
|
|
@@ -1137,6 +1137,7 @@ cluster:
|
|
|
1137
1137
|
nodeRole:
|
|
1138
1138
|
label: Node Role
|
|
1139
1139
|
detail: Choose what roles the node will have in the cluster. The cluster needs to have at least one node with each role.
|
|
1140
|
+
warning: The cluster needs to have at least one node with each role to be usable.
|
|
1140
1141
|
advanced:
|
|
1141
1142
|
label: Advanced
|
|
1142
1143
|
detail: Additional control over how the node will be registered. These values will often need to be different for each node registered.
|
|
@@ -1672,6 +1673,11 @@ cluster:
|
|
|
1672
1673
|
haveArgInfo: Configuration information is not available for the selected Kubernetes version. The options available on this screen will be limited; you may want to use the YAML editor.
|
|
1673
1674
|
deprecatedPsp: Pod Security Policies are deprecated as of Kubernetes v1.21, and have been removed in Kubernetes v1.25.
|
|
1674
1675
|
removedPsp: Pod Security Policies have been removed in Kubernetes v1.25. Use Pod Security Admission instead.
|
|
1676
|
+
machinePoolError: |-
|
|
1677
|
+
{count, plural,
|
|
1678
|
+
=1 { {pool_name}: The provided value for {fields} was not found in the list of expected values. This can happen with clusters provisioned outside of Rancher or when options for the provider have changed. }
|
|
1679
|
+
other { {pool_name}: The provided values for {fields} were not found in the list of expected values. This can happen with clusters provisioned outside of Rancher or when options for the provider have changed. }
|
|
1680
|
+
}
|
|
1675
1681
|
rkeTemplateUpgrade: Template revision {name} available for upgrade
|
|
1676
1682
|
|
|
1677
1683
|
availabilityWarnings:
|
|
@@ -1752,6 +1758,7 @@ cluster:
|
|
|
1752
1758
|
searchPlaceholder: Start typing to search
|
|
1753
1759
|
noResults: No results found
|
|
1754
1760
|
privateRegistry:
|
|
1761
|
+
header: Registry for Rancher System Container Images
|
|
1755
1762
|
label: Enable cluster scoped container registry for Rancher system container images
|
|
1756
1763
|
description: "If enabled, Rancher will pull container images from this registry during cluster provisioning. By default, Rancher will also use this registry when installing Rancher's official Helm chart apps. If the cluster scoped registry is disabled, system images are pulled from the System Default Registry in the global settings."
|
|
1757
1764
|
docsLinkRke2: "For help configuring private registry mirrors, see the RKE2 <a href=\"https://docs.rke2.io/install/containerd_registry_configuration/\" target=\"_blank\">documentation.</a>"
|
|
@@ -1864,6 +1871,8 @@ cluster:
|
|
|
1864
1871
|
option:
|
|
1865
1872
|
none: (None)
|
|
1866
1873
|
default: Default - RKE2 Embedded
|
|
1874
|
+
secretEncryption:
|
|
1875
|
+
label: Encrypt Secrets
|
|
1867
1876
|
cisProfile:
|
|
1868
1877
|
option: (None)
|
|
1869
1878
|
enableNetworkPolicy:
|
|
@@ -1872,6 +1881,7 @@ cluster:
|
|
|
1872
1881
|
workNode:
|
|
1873
1882
|
label: Worker Nodes
|
|
1874
1883
|
controlPlaneConcurrency:
|
|
1884
|
+
header: Control Plane
|
|
1875
1885
|
label: Control Plane Concurrency
|
|
1876
1886
|
toolTip: "This can be either a fixed number of nodes (e.g. 1) at a time or a percentage (e.g. 10%)"
|
|
1877
1887
|
workerConcurrency:
|
|
@@ -1879,8 +1889,20 @@ cluster:
|
|
|
1879
1889
|
toolTip: "This can be either a fixed number of nodes (e.g. 1) at a time or a percentage (e.g. 10%)"
|
|
1880
1890
|
drain:
|
|
1881
1891
|
label: Drain Nodes
|
|
1882
|
-
toolTip: Draining preemptively removes the pods on each node so there are no running workloads on the nodes being upgraded. Upgrading without draining is faster and causes less shuffling around, but pods may still be restarted depending on the upgrade being performed.
|
|
1883
|
-
|
|
1892
|
+
toolTip: Draining preemptively removes the pods on each node so there are no running workloads on the nodes being upgraded. Upgrading without draining is faster and causes less shuffling around, but pods may still be restarted depending on the upgrade being performed.
|
|
1893
|
+
deleteEmptyDir:
|
|
1894
|
+
warning: "By default, pods using emptyDir volumes will be deleted on upgrade. Operations reliant on emptyDir volumes persisting through the pod's lifecycle may be impacted."
|
|
1895
|
+
label: Delete pods using emptyDir volumes
|
|
1896
|
+
tooltip: emptyDir volumes are often used for ephemeral data, but the data will be permanently deleted. Draining will fail if this is not set and there are pods using emptyDir.
|
|
1897
|
+
force:
|
|
1898
|
+
label: Delete standalone pods
|
|
1899
|
+
tooltip: Delete standalone pods which are not managed by a Workload controller (Deployment, Job, etc). Draining will fail if this is not set and there are standalone pods.
|
|
1900
|
+
gracePeriod:
|
|
1901
|
+
checkboxLabel: Override pod termination grace periods
|
|
1902
|
+
inputLabel: Grace Period
|
|
1903
|
+
timeout:
|
|
1904
|
+
checkboxLabel: Timeout after
|
|
1905
|
+
inputLabel: Drain Timeout
|
|
1884
1906
|
truncateHostnames: Truncate hostnames to 15 characters for NetBIOS compatibility.
|
|
1885
1907
|
address:
|
|
1886
1908
|
tooltip: Cluster networking values cannot be changed after the cluster is created.
|
|
@@ -4124,9 +4146,8 @@ plugins:
|
|
|
4124
4146
|
image:
|
|
4125
4147
|
name: images
|
|
4126
4148
|
label: Deployment Image
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
label: Cache State
|
|
4149
|
+
repository:
|
|
4150
|
+
label: Repository Name
|
|
4130
4151
|
tabs:
|
|
4131
4152
|
all: All
|
|
4132
4153
|
available: Available
|
|
@@ -4153,7 +4174,7 @@ plugins:
|
|
|
4153
4174
|
label: Uninstall
|
|
4154
4175
|
title: "Uninstall Extension: {name}"
|
|
4155
4176
|
prompt: "Are you sure that you want to uninstall this Extension?"
|
|
4156
|
-
|
|
4177
|
+
catalog: "Are you sure that you want to uninstall this Extension Catalog Image? This will also remove any Extensions provided by this image."
|
|
4157
4178
|
upgradeAvailable: A newer version of this Extension is available
|
|
4158
4179
|
reload: Extensions changed - reload required
|
|
4159
4180
|
safeMode:
|
|
@@ -5704,6 +5725,7 @@ validation:
|
|
|
5704
5725
|
missingResource: You must specify a Resource for each resource grant
|
|
5705
5726
|
missingApiGroup: You must specify an API Group for each resource grant
|
|
5706
5727
|
missingOneResource: You must specify at least one Resource, Non-Resource URL or API Group for each resource grant
|
|
5728
|
+
noResourceAndNonResource: Each rule may contain Resources or Non-Resource URLs but not both
|
|
5707
5729
|
service:
|
|
5708
5730
|
externalName:
|
|
5709
5731
|
none: External Name is required on an ExternalName Service.
|
|
@@ -758,7 +758,7 @@ backupRestoreOperator:
|
|
|
758
758
|
warning: '此 {type} 没有将其回收策略设置为 "保留"。如果卷被更改或未绑定,你的备份可能会丢失。'
|
|
759
759
|
encryption: 加密
|
|
760
760
|
encryptionConfigName:
|
|
761
|
-
backuptip: '<code>cattle-
|
|
761
|
+
backuptip: '<code>cattle-resources-system</code>命名空间中具有<code>encryption-provider-config.yaml</code>密钥的任何密文。<br/>此文件的内容是还原此备份所必需的,Rancher Backup 不会存储这些内容。'
|
|
762
762
|
label: 加密配置密文
|
|
763
763
|
options:
|
|
764
764
|
none: 存储未加密的备份内容。
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { abbreviateClusterName } from '@shell/utils/cluster';
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
props: {
|
|
6
|
+
cluster: {
|
|
7
|
+
type: Object,
|
|
8
|
+
required: true,
|
|
9
|
+
},
|
|
10
|
+
},
|
|
11
|
+
computed: {
|
|
12
|
+
isEnabled() {
|
|
13
|
+
return !!this.cluster?.ready;
|
|
14
|
+
},
|
|
15
|
+
showLocalIcon() {
|
|
16
|
+
return this.cluster.isLocal && !this.cluster.isHarvester && !this.cluster.badge?.iconText;
|
|
17
|
+
},
|
|
18
|
+
badgeLogoBorderBottom() {
|
|
19
|
+
const color = this.cluster.badge?.color;
|
|
20
|
+
|
|
21
|
+
return color ? `4px solid ${ color }` : '';
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
methods: {
|
|
25
|
+
smallIdentifier(input) {
|
|
26
|
+
if (this.cluster.badge?.iconText) {
|
|
27
|
+
return this.cluster.badge?.iconText;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (this.cluster.isLocal && !this.cluster.badge?.iconText) {
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return abbreviateClusterName(input);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
</script>
|
|
39
|
+
|
|
40
|
+
<template>
|
|
41
|
+
<div
|
|
42
|
+
v-if="cluster"
|
|
43
|
+
class="cluster-icon-menu"
|
|
44
|
+
>
|
|
45
|
+
<div
|
|
46
|
+
class="cluster-badge-logo"
|
|
47
|
+
:class="{ 'disabled': !isEnabled }"
|
|
48
|
+
:style="{ borderBottom: badgeLogoBorderBottom }"
|
|
49
|
+
>
|
|
50
|
+
<span
|
|
51
|
+
class="cluster-badge-logo-text"
|
|
52
|
+
>
|
|
53
|
+
{{ smallIdentifier(cluster.label) }}
|
|
54
|
+
</span>
|
|
55
|
+
<svg
|
|
56
|
+
v-if="showLocalIcon"
|
|
57
|
+
class="cluster-local-logo"
|
|
58
|
+
version="1.1"
|
|
59
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
60
|
+
xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
61
|
+
x="0px"
|
|
62
|
+
y="0px"
|
|
63
|
+
viewBox="0 0 100 100"
|
|
64
|
+
style="enable-background:new 0 0 100 100;"
|
|
65
|
+
xml:space="preserve"
|
|
66
|
+
>
|
|
67
|
+
<g>
|
|
68
|
+
<g>
|
|
69
|
+
<path
|
|
70
|
+
class="rancher-icon-fill"
|
|
71
|
+
d="M26.0862026,44.4953918H8.6165142c-5.5818157,0-9.3979139-4.6252708-8.4802637-10.1311035l2.858391-17.210701
|
|
72
|
+
C3.912292,11.6477556,6.1382647,7.1128125,7.8419709,7.1128125s3.1788611,4.5368752,3.1788611,10.1186218v4.4837742
|
|
73
|
+
c0,5.5817471,4.4044495,9.5409164,9.9862652,9.5409164h5.0791054V44.4953918z"
|
|
74
|
+
/>
|
|
75
|
+
</g>
|
|
76
|
+
<path
|
|
77
|
+
class="rancher-icon-fill"
|
|
78
|
+
d="M63.0214729,92.8871841H37.0862045c-6.0751343,0-11.0000019-4.9248657-11.0000019-11V30.3864384
|
|
79
|
+
c0-6.0751324,4.9248676-11,11.0000019-11h25.9352684c6.0751305,0,11.0000038,4.9248676,11.0000038,11v51.5007477
|
|
80
|
+
C74.0214767,87.9623184,69.0966034,92.8871841,63.0214729,92.8871841z"
|
|
81
|
+
/>
|
|
82
|
+
<g>
|
|
83
|
+
<path
|
|
84
|
+
class="rancher-icon-fill"
|
|
85
|
+
d="M73.9137955,44.4953918h17.4696884c5.5818176,0,9.3979187-4.6252708,8.4802628-10.1311035
|
|
86
|
+
l-2.8583908-17.210701c-0.9176483-5.5058317-3.1436234-10.0407753-4.8473282-10.0407753
|
|
87
|
+
s-3.1788635,4.5368752-3.1788635,10.1186218v4.4837742c0,5.5817471-4.4044418,9.5409164-9.9862595,9.5409164h-5.0791092
|
|
88
|
+
V44.4953918z"
|
|
89
|
+
/>
|
|
90
|
+
</g>
|
|
91
|
+
</g>
|
|
92
|
+
</svg>
|
|
93
|
+
</div>
|
|
94
|
+
<i
|
|
95
|
+
v-if="cluster.pinned"
|
|
96
|
+
class="icon icon-pin cluster-pin-icon"
|
|
97
|
+
/>
|
|
98
|
+
</div>
|
|
99
|
+
</template>
|
|
100
|
+
|
|
101
|
+
<style lang="scss" scoped>
|
|
102
|
+
|
|
103
|
+
.cluster-icon-menu {
|
|
104
|
+
position: relative;
|
|
105
|
+
align-items: center;
|
|
106
|
+
display: flex;
|
|
107
|
+
height: 28px;
|
|
108
|
+
justify-content: center;
|
|
109
|
+
width: 42px;
|
|
110
|
+
}
|
|
111
|
+
.cluster-pin-icon {
|
|
112
|
+
position: absolute;
|
|
113
|
+
top: -6px;
|
|
114
|
+
right: -4px;
|
|
115
|
+
font-size: 12px;
|
|
116
|
+
transform: scaleX(-1);
|
|
117
|
+
color: var(--body-text);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.cluster-local-logo {
|
|
121
|
+
width: 20px;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.cluster-badge-logo {
|
|
125
|
+
width: 42px;
|
|
126
|
+
height: 28px;
|
|
127
|
+
display: flex;
|
|
128
|
+
align-items: center;
|
|
129
|
+
justify-content: center;
|
|
130
|
+
color: var(--default-active-text);
|
|
131
|
+
font-weight: bold;
|
|
132
|
+
background: var(--nav-icon-badge-bg);
|
|
133
|
+
border: 1px solid var(--default-border);
|
|
134
|
+
border-radius: 5px;
|
|
135
|
+
font-size: 12px;
|
|
136
|
+
text-transform: uppercase;
|
|
137
|
+
|
|
138
|
+
&.disabled {
|
|
139
|
+
filter: grayscale(1);
|
|
140
|
+
color: var(--muted);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
</style>
|
|
@@ -17,6 +17,8 @@ import {
|
|
|
17
17
|
import { BEFORE_SAVE_HOOKS } from '@shell/mixins/child-hook';
|
|
18
18
|
import Wizard from '@shell/components/Wizard';
|
|
19
19
|
|
|
20
|
+
export const CONTEXT_HOOK_EDIT_YAML = 'show-preview-yaml';
|
|
21
|
+
|
|
20
22
|
export default {
|
|
21
23
|
|
|
22
24
|
name: 'CruResource',
|
|
@@ -110,6 +112,7 @@ export default {
|
|
|
110
112
|
type: Function,
|
|
111
113
|
default: null,
|
|
112
114
|
},
|
|
115
|
+
|
|
113
116
|
steps: {
|
|
114
117
|
type: Array,
|
|
115
118
|
default: () => []
|
|
@@ -324,7 +327,10 @@ export default {
|
|
|
324
327
|
|
|
325
328
|
async showPreviewYaml() {
|
|
326
329
|
if ( this.applyHooks ) {
|
|
327
|
-
await this.applyHooks(
|
|
330
|
+
await this.applyHooks(
|
|
331
|
+
BEFORE_SAVE_HOOKS,
|
|
332
|
+
CONTEXT_HOOK_EDIT_YAML,
|
|
333
|
+
);
|
|
328
334
|
}
|
|
329
335
|
|
|
330
336
|
const resourceYaml = this.createResourceYaml(this.yamlModifiers);
|
|
@@ -5,6 +5,8 @@ import { STATE, AGE, NAME } from '@shell/config/table-headers';
|
|
|
5
5
|
import { uniq } from '@shell/utils/array';
|
|
6
6
|
import { MANAGEMENT, NAMESPACE, VIRTUAL_TYPES } from '@shell/config/types';
|
|
7
7
|
import { PROJECT_ID, FLAT_VIEW } from '@shell/config/query-params';
|
|
8
|
+
import { PanelLocation, ExtensionPoint } from '@shell/core/types';
|
|
9
|
+
import ExtensionPanel from '@shell/components/ExtensionPanel';
|
|
8
10
|
import Masthead from '@shell/components/ResourceList/Masthead';
|
|
9
11
|
import { mapPref, GROUP_RESOURCES, ALL_NAMESPACES } from '@shell/store/prefs';
|
|
10
12
|
import MoveModal from '@shell/components/MoveModal';
|
|
@@ -16,7 +18,7 @@ import DOMPurify from 'dompurify';
|
|
|
16
18
|
export default {
|
|
17
19
|
name: 'ListProjectNamespace',
|
|
18
20
|
components: {
|
|
19
|
-
Masthead, MoveModal, ResourceTable
|
|
21
|
+
ExtensionPanel, Masthead, MoveModal, ResourceTable
|
|
20
22
|
},
|
|
21
23
|
mixins: [ResourceFetch],
|
|
22
24
|
|
|
@@ -56,6 +58,8 @@ export default {
|
|
|
56
58
|
schema: null,
|
|
57
59
|
projects: [],
|
|
58
60
|
projectSchema: null,
|
|
61
|
+
extensionType: ExtensionPoint.PANEL,
|
|
62
|
+
extensionLocation: PanelLocation.RESOURCE_LIST,
|
|
59
63
|
MANAGEMENT,
|
|
60
64
|
VIRTUAL_TYPES,
|
|
61
65
|
defaultCreateProjectLocation: {
|
|
@@ -379,6 +383,12 @@ export default {
|
|
|
379
383
|
</n-link>
|
|
380
384
|
</template>
|
|
381
385
|
</Masthead>
|
|
386
|
+
<!-- Extensions area -->
|
|
387
|
+
<ExtensionPanel
|
|
388
|
+
:resource="{}"
|
|
389
|
+
:type="extensionType"
|
|
390
|
+
:location="extensionLocation"
|
|
391
|
+
/>
|
|
382
392
|
<ResourceTable
|
|
383
393
|
ref="table"
|
|
384
394
|
class="table"
|
|
@@ -93,6 +93,19 @@ export default {
|
|
|
93
93
|
},
|
|
94
94
|
showAsDialog() {
|
|
95
95
|
return this.consent && !!this.banner.button;
|
|
96
|
+
},
|
|
97
|
+
|
|
98
|
+
// ID to place on the Banner DIV
|
|
99
|
+
id() {
|
|
100
|
+
if (this.header) {
|
|
101
|
+
return 'banner-header';
|
|
102
|
+
} else if (this.consent) {
|
|
103
|
+
return 'banner-consent';
|
|
104
|
+
} else if (this.footer) {
|
|
105
|
+
return 'banner-footer';
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return 'banner';
|
|
96
109
|
}
|
|
97
110
|
},
|
|
98
111
|
|
|
@@ -130,7 +143,10 @@ export default {
|
|
|
130
143
|
</script>
|
|
131
144
|
|
|
132
145
|
<template>
|
|
133
|
-
<div
|
|
146
|
+
<div
|
|
147
|
+
v-if="showBanner"
|
|
148
|
+
:id="id"
|
|
149
|
+
>
|
|
134
150
|
<div
|
|
135
151
|
v-if="!showAsDialog"
|
|
136
152
|
class="banner"
|
package/components/Markdown.vue
CHANGED
|
@@ -39,7 +39,7 @@ export default {
|
|
|
39
39
|
const renderer = new marked.Renderer();
|
|
40
40
|
const linkRenderer = renderer.link;
|
|
41
41
|
|
|
42
|
-
const base = this.$router.resolve(this.$route).href.replace(/#.*$/, '');
|
|
42
|
+
const base = this.$router ? this.$router.resolve(this.$route).href.replace(/#.*$/, '') : '';
|
|
43
43
|
|
|
44
44
|
renderer.link = function(href, title, text) {
|
|
45
45
|
let external = true;
|
|
@@ -2,8 +2,9 @@ import Questions from '@shell/components/Questions';
|
|
|
2
2
|
import { mount } from '@vue/test-utils';
|
|
3
3
|
import { _EDIT } from '@shell/config/query-params';
|
|
4
4
|
const defaultStubs = {
|
|
5
|
-
Tab:
|
|
6
|
-
Tabbed:
|
|
5
|
+
Tab: true,
|
|
6
|
+
Tabbed: true,
|
|
7
|
+
CodeMirror: true
|
|
7
8
|
};
|
|
8
9
|
const defaultGetters = {
|
|
9
10
|
currentStore: () => 'current_store',
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
import { mapGetters } from 'vuex';
|
|
3
3
|
import day from 'dayjs';
|
|
4
|
+
import isEmpty from 'lodash/isEmpty';
|
|
4
5
|
import { dasherize, ucFirst } from '@shell/utils/string';
|
|
5
6
|
import { get, clone } from '@shell/utils/object';
|
|
6
7
|
import { removeObject } from '@shell/utils/array';
|
|
@@ -801,11 +802,11 @@ export default {
|
|
|
801
802
|
|
|
802
803
|
// Can the action of interest be applied to the specified resource?
|
|
803
804
|
canRunBulkActionOfInterest(resource) {
|
|
804
|
-
if (!this.actionOfInterest) {
|
|
805
|
+
if ( !this.actionOfInterest || isEmpty(resource?.availableActions) ) {
|
|
805
806
|
return false;
|
|
806
807
|
}
|
|
807
808
|
|
|
808
|
-
const matchingResourceAction = resource.availableActions
|
|
809
|
+
const matchingResourceAction = resource.availableActions?.find((a) => a.action === this.actionOfInterest.action);
|
|
809
810
|
|
|
810
811
|
return matchingResourceAction?.enabled;
|
|
811
812
|
},
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
import { MANAGEMENT, RBAC } from '@shell/config/types';
|
|
3
3
|
import CruResource from '@shell/components/CruResource';
|
|
4
4
|
import CreateEditView from '@shell/mixins/create-edit-view';
|
|
5
|
+
import FormValidation from '@shell/mixins/form-validation';
|
|
6
|
+
import Error from '@shell/components/form/Error';
|
|
5
7
|
import { RadioGroup } from '@components/Form/Radio';
|
|
6
8
|
import Select from '@shell/components/form/Select';
|
|
7
9
|
import ArrayList from '@shell/components/form/ArrayList';
|
|
@@ -58,9 +60,10 @@ export default {
|
|
|
58
60
|
Tabbed,
|
|
59
61
|
SortableTable,
|
|
60
62
|
Loading,
|
|
63
|
+
Error
|
|
61
64
|
},
|
|
62
65
|
|
|
63
|
-
mixins: [CreateEditView],
|
|
66
|
+
mixins: [CreateEditView, FormValidation],
|
|
64
67
|
|
|
65
68
|
async fetch() {
|
|
66
69
|
// We don't want to get all schemas from the cluster because there are
|
|
@@ -109,6 +112,9 @@ export default {
|
|
|
109
112
|
scopedResources: SCOPED_RESOURCES,
|
|
110
113
|
defaultValue: false,
|
|
111
114
|
selectFocused: null,
|
|
115
|
+
fvFormRuleSets: [
|
|
116
|
+
{ path: 'displayName', rules: ['required'] }
|
|
117
|
+
],
|
|
112
118
|
};
|
|
113
119
|
},
|
|
114
120
|
|
|
@@ -524,7 +530,8 @@ export default {
|
|
|
524
530
|
:can-yaml="!isCreate"
|
|
525
531
|
:mode="mode"
|
|
526
532
|
:resource="value"
|
|
527
|
-
:errors="
|
|
533
|
+
:errors="fvUnreportedValidationErrors"
|
|
534
|
+
:validation-passed="fvFormIsValid"
|
|
528
535
|
:cancel-event="true"
|
|
529
536
|
@error="e=>errors = e"
|
|
530
537
|
@finish="save"
|
|
@@ -568,6 +575,7 @@ export default {
|
|
|
568
575
|
name-key="displayName"
|
|
569
576
|
description-key="description"
|
|
570
577
|
label="Name"
|
|
578
|
+
:rules="{ name: fvGetAndReportPathRules('displayName') }"
|
|
571
579
|
/>
|
|
572
580
|
<div
|
|
573
581
|
v-if="isRancherType"
|
|
@@ -606,6 +614,11 @@ export default {
|
|
|
606
614
|
:label="t('rbac.roletemplate.tabs.grantResources.label')"
|
|
607
615
|
:weight="1"
|
|
608
616
|
>
|
|
617
|
+
<Error
|
|
618
|
+
:value="value.rules"
|
|
619
|
+
:rules="fvGetAndReportPathRules('rules')"
|
|
620
|
+
as-banner
|
|
621
|
+
/>
|
|
609
622
|
<ArrayList
|
|
610
623
|
v-model="value.rules"
|
|
611
624
|
label="Resources"
|
|
@@ -5,7 +5,18 @@ export default {
|
|
|
5
5
|
|
|
6
6
|
methods: {
|
|
7
7
|
async login() {
|
|
8
|
-
const
|
|
8
|
+
const { requestId, publicKey, responseType } = this.$route.query;
|
|
9
|
+
|
|
10
|
+
const res = await this.$store.dispatch('auth/login', {
|
|
11
|
+
provider: this.name,
|
|
12
|
+
body: {
|
|
13
|
+
finalRedirectUrl: window.location.origin,
|
|
14
|
+
requestId,
|
|
15
|
+
publicKey,
|
|
16
|
+
responseType
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
|
|
9
20
|
const { idpRedirectUrl } = res;
|
|
10
21
|
|
|
11
22
|
window.location.href = idpRedirectUrl;
|
|
@@ -149,11 +149,10 @@ export default {
|
|
|
149
149
|
return;
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
-
//
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
const newOption = this.options.find((opt) => isEqual(this.reduce(option), this.reduce(opt)));
|
|
152
|
+
// This check is only needed if its possible for an option's label to change without the option's value changing - we can skip this if options are just strings or numbers
|
|
153
|
+
// HOWEVER even if strings are passed to v-select the 'option' in the slot is normalized to {label: <option>} so we have to check the options prop here instead of the 'option' itself
|
|
154
|
+
if (typeof this.options[0] === 'object') {
|
|
155
|
+
const newOption = this.getUpdatedOption(option);
|
|
157
156
|
|
|
158
157
|
if (newOption) {
|
|
159
158
|
const label = get(newOption, this.optionLabel);
|
|
@@ -178,6 +177,14 @@ export default {
|
|
|
178
177
|
}
|
|
179
178
|
},
|
|
180
179
|
|
|
180
|
+
// If the option's label changed in parent but value did not, the label wont be automatically updated here
|
|
181
|
+
// Ensure that the label being shown is still present in the options prop and find the new one if not
|
|
182
|
+
getUpdatedOption(option) {
|
|
183
|
+
const isOutdated = this.options && !this.options.find((opt) => option[this.optionLabel] === opt[this.optionLabel]);
|
|
184
|
+
|
|
185
|
+
return isOutdated ? this.options.find((opt) => isEqual(this.reduce(option), this.reduce(opt))) : undefined;
|
|
186
|
+
},
|
|
187
|
+
|
|
181
188
|
positionDropdown(dropdownList, component, { width }) {
|
|
182
189
|
calculatePosition(dropdownList, component, width, this.placement);
|
|
183
190
|
},
|
|
@@ -198,7 +198,7 @@ export default {
|
|
|
198
198
|
async principalProperty() {
|
|
199
199
|
const principal = await this.principal;
|
|
200
200
|
|
|
201
|
-
return principal
|
|
201
|
+
return principal?.principalType === 'group' ? 'groupPrincipalId' : 'userPrincipalId';
|
|
202
202
|
},
|
|
203
203
|
|
|
204
204
|
onAdd(principalId) {
|
|
@@ -56,8 +56,13 @@ export default {
|
|
|
56
56
|
},
|
|
57
57
|
|
|
58
58
|
async fetch() {
|
|
59
|
+
const roleBindingRequestParams = { type: this.type, opt: { force: true } };
|
|
60
|
+
|
|
61
|
+
if (this.type === NORMAN.PROJECT_ROLE_TEMPLATE_BINDING && this.parentId) {
|
|
62
|
+
Object.assign(roleBindingRequestParams, { opt: { filter: { projectId: this.parentId.split('/').join(':') } } });
|
|
63
|
+
}
|
|
59
64
|
const userHydration = [
|
|
60
|
-
this.schema ? this.$store.dispatch(`rancher/findAll`,
|
|
65
|
+
this.schema ? this.$store.dispatch(`rancher/findAll`, roleBindingRequestParams) : [],
|
|
61
66
|
this.$store.dispatch('rancher/findAll', { type: NORMAN.PRINCIPAL }),
|
|
62
67
|
this.$store.dispatch(`management/findAll`, { type: MANAGEMENT.ROLE_TEMPLATE }),
|
|
63
68
|
this.$store.dispatch(`management/findAll`, { type: MANAGEMENT.USER })
|
|
@@ -8,7 +8,8 @@ describe('component: KeyValue', () => {
|
|
|
8
8
|
const wrapper = mount(KeyValue, {
|
|
9
9
|
propsData: { value: { value } },
|
|
10
10
|
mocks: { $store: { getters: { 'i18n/t': jest.fn() } } },
|
|
11
|
-
directives: { t }
|
|
11
|
+
directives: { t },
|
|
12
|
+
stubs: { CodeMirror: true }
|
|
12
13
|
});
|
|
13
14
|
|
|
14
15
|
const inputValue = wrapper.find('textarea').element as HTMLInputElement;
|
|
@@ -24,7 +25,8 @@ describe('component: KeyValue', () => {
|
|
|
24
25
|
valueMarkdownMultiline: true,
|
|
25
26
|
},
|
|
26
27
|
mocks: { $store: { getters: { 'i18n/t': jest.fn() } } },
|
|
27
|
-
directives: { t }
|
|
28
|
+
directives: { t },
|
|
29
|
+
stubs: { CodeMirror: true }
|
|
28
30
|
});
|
|
29
31
|
|
|
30
32
|
const inputFieldTextArea = wrapper.find('textarea').element;
|
|
@@ -41,7 +43,8 @@ describe('component: KeyValue', () => {
|
|
|
41
43
|
valueMarkdownMultiline: false,
|
|
42
44
|
},
|
|
43
45
|
mocks: { $store: { getters: { 'i18n/t': jest.fn() } } },
|
|
44
|
-
directives: { t }
|
|
46
|
+
directives: { t },
|
|
47
|
+
stubs: { CodeMirror: true }
|
|
45
48
|
});
|
|
46
49
|
|
|
47
50
|
const inputFieldTextArea = wrapper.find('[data-testid="text-area-auto-grow"]');
|
|
@@ -133,6 +133,24 @@ describe('component: LabeledSelect', () => {
|
|
|
133
133
|
// Component is from a library and class is not going to be changed
|
|
134
134
|
expect(wrapper.find('.vs__selected').text()).toBe(translation);
|
|
135
135
|
});
|
|
136
|
+
|
|
137
|
+
it.each([
|
|
138
|
+
[['a'], 'b', 0],
|
|
139
|
+
[[{ value: 'a', label: 'A' }], { value: 'a', label: 'B' }, 4]
|
|
140
|
+
])('should only check for a new label if options are objects', async(options: any[], newOption, checkUpdateCalled: number) => {
|
|
141
|
+
const spyUpdatedOption = jest.spyOn(LabeledSelect.methods, 'getUpdatedOption');
|
|
142
|
+
|
|
143
|
+
const wrapper = mount(LabeledSelect, {
|
|
144
|
+
propsData: {
|
|
145
|
+
value: 'a',
|
|
146
|
+
options
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
await wrapper.setProps({ options: [newOption] });
|
|
151
|
+
|
|
152
|
+
expect(spyUpdatedOption).toHaveBeenCalledTimes(checkUpdateCalled);
|
|
153
|
+
});
|
|
136
154
|
});
|
|
137
155
|
});
|
|
138
156
|
});
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
<script>
|
|
2
|
-
import { POD } from '@shell/config/types';
|
|
3
2
|
export default {
|
|
4
3
|
name: 'PodsUsage',
|
|
5
4
|
props: {
|
|
@@ -8,47 +7,23 @@ export default {
|
|
|
8
7
|
required: true
|
|
9
8
|
},
|
|
10
9
|
},
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
};
|
|
16
|
-
},
|
|
17
|
-
methods: {
|
|
18
|
-
async startDelayedLoading() {
|
|
19
|
-
const id = this.row?.mgmt?.id;
|
|
20
|
-
|
|
21
|
-
if (this.row?.isReady && id) {
|
|
22
|
-
const req = await this.$store.dispatch('management/request', { url: `/k8s/clusters/${ id }/v1/counts` });
|
|
10
|
+
computed: {
|
|
11
|
+
podsUsage() {
|
|
12
|
+
const usedPods = this.row?.mgmt?.status?.requested?.pods;
|
|
13
|
+
const totalPods = this.row?.mgmt?.status?.allocatable?.pods;
|
|
23
14
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const totalPods = this.row?.mgmt?.status?.allocatable?.pods;
|
|
27
|
-
|
|
28
|
-
if (totalPods) {
|
|
29
|
-
this.podsUsage = `${ usedPods }/${ totalPods }`;
|
|
30
|
-
} else {
|
|
31
|
-
this.podsUsage = '—';
|
|
32
|
-
}
|
|
33
|
-
} else {
|
|
34
|
-
this.loading = false;
|
|
35
|
-
this.podsUsage = '—';
|
|
15
|
+
if (!this.row?.isReady || !totalPods) {
|
|
16
|
+
return '—';
|
|
36
17
|
}
|
|
18
|
+
|
|
19
|
+
return `${ usedPods || 0 }/${ totalPods }`;
|
|
37
20
|
}
|
|
38
|
-
}
|
|
21
|
+
}
|
|
39
22
|
};
|
|
40
23
|
</script>
|
|
41
24
|
|
|
42
25
|
<template>
|
|
43
|
-
<
|
|
44
|
-
|
|
45
|
-
class="icon icon-spinner icon-spin"
|
|
46
|
-
/>
|
|
47
|
-
<p v-else>
|
|
48
|
-
{{ podsUsage }}
|
|
26
|
+
<p>
|
|
27
|
+
<span>{{ podsUsage }}</span>
|
|
49
28
|
</p>
|
|
50
29
|
</template>
|
|
51
|
-
|
|
52
|
-
<style lang="scss" scoped>
|
|
53
|
-
|
|
54
|
-
</style>
|