@rancher/shell 0.3.6 → 0.3.8
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/translations/en-us.yaml +40 -12
- package/components/Inactivity.vue +229 -0
- package/components/auth/AuthBanner.vue +6 -0
- package/config/settings.ts +4 -0
- package/creators/app/files/.gitignore +73 -0
- package/creators/app/init +1 -0
- package/edit/auth/ldap/config.vue +21 -1
- package/edit/auth/saml.vue +132 -37
- package/edit/provisioning.cattle.io.cluster/rke2.vue +3 -15
- package/edit/workload/mixins/workload.js +12 -4
- package/layouts/blank.vue +4 -0
- package/layouts/default.vue +3 -0
- package/layouts/home.vue +4 -1
- package/layouts/plain.vue +4 -1
- package/models/provisioning.cattle.io.cluster.js +24 -0
- package/package.json +1 -1
- package/pages/c/_cluster/explorer/index.vue +1 -4
- package/pages/c/_cluster/settings/performance.vue +61 -7
- package/scripts/extension/publish +1 -1
- package/store/index.js +36 -21
- package/utils/gc/gc.ts +1 -1
|
@@ -455,7 +455,13 @@ authConfig:
|
|
|
455
455
|
objectClass: Object Class
|
|
456
456
|
password: Password
|
|
457
457
|
port: Port
|
|
458
|
+
protocol: Protocol
|
|
459
|
+
protocols:
|
|
460
|
+
starttls: Start TLS
|
|
461
|
+
ldap: LDAP
|
|
462
|
+
tls: LDAPS (TLS)
|
|
458
463
|
customizeSchema: Customize Schema
|
|
464
|
+
oktaSchema: 'The defaults below are for a generic OpenLDAP server. For more information on the values to use when using the Okta LDAP interface, see: <a target="_blank" rel="noopener noreferrer nofollow" href="https://help.okta.com/en-us/Content/Topics/Directory/LDAP-interface-connection-settings.htm">Okta LDAP Interface connection settings</a>'
|
|
459
465
|
users: Users
|
|
460
466
|
groups: Groups
|
|
461
467
|
searchAttribute: Search Attribute
|
|
@@ -500,6 +506,13 @@ authConfig:
|
|
|
500
506
|
shibboleth: Configure a Shibboleth account
|
|
501
507
|
showLdap: Configure an OpenLDAP Server
|
|
502
508
|
userName: User Name Field
|
|
509
|
+
search:
|
|
510
|
+
title: User and Group Search
|
|
511
|
+
message: The SAML Protocol does not support search or lookup for users or groups. In order to enabled search, an OpenLDAP server must be configured.
|
|
512
|
+
on: LDAP User and Group search has been configured
|
|
513
|
+
off: LDAP User and Group search is not configured
|
|
514
|
+
show: Show details
|
|
515
|
+
hide: Hide details
|
|
503
516
|
azuread:
|
|
504
517
|
tenantId: Tenant ID
|
|
505
518
|
applicationId: Application ID
|
|
@@ -1236,7 +1249,7 @@ cluster:
|
|
|
1236
1249
|
memory: Memory
|
|
1237
1250
|
disk: Disk
|
|
1238
1251
|
image: Image
|
|
1239
|
-
network:
|
|
1252
|
+
network:
|
|
1240
1253
|
title: Networks
|
|
1241
1254
|
network: Network
|
|
1242
1255
|
addNetwork: Add Network
|
|
@@ -1814,7 +1827,7 @@ cluster:
|
|
|
1814
1827
|
option: Default - RKE2 Embedded
|
|
1815
1828
|
defaultPodSecurityAdmissionConfigurationTemplateName:
|
|
1816
1829
|
label: Pod Security Admission Configuration Template
|
|
1817
|
-
option:
|
|
1830
|
+
option:
|
|
1818
1831
|
none: (None)
|
|
1819
1832
|
default: Default - RKE2 Embedded
|
|
1820
1833
|
cisProfile:
|
|
@@ -2189,16 +2202,16 @@ fleet:
|
|
|
2189
2202
|
error: Error
|
|
2190
2203
|
ready: Ready
|
|
2191
2204
|
errors: Errors
|
|
2192
|
-
workspaces:
|
|
2205
|
+
workspaces:
|
|
2193
2206
|
tabs:
|
|
2194
2207
|
restrictions: Allowed Target Namespaces
|
|
2195
2208
|
timeout: Workspace creation timeout. It's possible the workspace was created. We suggest checking the workspace page before trying to create another.
|
|
2196
|
-
restrictions:
|
|
2209
|
+
restrictions:
|
|
2197
2210
|
addTitle: 'allowedTargetNamespaces'
|
|
2198
2211
|
addLabel: Add
|
|
2199
2212
|
banner: "{count, plural,
|
|
2200
2213
|
=0 { Adding namespaces here will create a GitRepoRestriction. }
|
|
2201
|
-
other { Only the Git Repo Restriction's <code>allowedTargetNamespaces</code> is managed here. You can make additional changes to the Git Repo Restriction}
|
|
2214
|
+
other { Only the Git Repo Restriction's <code>allowedTargetNamespaces</code> is managed here. You can make additional changes to the Git Repo Restriction}
|
|
2202
2215
|
}"
|
|
2203
2216
|
footer:
|
|
2204
2217
|
docs: Docs
|
|
@@ -2247,7 +2260,7 @@ gatekeeperIndex:
|
|
|
2247
2260
|
unavailable: OPA + Gatekeeper is not available in the system-charts catalog.
|
|
2248
2261
|
violations: Violations
|
|
2249
2262
|
|
|
2250
|
-
gatekeeperInstall:
|
|
2263
|
+
gatekeeperInstall:
|
|
2251
2264
|
auditInterval: Auto Interval
|
|
2252
2265
|
constraintViolationsLimit: Constraint Violations Limit
|
|
2253
2266
|
runtimeDefaultSeccompProfile: Enable Runtime Default Seccomp Profile
|
|
@@ -3939,6 +3952,15 @@ podDisruptionBudget:
|
|
|
3939
3952
|
maxUnavailable:
|
|
3940
3953
|
label: Max. unavailable Pods
|
|
3941
3954
|
|
|
3955
|
+
inactivity:
|
|
3956
|
+
title: Session expiring
|
|
3957
|
+
titleExpired: Session expired
|
|
3958
|
+
banner: Your session is about to expire due to inactivity. Any unsaved changes will be lost.
|
|
3959
|
+
bannerExpired: Your session has expired in this tab due to inactivity.
|
|
3960
|
+
content: Click “Resume Session” to keep the session in this tab active or refresh the browser after the session has expired.
|
|
3961
|
+
contentExpired: To return to this page click “Refresh” below or refresh the browser.
|
|
3962
|
+
cta: Resume Session
|
|
3963
|
+
ctaExpired: Refresh
|
|
3942
3964
|
|
|
3943
3965
|
# Rancher Extensions
|
|
3944
3966
|
plugins:
|
|
@@ -4038,7 +4060,7 @@ plugins:
|
|
|
4038
4060
|
podSecurityAdmission:
|
|
4039
4061
|
name: Pod Security Admission
|
|
4040
4062
|
description: Define the admission control mode you want to use for the pod security
|
|
4041
|
-
banner:
|
|
4063
|
+
banner:
|
|
4042
4064
|
modifications: 'Changing any template that is currently in use will cause an update to those live clusters the next time the cluster is updated'
|
|
4043
4065
|
labels:
|
|
4044
4066
|
enforce: Enforce
|
|
@@ -4052,7 +4074,7 @@ podSecurityAdmission:
|
|
|
4052
4074
|
restricted: restricted
|
|
4053
4075
|
version:
|
|
4054
4076
|
placeholder: 'Version (default: latest)'
|
|
4055
|
-
exemptions:
|
|
4077
|
+
exemptions:
|
|
4056
4078
|
title: Exemptions
|
|
4057
4079
|
description: Allow the creation of pods for specific Usernames, RuntimeClassNames, and Namespaces that would otherwise be prohibited due to the policies set above.
|
|
4058
4080
|
placeholder: Enter a comma separated list of {psaExemptionsControl}
|
|
@@ -5173,7 +5195,7 @@ storageClass:
|
|
|
5173
5195
|
warning: 'The {provisioner} in-tree plugin is deprecated: Find a CSI driver <a target="_blank" rel="noopener noreferrer nofollow" href="https://kubernetes-csi.github.io/docs/drivers.html">here.</a>'
|
|
5174
5196
|
harvesterhci:
|
|
5175
5197
|
title: Harvester (CSI)
|
|
5176
|
-
warning:
|
|
5198
|
+
warning:
|
|
5177
5199
|
unSatisfiesVersion: Please upgrade your Harvester CSI driver version to use this feature. (csi-driver >= v0.1.15)
|
|
5178
5200
|
hostStorageClass:
|
|
5179
5201
|
label: Host Storage Class
|
|
@@ -6118,8 +6140,6 @@ workload:
|
|
|
6118
6140
|
pod: Pod
|
|
6119
6141
|
containers: Containers
|
|
6120
6142
|
|
|
6121
|
-
|
|
6122
|
-
|
|
6123
6143
|
##############################
|
|
6124
6144
|
# Model Properties
|
|
6125
6145
|
##############################
|
|
@@ -6765,7 +6785,7 @@ typeLabel:
|
|
|
6765
6785
|
{count, plural,
|
|
6766
6786
|
one { Cluster Registration Token }
|
|
6767
6787
|
other { Cluster Registration Tokens }
|
|
6768
|
-
}
|
|
6788
|
+
}
|
|
6769
6789
|
|
|
6770
6790
|
action:
|
|
6771
6791
|
clone: Clone
|
|
@@ -6989,6 +7009,14 @@ performance:
|
|
|
6989
7009
|
label: Websocket Web Worker
|
|
6990
7010
|
description: Updates to resources pushed to the UI come via WebSocket and are handled in the UI thread. Enable this option to handle cluster WebSocket updates in a Web Worker in a separate thread. This should help the responsiveness of the UI in systems where resources change often.
|
|
6991
7011
|
checkboxLabel: Enable Advanced Websocket Web Worker
|
|
7012
|
+
inactivity:
|
|
7013
|
+
title: Inactivity
|
|
7014
|
+
checkboxLabel: Enable inactivity session expiration
|
|
7015
|
+
inputLabel: Inactivity timeout (minutes)
|
|
7016
|
+
information: To change the automatic logout behaviour, edit the authorisation and/or session token timeout values (<code>auth-user-session-ttl-minutes</code> and <code>auth-token-max-ttl-minutes</code>) in the Settings page.
|
|
7017
|
+
description: When enabled and the user is inactive past the specified timeout, the UI will no longer fresh page content and the user must reload the page to continue.
|
|
7018
|
+
authUserTTL: This timeout cannot be higher than the user session timeout auth-user-session-ttl-minutes, which is currently {current} minutes.
|
|
7019
|
+
|
|
6992
7020
|
|
|
6993
7021
|
banner:
|
|
6994
7022
|
label: Fixed Banners
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import ModalWithCard from '@shell/components/ModalWithCard';
|
|
3
|
+
import { Banner } from '@components/Banner';
|
|
4
|
+
import PercentageBar from '@shell/components/PercentageBar.vue';
|
|
5
|
+
import throttle from 'lodash/throttle';
|
|
6
|
+
import { MANAGEMENT } from '@shell/config/types';
|
|
7
|
+
import { DEFAULT_PERF_SETTING, SETTING } from '@shell/config/settings';
|
|
8
|
+
|
|
9
|
+
export default {
|
|
10
|
+
name: 'Inactivity',
|
|
11
|
+
components: {
|
|
12
|
+
ModalWithCard, Banner, PercentageBar
|
|
13
|
+
},
|
|
14
|
+
data() {
|
|
15
|
+
return {
|
|
16
|
+
enabled: null,
|
|
17
|
+
isOpen: false,
|
|
18
|
+
isInactive: false,
|
|
19
|
+
showModalAfter: null,
|
|
20
|
+
inactivityTimeoutId: null,
|
|
21
|
+
courtesyTimer: null,
|
|
22
|
+
courtesyTimerId: null,
|
|
23
|
+
courtesyCountdown: null,
|
|
24
|
+
trackInactivity: throttle(this._trackInactivity, 1000),
|
|
25
|
+
};
|
|
26
|
+
},
|
|
27
|
+
async mounted() {
|
|
28
|
+
// Info: normally, this is done in the fetch hook but for some reasons while awaiting for things that will take a while, it won't be ready by the time mounted() is called, pending for investigation.
|
|
29
|
+
let settings;
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
const settingsString = await this.$store.dispatch('management/find', { type: MANAGEMENT.SETTING, id: SETTING.UI_PERFORMANCE });
|
|
33
|
+
|
|
34
|
+
settings = settingsString?.value ? JSON.parse(settingsString.value) : DEFAULT_PERF_SETTING;
|
|
35
|
+
} catch { }
|
|
36
|
+
|
|
37
|
+
if (!settings || !settings?.inactivity || !settings?.inactivity.enabled) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
this.enabled = settings?.inactivity?.enabled || false;
|
|
42
|
+
|
|
43
|
+
// Total amount of time before the user's session is lost
|
|
44
|
+
const thresholdToSeconds = settings?.inactivity?.threshold * 60;
|
|
45
|
+
|
|
46
|
+
// Amount of time the user sees the inactivity warning
|
|
47
|
+
this.courtesyTimer = Math.floor(thresholdToSeconds * 0.1);
|
|
48
|
+
this.courtesyTimer = Math.min(this.courtesyTimer, 60 * 5); // Never show the modal more than 5 minutes
|
|
49
|
+
// Amount of time before the user sees the inactivity warning
|
|
50
|
+
// Note - time before warning is shown + time warning is shown = settings threshold (total amount of time)
|
|
51
|
+
this.showModalAfter = thresholdToSeconds - this.courtesyTimer;
|
|
52
|
+
|
|
53
|
+
console.debug(`Inactivity modal will show after ${ this.showModalAfter / 60 }(m) and be shown for ${ this.courtesyTimer / 60 }(m)`); // eslint-disable-line no-console
|
|
54
|
+
|
|
55
|
+
this.courtesyCountdown = this.courtesyTimer;
|
|
56
|
+
|
|
57
|
+
if (settings?.inactivity.enabled) {
|
|
58
|
+
this.trackInactivity();
|
|
59
|
+
this.addIdleListeners();
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
beforeDestroy() {
|
|
63
|
+
this.removeEventListener();
|
|
64
|
+
this.clearAllTimeouts();
|
|
65
|
+
},
|
|
66
|
+
methods: {
|
|
67
|
+
_trackInactivity() {
|
|
68
|
+
if (this.isInactive || this.isOpen || !this.showModalAfter) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
this.clearAllTimeouts();
|
|
73
|
+
const endTime = Date.now() + this.showModalAfter * 1000;
|
|
74
|
+
|
|
75
|
+
const checkInactivityTimer = () => {
|
|
76
|
+
const now = Date.now();
|
|
77
|
+
|
|
78
|
+
if (now >= endTime) {
|
|
79
|
+
this.isOpen = true;
|
|
80
|
+
this.startCountdown();
|
|
81
|
+
|
|
82
|
+
this.$modal.show('inactivityModal');
|
|
83
|
+
} else {
|
|
84
|
+
this.inactivityTimeoutId = setTimeout(checkInactivityTimer, 1000);
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
checkInactivityTimer();
|
|
89
|
+
},
|
|
90
|
+
startCountdown() {
|
|
91
|
+
const endTime = Date.now() + (this.courtesyCountdown * 1000);
|
|
92
|
+
|
|
93
|
+
const checkCountdown = () => {
|
|
94
|
+
const now = Date.now();
|
|
95
|
+
|
|
96
|
+
if (now >= endTime) {
|
|
97
|
+
this.isInactive = true;
|
|
98
|
+
this.unsubscribe();
|
|
99
|
+
this.clearAllTimeouts();
|
|
100
|
+
} else {
|
|
101
|
+
this.courtesyCountdown = Math.floor((endTime - now) / 1000);
|
|
102
|
+
this.courtesyTimerId = setTimeout(checkCountdown, 1000);
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
checkCountdown();
|
|
107
|
+
},
|
|
108
|
+
addIdleListeners() {
|
|
109
|
+
document.addEventListener('mousemove', this.trackInactivity);
|
|
110
|
+
document.addEventListener('mousedown', this.trackInactivity);
|
|
111
|
+
document.addEventListener('keypress', this.trackInactivity);
|
|
112
|
+
document.addEventListener('touchmove', this.trackInactivity);
|
|
113
|
+
document.addEventListener('visibilitychange', this.trackInactivity);
|
|
114
|
+
},
|
|
115
|
+
removeEventListener() {
|
|
116
|
+
document.removeEventListener('mousemove', this.trackInactivity);
|
|
117
|
+
document.removeEventListener('mousedown', this.trackInactivity);
|
|
118
|
+
document.removeEventListener('keypress', this.trackInactivity);
|
|
119
|
+
document.removeEventListener('touchmove', this.trackInactivity);
|
|
120
|
+
document.removeEventListener('visibilitychange', this.trackInactivity);
|
|
121
|
+
},
|
|
122
|
+
|
|
123
|
+
resume() {
|
|
124
|
+
this.isInactive = false;
|
|
125
|
+
this.isOpen = false;
|
|
126
|
+
this.courtesyCountdown = this.courtesyTimer;
|
|
127
|
+
this.clearAllTimeouts();
|
|
128
|
+
|
|
129
|
+
this.$modal.hide('inactivityModal');
|
|
130
|
+
},
|
|
131
|
+
|
|
132
|
+
refresh() {
|
|
133
|
+
window.location.reload();
|
|
134
|
+
},
|
|
135
|
+
|
|
136
|
+
unsubscribe() {
|
|
137
|
+
this.$store.dispatch('unsubscribe');
|
|
138
|
+
},
|
|
139
|
+
clearAllTimeouts() {
|
|
140
|
+
clearTimeout(this.inactivityTimeoutId);
|
|
141
|
+
clearTimeout(this.courtesyTimerId);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
},
|
|
145
|
+
computed: {
|
|
146
|
+
isInactiveTexts() {
|
|
147
|
+
return this.isInactive ? {
|
|
148
|
+
title: this.t('inactivity.titleExpired'),
|
|
149
|
+
banner: this.t('inactivity.bannerExpired'),
|
|
150
|
+
content: this.t('inactivity.contentExpired'),
|
|
151
|
+
} : {
|
|
152
|
+
title: this.t('inactivity.title'),
|
|
153
|
+
banner: this.t('inactivity.banner'),
|
|
154
|
+
content: this.t('inactivity.content'),
|
|
155
|
+
};
|
|
156
|
+
},
|
|
157
|
+
timerPercentageLeft() {
|
|
158
|
+
return Math.floor((this.courtesyCountdown / this.courtesyTimer ) * 100);
|
|
159
|
+
},
|
|
160
|
+
colorStops() {
|
|
161
|
+
return {
|
|
162
|
+
0: '--info', 30: '--info', 70: '--info'
|
|
163
|
+
};
|
|
164
|
+
},
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
</script>
|
|
168
|
+
|
|
169
|
+
<template>
|
|
170
|
+
<ModalWithCard
|
|
171
|
+
ref="inactivityModal"
|
|
172
|
+
name="inactivityModal"
|
|
173
|
+
save-text="Continue"
|
|
174
|
+
:v-if="isOpen"
|
|
175
|
+
@finish="resume"
|
|
176
|
+
>
|
|
177
|
+
<template #title>
|
|
178
|
+
{{ isInactiveTexts.title }}
|
|
179
|
+
</template>
|
|
180
|
+
<span>{{ courtesyCountdown }}</span>
|
|
181
|
+
|
|
182
|
+
<template #content>
|
|
183
|
+
<Banner color="info">
|
|
184
|
+
{{ isInactiveTexts.banner }}
|
|
185
|
+
</Banner>
|
|
186
|
+
|
|
187
|
+
<p>
|
|
188
|
+
{{ isInactiveTexts.content }}
|
|
189
|
+
</p>
|
|
190
|
+
|
|
191
|
+
<PercentageBar
|
|
192
|
+
v-if="!isInactive"
|
|
193
|
+
class="mt-20"
|
|
194
|
+
:value="timerPercentageLeft"
|
|
195
|
+
:color-stops="colorStops"
|
|
196
|
+
/>
|
|
197
|
+
</template>
|
|
198
|
+
|
|
199
|
+
<template
|
|
200
|
+
#footer
|
|
201
|
+
>
|
|
202
|
+
<div class="card-actions">
|
|
203
|
+
<button
|
|
204
|
+
v-if="!isInactive"
|
|
205
|
+
class="btn role-tertiary bg-primary"
|
|
206
|
+
@click.prevent="resume"
|
|
207
|
+
>
|
|
208
|
+
<t k="inactivity.cta" />
|
|
209
|
+
</button>
|
|
210
|
+
|
|
211
|
+
<button
|
|
212
|
+
v-if="isInactive"
|
|
213
|
+
class="btn role-tertiary bg-primary"
|
|
214
|
+
@click.prevent="refresh"
|
|
215
|
+
>
|
|
216
|
+
<t k="inactivity.ctaExpired" />
|
|
217
|
+
</button>
|
|
218
|
+
</div>
|
|
219
|
+
</template>
|
|
220
|
+
</ModalWithCard>
|
|
221
|
+
</template>
|
|
222
|
+
|
|
223
|
+
<style lang="scss" scoped>
|
|
224
|
+
.card-actions {
|
|
225
|
+
display: flex;
|
|
226
|
+
width: 100%;
|
|
227
|
+
justify-content: flex-end;
|
|
228
|
+
}
|
|
229
|
+
</style>
|
package/config/settings.ts
CHANGED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# compiled output
|
|
2
|
+
/dist
|
|
3
|
+
/tmp
|
|
4
|
+
/out-tsc
|
|
5
|
+
|
|
6
|
+
# Runtime data
|
|
7
|
+
pids
|
|
8
|
+
*.pid
|
|
9
|
+
*.seed
|
|
10
|
+
*.pid.lock
|
|
11
|
+
|
|
12
|
+
# Directory for instrumented libs generated by jscoverage/JSCover
|
|
13
|
+
lib-cov
|
|
14
|
+
|
|
15
|
+
# Coverage directory used by tools like istanbul
|
|
16
|
+
coverage
|
|
17
|
+
|
|
18
|
+
# nyc test coverage
|
|
19
|
+
.nyc_output
|
|
20
|
+
|
|
21
|
+
# IDEs and editors
|
|
22
|
+
.idea
|
|
23
|
+
.project
|
|
24
|
+
.classpath
|
|
25
|
+
.c9/
|
|
26
|
+
*.launch
|
|
27
|
+
.settings/
|
|
28
|
+
*.sublime-workspace
|
|
29
|
+
|
|
30
|
+
# IDE - VSCode
|
|
31
|
+
.vscode/*
|
|
32
|
+
!.vscode/settings.json
|
|
33
|
+
!.vscode/tasks.json
|
|
34
|
+
!.vscode/launch.json
|
|
35
|
+
!.vscode/extensions.json
|
|
36
|
+
|
|
37
|
+
# misc
|
|
38
|
+
.sass-cache
|
|
39
|
+
connect.lock
|
|
40
|
+
typings
|
|
41
|
+
|
|
42
|
+
# Logs
|
|
43
|
+
logs
|
|
44
|
+
*.log
|
|
45
|
+
npm-debug.log*
|
|
46
|
+
yarn-debug.log*
|
|
47
|
+
yarn-error.log*
|
|
48
|
+
|
|
49
|
+
# Dependency directories
|
|
50
|
+
node_modules/
|
|
51
|
+
jspm_packages/
|
|
52
|
+
|
|
53
|
+
# Optional npm cache directory
|
|
54
|
+
.npm
|
|
55
|
+
|
|
56
|
+
# Optional eslint cache
|
|
57
|
+
.eslintcache
|
|
58
|
+
|
|
59
|
+
# Optional REPL history
|
|
60
|
+
.node_repl_history
|
|
61
|
+
|
|
62
|
+
# Output of 'npm pack'
|
|
63
|
+
*.tgz
|
|
64
|
+
|
|
65
|
+
# Yarn Integrity file
|
|
66
|
+
.yarn-integrity
|
|
67
|
+
|
|
68
|
+
# dotenv environment variables file
|
|
69
|
+
.env
|
|
70
|
+
|
|
71
|
+
# System Files
|
|
72
|
+
.DS_Store
|
|
73
|
+
Thumbs.db
|
package/creators/app/init
CHANGED
|
@@ -5,6 +5,7 @@ import { Checkbox } from '@components/Form/Checkbox';
|
|
|
5
5
|
import UnitInput from '@shell/components/form/UnitInput';
|
|
6
6
|
import { Banner } from '@components/Banner';
|
|
7
7
|
import FileSelector from '@shell/components/form/FileSelector';
|
|
8
|
+
import { OKTA, SHIBBOLETH } from '../saml';
|
|
8
9
|
|
|
9
10
|
const DEFAULT_NON_TLS_PORT = 389;
|
|
10
11
|
const DEFAULT_TLS_PORT = 636;
|
|
@@ -33,6 +34,11 @@ export default {
|
|
|
33
34
|
type: {
|
|
34
35
|
type: String,
|
|
35
36
|
required: true
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
isCreate: {
|
|
40
|
+
type: Boolean,
|
|
41
|
+
default: false
|
|
36
42
|
}
|
|
37
43
|
|
|
38
44
|
},
|
|
@@ -46,9 +52,17 @@ export default {
|
|
|
46
52
|
model: this.value,
|
|
47
53
|
hostname: this.value.servers.join(','),
|
|
48
54
|
serverSetting: null,
|
|
55
|
+
OKTA
|
|
49
56
|
};
|
|
50
57
|
},
|
|
51
58
|
|
|
59
|
+
computed: {
|
|
60
|
+
// Does the auth provider support LDAP for search in addition to SAML?
|
|
61
|
+
isSamlProvider() {
|
|
62
|
+
return this.type === SHIBBOLETH || this.type === OKTA;
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
|
|
52
66
|
watch: {
|
|
53
67
|
hostname(neu, old) {
|
|
54
68
|
this.value.servers = neu.split(',');
|
|
@@ -232,6 +246,12 @@ export default {
|
|
|
232
246
|
<div class="row">
|
|
233
247
|
<h3> {{ t('authConfig.ldap.customizeSchema') }}</h3>
|
|
234
248
|
</div>
|
|
249
|
+
<Banner
|
|
250
|
+
v-if="type === OKTA && isCreate"
|
|
251
|
+
class="row"
|
|
252
|
+
color="info"
|
|
253
|
+
label-key="authConfig.ldap.oktaSchema"
|
|
254
|
+
/>
|
|
235
255
|
<div class="row">
|
|
236
256
|
<div class="col span-6">
|
|
237
257
|
<h4>{{ t('authConfig.ldap.users') }}</h4>
|
|
@@ -361,7 +381,7 @@ export default {
|
|
|
361
381
|
/>
|
|
362
382
|
</div>
|
|
363
383
|
<div
|
|
364
|
-
v-if="
|
|
384
|
+
v-if="!isSamlProvider"
|
|
365
385
|
class=" col span-6"
|
|
366
386
|
>
|
|
367
387
|
<RadioGroup
|
package/edit/auth/saml.vue
CHANGED
|
@@ -11,6 +11,31 @@ import FileSelector from '@shell/components/form/FileSelector';
|
|
|
11
11
|
import AuthBanner from '@shell/components/auth/AuthBanner';
|
|
12
12
|
import config from '@shell/edit/auth/ldap/config';
|
|
13
13
|
|
|
14
|
+
export const SHIBBOLETH = 'shibboleth';
|
|
15
|
+
export const OKTA = 'okta';
|
|
16
|
+
|
|
17
|
+
// Standard LDAP defaults
|
|
18
|
+
const LDAP_DEFAULTS = {
|
|
19
|
+
connectionTimeout: 5000,
|
|
20
|
+
groupDNAttribute: 'entryDN',
|
|
21
|
+
groupMemberMappingAttribute: 'member',
|
|
22
|
+
groupMemberUserAttribute: 'entryDN',
|
|
23
|
+
groupNameAttribute: 'cn',
|
|
24
|
+
groupObjectClass: 'groupOfNames',
|
|
25
|
+
groupSearchAttribute: 'cn',
|
|
26
|
+
nestedGroupMembershipEnabled: false,
|
|
27
|
+
port: 389,
|
|
28
|
+
servers: [],
|
|
29
|
+
starttls: false,
|
|
30
|
+
tls: false,
|
|
31
|
+
disabledStatusBitmask: 0,
|
|
32
|
+
userLoginAttribute: 'uid',
|
|
33
|
+
userMemberAttribute: 'memberOf',
|
|
34
|
+
userNameAttribute: 'cn',
|
|
35
|
+
userObjectClass: 'inetOrgPerson',
|
|
36
|
+
userSearchAttribute: 'uid|sn|givenName'
|
|
37
|
+
};
|
|
38
|
+
|
|
14
39
|
export default {
|
|
15
40
|
components: {
|
|
16
41
|
Loading,
|
|
@@ -26,7 +51,10 @@ export default {
|
|
|
26
51
|
|
|
27
52
|
mixins: [CreateEditView, AuthConfig],
|
|
28
53
|
data() {
|
|
29
|
-
return {
|
|
54
|
+
return {
|
|
55
|
+
showLdap: false,
|
|
56
|
+
showLdapDetails: false,
|
|
57
|
+
};
|
|
30
58
|
},
|
|
31
59
|
|
|
32
60
|
computed: {
|
|
@@ -42,32 +70,32 @@ export default {
|
|
|
42
70
|
return { enabled: true, ...this.model };
|
|
43
71
|
},
|
|
44
72
|
|
|
73
|
+
// Does the auth provider support LDAP for search in addition to SAML?
|
|
74
|
+
supportsLDAPSearch() {
|
|
75
|
+
return this.NAME === SHIBBOLETH || this.NAME === OKTA;
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
ldapHosts() {
|
|
79
|
+
const hosts = this.model?.openLdapConfig.servers || [];
|
|
80
|
+
|
|
81
|
+
return hosts.join(',');
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
ldapProtocol() {
|
|
85
|
+
if (this.model?.openLdapConfig?.starttls) {
|
|
86
|
+
return this.t('authConfig.ldap.protocols.starttls');
|
|
87
|
+
} else if (this.model?.openLdapConfig?.tls) {
|
|
88
|
+
return this.t('authConfig.ldap.protocols.tls');
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return this.t('authConfig.ldap.protocols.ldap');
|
|
92
|
+
}
|
|
45
93
|
},
|
|
46
94
|
watch: {
|
|
47
95
|
showLdap(neu, old) {
|
|
48
96
|
if (neu && !this.model.openLdapConfig) {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
groupDNAttribute: 'entryDN',
|
|
52
|
-
groupMemberMappingAttribute: 'member',
|
|
53
|
-
groupMemberUserAttribute: 'entryDN',
|
|
54
|
-
groupNameAttribute: 'cn',
|
|
55
|
-
groupObjectClass: 'groupOfNames',
|
|
56
|
-
groupSearchAttribute: 'cn',
|
|
57
|
-
nestedGroupMembershipEnabled: false,
|
|
58
|
-
port: 389,
|
|
59
|
-
servers: [],
|
|
60
|
-
starttls: false,
|
|
61
|
-
tls: false,
|
|
62
|
-
disabledStatusBitmask: 0,
|
|
63
|
-
userLoginAttribute: 'uid',
|
|
64
|
-
userMemberAttribute: 'memberOf',
|
|
65
|
-
userNameAttribute: 'cn',
|
|
66
|
-
userObjectClass: 'inetOrgPerson',
|
|
67
|
-
userSearchAttribute: 'uid|sn|givenName'
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
this.$set(this.model, 'openLdapConfig', config);
|
|
97
|
+
// Use a spread of config, so that if don't make changes to the defaults if the user edits them
|
|
98
|
+
this.$set(this.model, 'openLdapConfig', { ...LDAP_DEFAULTS });
|
|
71
99
|
}
|
|
72
100
|
}
|
|
73
101
|
}
|
|
@@ -98,7 +126,9 @@ export default {
|
|
|
98
126
|
:disable="disable"
|
|
99
127
|
:edit="goToEdit"
|
|
100
128
|
>
|
|
101
|
-
<template
|
|
129
|
+
<template
|
|
130
|
+
slot="rows"
|
|
131
|
+
>
|
|
102
132
|
<tr><td>{{ t(`authConfig.saml.displayName`) }}: </td><td>{{ model.displayNameField }}</td></tr>
|
|
103
133
|
<tr><td>{{ t(`authConfig.saml.userName`) }}: </td><td>{{ model.userNameField }}</td></tr>
|
|
104
134
|
<tr><td>{{ t(`authConfig.saml.UID`) }}: </td><td>{{ model.uidField }}</td></tr>
|
|
@@ -106,6 +136,47 @@ export default {
|
|
|
106
136
|
<tr><td>{{ t(`authConfig.saml.api`) }}: </td><td>{{ model.rancherApiHost }}</td></tr>
|
|
107
137
|
<tr><td>{{ t(`authConfig.saml.groups`) }}: </td><td>{{ model.groupsField }}</td></tr>
|
|
108
138
|
</template>
|
|
139
|
+
|
|
140
|
+
<template
|
|
141
|
+
v-if="supportsLDAPSearch"
|
|
142
|
+
slot="footer"
|
|
143
|
+
>
|
|
144
|
+
<Banner
|
|
145
|
+
v-if="showLdap"
|
|
146
|
+
color="success"
|
|
147
|
+
class="banner"
|
|
148
|
+
>
|
|
149
|
+
<div
|
|
150
|
+
class="advanced-ldap-banner"
|
|
151
|
+
>
|
|
152
|
+
<div>{{ t('authConfig.saml.search.on') }}</div>
|
|
153
|
+
<div>
|
|
154
|
+
<a
|
|
155
|
+
class="toggle-btn"
|
|
156
|
+
@click="showLdapDetails = !showLdapDetails"
|
|
157
|
+
>
|
|
158
|
+
<template v-if="showLdapDetails">{{ t('authConfig.saml.search.hide') }}</template>
|
|
159
|
+
<template v-else>{{ t('authConfig.saml.search.show') }}</template>
|
|
160
|
+
</a>
|
|
161
|
+
</div>
|
|
162
|
+
</div>
|
|
163
|
+
</Banner>
|
|
164
|
+
<Banner
|
|
165
|
+
v-else
|
|
166
|
+
color="info"
|
|
167
|
+
>
|
|
168
|
+
{{ t('authConfig.saml.search.off') }}
|
|
169
|
+
</Banner>
|
|
170
|
+
|
|
171
|
+
<table v-if="showLdapDetails && model.openLdapConfig">
|
|
172
|
+
<tr><td>{{ t('authConfig.ldap.hostname.label') }}:</td><td>{{ ldapHosts }}</td></tr>
|
|
173
|
+
<tr><td>{{ t('authConfig.ldap.port') }}:</td><td>{{ model.openLdapConfig.port }}</td></tr>
|
|
174
|
+
<tr><td>{{ t('authConfig.ldap.protocol') }}:</td><td>{{ ldapProtocol }}</td></tr>
|
|
175
|
+
<tr><td>{{ t('authConfig.ldap.serviceAccountDN') }}:</td><td>{{ model.openLdapConfig.serviceAccountDistinguishedName }}</td></tr>
|
|
176
|
+
<tr><td>{{ t('authConfig.ldap.userSearchBase.label') }}:</td><td>{{ model.openLdapConfig.userSearchBase }}</td></tr>
|
|
177
|
+
<tr><td>{{ t('authConfig.ldap.groupSearchBase.label') }}:</td><td>{{ model.openLdapConfig.groupSearchBase }}</td></tr>
|
|
178
|
+
</table>
|
|
179
|
+
</template>
|
|
109
180
|
</AuthBanner>
|
|
110
181
|
|
|
111
182
|
<hr>
|
|
@@ -233,7 +304,26 @@ export default {
|
|
|
233
304
|
/>
|
|
234
305
|
</div>
|
|
235
306
|
</div>
|
|
236
|
-
<div
|
|
307
|
+
<div
|
|
308
|
+
v-if="!model.enabled"
|
|
309
|
+
class="row"
|
|
310
|
+
>
|
|
311
|
+
<div class="col span-12">
|
|
312
|
+
<Banner color="info">
|
|
313
|
+
<div v-clean-html="t('authConfig.associatedWarning', tArgs, true)" />
|
|
314
|
+
</Banner>
|
|
315
|
+
</div>
|
|
316
|
+
</div>
|
|
317
|
+
<div v-if="supportsLDAPSearch">
|
|
318
|
+
<div class="row">
|
|
319
|
+
<h2>{{ t('authConfig.saml.search.title') }}</h2>
|
|
320
|
+
</div>
|
|
321
|
+
<div class="row">
|
|
322
|
+
<Banner
|
|
323
|
+
label-key="authConfig.saml.search.message"
|
|
324
|
+
color="info"
|
|
325
|
+
/>
|
|
326
|
+
</div>
|
|
237
327
|
<div class="row">
|
|
238
328
|
<Checkbox
|
|
239
329
|
v-model="showLdap"
|
|
@@ -243,25 +333,15 @@ export default {
|
|
|
243
333
|
</div>
|
|
244
334
|
<div class="row mt-20 mb-20">
|
|
245
335
|
<config
|
|
246
|
-
v-if="showLdap"
|
|
336
|
+
v-if="showLdap && model.openLdapConfig"
|
|
247
337
|
v-model="model.openLdapConfig"
|
|
248
338
|
:type="NAME"
|
|
249
339
|
:mode="mode"
|
|
340
|
+
:is-create="!model.enabled"
|
|
250
341
|
/>
|
|
251
342
|
</div>
|
|
252
343
|
</div>
|
|
253
344
|
</template>
|
|
254
|
-
<div
|
|
255
|
-
v-if="!model.enabled"
|
|
256
|
-
class="row"
|
|
257
|
-
>
|
|
258
|
-
<div class="col span-12">
|
|
259
|
-
<Banner
|
|
260
|
-
v-clean-html="t('authConfig.associatedWarning', tArgs, true)"
|
|
261
|
-
color="info"
|
|
262
|
-
/>
|
|
263
|
-
</div>
|
|
264
|
-
</div>
|
|
265
345
|
</CruResource>
|
|
266
346
|
</div>
|
|
267
347
|
</template>
|
|
@@ -274,4 +354,19 @@ export default {
|
|
|
274
354
|
margin: 0 3px;
|
|
275
355
|
}
|
|
276
356
|
}
|
|
357
|
+
|
|
358
|
+
// Banner shows message and link formatted right aligned
|
|
359
|
+
.advanced-ldap-banner {
|
|
360
|
+
display: flex;
|
|
361
|
+
flex: 1;
|
|
362
|
+
|
|
363
|
+
> :first-child {
|
|
364
|
+
flex: 1;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
.toggle-btn {
|
|
368
|
+
cursor: pointer;
|
|
369
|
+
user-select: none;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
277
372
|
</style>
|
|
@@ -306,12 +306,7 @@ export default {
|
|
|
306
306
|
const lastDefaultPodSecurityPolicyTemplateName = this.value.spec.defaultPodSecurityPolicyTemplateName;
|
|
307
307
|
const previousKubernetesVersion = this.value.spec.kubernetesVersion;
|
|
308
308
|
|
|
309
|
-
const truncateLimit = this.value.
|
|
310
|
-
|
|
311
|
-
// Is hostname truncation supported by the backend?
|
|
312
|
-
const provSchema = this.$store.getters['management/schemaFor'](CAPI.RANCHER_CLUSTER);
|
|
313
|
-
const specSchemaName = provSchema?.resourceFields?.spec?.type;
|
|
314
|
-
const specSchema = specSchemaName ? this.$store.getters['management/schemaFor'](specSchemaName) : {};
|
|
309
|
+
const truncateLimit = this.value.defaultHostnameLengthLimit;
|
|
315
310
|
|
|
316
311
|
return {
|
|
317
312
|
loadedOnce: false,
|
|
@@ -350,7 +345,6 @@ export default {
|
|
|
350
345
|
psps: null, // List of policies if any
|
|
351
346
|
truncateHostnames: truncateLimit === NETBIOS_TRUNCATION_LENGTH,
|
|
352
347
|
truncateLimit,
|
|
353
|
-
supportsTruncation: !!specSchema?.resourceFields?.machinePoolDefaults,
|
|
354
348
|
};
|
|
355
349
|
},
|
|
356
350
|
|
|
@@ -1100,14 +1094,9 @@ export default {
|
|
|
1100
1094
|
*/
|
|
1101
1095
|
truncateName() {
|
|
1102
1096
|
if (this.truncateHostnames) {
|
|
1103
|
-
this.value.
|
|
1104
|
-
this.value.machinePoolDefaults.hostnameLengthLimit = 15;
|
|
1097
|
+
this.value.defaultHostnameLengthLimit = NETBIOS_TRUNCATION_LENGTH;
|
|
1105
1098
|
} else {
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
if (Object.keys(this.value.machinePoolDefaults).length === 0) {
|
|
1109
|
-
delete this.value.machinePoolDefaults;
|
|
1110
|
-
}
|
|
1099
|
+
this.value.removeDefaultHostnameLengthLimit();
|
|
1111
1100
|
}
|
|
1112
1101
|
},
|
|
1113
1102
|
/**
|
|
@@ -2598,7 +2587,6 @@ export default {
|
|
|
2598
2587
|
/>
|
|
2599
2588
|
</div>
|
|
2600
2589
|
<div
|
|
2601
|
-
v-if="supportsTruncation"
|
|
2602
2590
|
class="col span-6"
|
|
2603
2591
|
>
|
|
2604
2592
|
<Checkbox
|
|
@@ -26,7 +26,7 @@ import Loading from '@shell/components/Loading';
|
|
|
26
26
|
import Networking from '@shell/components/form/Networking';
|
|
27
27
|
import VolumeClaimTemplate from '@shell/edit/workload/VolumeClaimTemplate';
|
|
28
28
|
import Job from '@shell/edit/workload/Job';
|
|
29
|
-
import { _EDIT, _CREATE, _VIEW } from '@shell/config/query-params';
|
|
29
|
+
import { _EDIT, _CREATE, _VIEW, _CLONE } from '@shell/config/query-params';
|
|
30
30
|
import WorkloadPorts from '@shell/components/form/WorkloadPorts';
|
|
31
31
|
import ContainerResourceLimit from '@shell/components/ContainerResourceLimit';
|
|
32
32
|
import KeyValue from '@shell/components/form/KeyValue';
|
|
@@ -178,7 +178,7 @@ export default {
|
|
|
178
178
|
|
|
179
179
|
// EDIT view for POD
|
|
180
180
|
// Transform it from POD world to workload
|
|
181
|
-
if ((this.mode === _EDIT || this.mode === _VIEW ) && this.value.type === 'pod'
|
|
181
|
+
if ((this.mode === _EDIT || this.mode === _VIEW || this.realMode === _CLONE ) && this.value.type === 'pod') {
|
|
182
182
|
const podSpec = { ...this.value.spec };
|
|
183
183
|
const metadata = { ...this.value.metadata };
|
|
184
184
|
|
|
@@ -198,6 +198,7 @@ export default {
|
|
|
198
198
|
if (
|
|
199
199
|
this.mode === _CREATE ||
|
|
200
200
|
this.mode === _VIEW ||
|
|
201
|
+
this.realMode === _CLONE ||
|
|
201
202
|
(!createSidecar && !this.value.hasSidecars) // hasSideCars = containers.length > 1 || initContainers.length;
|
|
202
203
|
) {
|
|
203
204
|
container = containers[0];
|
|
@@ -703,7 +704,7 @@ export default {
|
|
|
703
704
|
if (
|
|
704
705
|
this.type !== WORKLOAD_TYPES.JOB &&
|
|
705
706
|
this.type !== WORKLOAD_TYPES.CRON_JOB &&
|
|
706
|
-
this.mode === _CREATE
|
|
707
|
+
(this.mode === _CREATE || this.realMode === _CLONE)
|
|
707
708
|
) {
|
|
708
709
|
this.spec.selector = { matchLabels: this.value.workloadSelector };
|
|
709
710
|
Object.assign(this.value.metadata.labels, this.value.workloadSelector);
|
|
@@ -721,7 +722,7 @@ export default {
|
|
|
721
722
|
if (
|
|
722
723
|
this.type !== WORKLOAD_TYPES.JOB &&
|
|
723
724
|
this.type !== WORKLOAD_TYPES.CRON_JOB &&
|
|
724
|
-
this.mode === _CREATE
|
|
725
|
+
(this.mode === _CREATE || this.realMode === _CLONE)
|
|
725
726
|
) {
|
|
726
727
|
if (!template.metadata) {
|
|
727
728
|
template.metadata = { labels: this.value.workloadSelector };
|
|
@@ -784,6 +785,13 @@ export default {
|
|
|
784
785
|
|
|
785
786
|
template.metadata.namespace = this.value.metadata.namespace;
|
|
786
787
|
|
|
788
|
+
// Handle the case where the user has changed the name of the workload
|
|
789
|
+
// Only do this for clone. Not allowed for edit
|
|
790
|
+
if (this.realMode === _CLONE) {
|
|
791
|
+
template.metadata.name = this.value.metadata.name;
|
|
792
|
+
template.metadata.description = this.value.metadata.description;
|
|
793
|
+
}
|
|
794
|
+
|
|
787
795
|
// delete this.value.kind;
|
|
788
796
|
if (this.container && !this.container.name) {
|
|
789
797
|
this.$set(this.container, 'name', this.value.metadata.name);
|
package/layouts/blank.vue
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
import Brand from '@shell/mixins/brand';
|
|
3
|
+
import Inactivity from '@shell/components/Inactivity';
|
|
3
4
|
|
|
4
5
|
export default {
|
|
6
|
+
components: { Inactivity },
|
|
5
7
|
middleware: ['authenticated'],
|
|
6
8
|
mixins: [Brand],
|
|
7
9
|
};
|
|
@@ -10,6 +12,8 @@ export default {
|
|
|
10
12
|
<template>
|
|
11
13
|
<main class="main-layout">
|
|
12
14
|
<nuxt />
|
|
15
|
+
|
|
16
|
+
<Inactivity />
|
|
13
17
|
</main>
|
|
14
18
|
</template>
|
|
15
19
|
|
package/layouts/default.vue
CHANGED
|
@@ -16,6 +16,7 @@ import PromptModal from '@shell/components/PromptModal';
|
|
|
16
16
|
import AssignTo from '@shell/components/AssignTo';
|
|
17
17
|
import Group from '@shell/components/nav/Group';
|
|
18
18
|
import Header from '@shell/components/nav/Header';
|
|
19
|
+
import Inactivity from '@shell/components/Inactivity';
|
|
19
20
|
import Brand from '@shell/mixins/brand';
|
|
20
21
|
import FixedBanner from '@shell/components/FixedBanner';
|
|
21
22
|
import AwsComplianceBanner from '@shell/components/AwsComplianceBanner';
|
|
@@ -57,6 +58,7 @@ export default {
|
|
|
57
58
|
AwsComplianceBanner,
|
|
58
59
|
AzureWarning,
|
|
59
60
|
DraggableZone,
|
|
61
|
+
Inactivity
|
|
60
62
|
},
|
|
61
63
|
|
|
62
64
|
mixins: [PageHeaderActions, Brand, BrowserTabVisibility],
|
|
@@ -765,6 +767,7 @@ export default {
|
|
|
765
767
|
</div>
|
|
766
768
|
<FixedBanner :footer="true" />
|
|
767
769
|
<GrowlManager />
|
|
770
|
+
<Inactivity />
|
|
768
771
|
<DraggableZone ref="draggableZone" />
|
|
769
772
|
</div>
|
|
770
773
|
</template>
|
package/layouts/home.vue
CHANGED
|
@@ -7,6 +7,7 @@ import { mapPref, THEME_SHORTCUT } from '@shell/store/prefs';
|
|
|
7
7
|
import AwsComplianceBanner from '@shell/components/AwsComplianceBanner';
|
|
8
8
|
import AzureWarning from '@shell/components/auth/AzureWarning';
|
|
9
9
|
import BrowserTabVisibility from '@shell/mixins/browser-tab-visibility';
|
|
10
|
+
import Inactivity from '@shell/components/Inactivity';
|
|
10
11
|
import { mapState } from 'vuex';
|
|
11
12
|
|
|
12
13
|
export default {
|
|
@@ -16,7 +17,8 @@ export default {
|
|
|
16
17
|
FixedBanner,
|
|
17
18
|
GrowlManager,
|
|
18
19
|
AzureWarning,
|
|
19
|
-
AwsComplianceBanner
|
|
20
|
+
AwsComplianceBanner,
|
|
21
|
+
Inactivity
|
|
20
22
|
},
|
|
21
23
|
|
|
22
24
|
mixins: [Brand, BrowserTabVisibility],
|
|
@@ -51,6 +53,7 @@ export default {
|
|
|
51
53
|
<template>
|
|
52
54
|
<div class="dashboard-root">
|
|
53
55
|
<FixedBanner :header="true" />
|
|
56
|
+
<Inactivity />
|
|
54
57
|
<AwsComplianceBanner />
|
|
55
58
|
<AzureWarning />
|
|
56
59
|
|
package/layouts/plain.vue
CHANGED
|
@@ -11,6 +11,7 @@ import GrowlManager from '@shell/components/GrowlManager';
|
|
|
11
11
|
import AwsComplianceBanner from '@shell/components/AwsComplianceBanner';
|
|
12
12
|
import AzureWarning from '@shell/components/auth/AzureWarning';
|
|
13
13
|
import BrowserTabVisibility from '@shell/mixins/browser-tab-visibility';
|
|
14
|
+
import Inactivity from '@shell/components/Inactivity';
|
|
14
15
|
|
|
15
16
|
export default {
|
|
16
17
|
|
|
@@ -23,7 +24,8 @@ export default {
|
|
|
23
24
|
FixedBanner,
|
|
24
25
|
GrowlManager,
|
|
25
26
|
AwsComplianceBanner,
|
|
26
|
-
AzureWarning
|
|
27
|
+
AzureWarning,
|
|
28
|
+
Inactivity
|
|
27
29
|
},
|
|
28
30
|
|
|
29
31
|
middleware: ['authenticated'],
|
|
@@ -83,6 +85,7 @@ export default {
|
|
|
83
85
|
|
|
84
86
|
<FixedBanner :footer="true" />
|
|
85
87
|
<GrowlManager />
|
|
88
|
+
<Inactivity />
|
|
86
89
|
</div>
|
|
87
90
|
</template>
|
|
88
91
|
|
|
@@ -393,6 +393,30 @@ export default class ProvCluster extends SteveModel {
|
|
|
393
393
|
}
|
|
394
394
|
}
|
|
395
395
|
|
|
396
|
+
get machinePoolDefaults() {
|
|
397
|
+
return this.spec.rkeConfig?.machinePoolDefaults;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
set defaultHostnameLengthLimit(value) {
|
|
401
|
+
this.spec.rkeConfig = this.spec.rkeConfig || {};
|
|
402
|
+
this.spec.rkeConfig.machinePoolDefaults = this.spec.rkeConfig.machinePoolDefaults || {};
|
|
403
|
+
this.spec.rkeConfig.machinePoolDefaults.hostnameLengthLimit = value;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
get defaultHostnameLengthLimit() {
|
|
407
|
+
return this.spec.rkeConfig?.machinePoolDefaults?.hostnameLengthLimit;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
removeDefaultHostnameLengthLimit() {
|
|
411
|
+
if (this.machinePoolDefaults?.hostnameLengthLimit) {
|
|
412
|
+
delete this.spec.rkeConfig.machinePoolDefaults.hostnameLengthLimit;
|
|
413
|
+
|
|
414
|
+
if (Object.keys(this.spec?.rkeConfig?.machinePoolDefaults).length === 0) {
|
|
415
|
+
delete this.spec.rkeConfig.machinePoolDefaults;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
396
420
|
get nodes() {
|
|
397
421
|
return this.$rootGetters['management/all'](MANAGEMENT.NODE).filter(node => node.id.startsWith(this.mgmtClusterId));
|
|
398
422
|
}
|
package/package.json
CHANGED
|
@@ -23,7 +23,6 @@ import {
|
|
|
23
23
|
WORKLOAD_TYPES,
|
|
24
24
|
COUNT,
|
|
25
25
|
CATALOG,
|
|
26
|
-
POD,
|
|
27
26
|
PSP,
|
|
28
27
|
} from '@shell/config/types';
|
|
29
28
|
import { mapPref, CLUSTER_TOOLS_TIP, PSP_DEPRECATION_BANNER } from '@shell/store/prefs';
|
|
@@ -264,11 +263,9 @@ export default {
|
|
|
264
263
|
},
|
|
265
264
|
|
|
266
265
|
podsUsed() {
|
|
267
|
-
const pods = resourceCounts(this.$store, POD);
|
|
268
|
-
|
|
269
266
|
return {
|
|
270
267
|
total: parseSi(this.currentCluster?.status?.allocatable?.pods || '0'),
|
|
271
|
-
useful: pods
|
|
268
|
+
useful: parseSi(this.currentCluster?.status?.requested?.pods || '0'),
|
|
272
269
|
};
|
|
273
270
|
},
|
|
274
271
|
|
|
@@ -23,13 +23,17 @@ export default {
|
|
|
23
23
|
async fetch() {
|
|
24
24
|
try {
|
|
25
25
|
this.uiPerfSetting = await this.$store.dispatch('management/find', { type: MANAGEMENT.SETTING, id: SETTING.UI_PERFORMANCE });
|
|
26
|
-
} catch
|
|
26
|
+
} catch {
|
|
27
27
|
this.uiPerfSetting = await this.$store.dispatch('management/create', { type: MANAGEMENT.SETTING }, { root: true });
|
|
28
28
|
// Setting does not exist - create a new one
|
|
29
29
|
this.uiPerfSetting.value = JSON.stringify(DEFAULT_PERF_SETTING);
|
|
30
30
|
this.uiPerfSetting.metadata = { name: SETTING.UI_PERFORMANCE };
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
try {
|
|
34
|
+
this.authUserTTL = await this.$store.dispatch(`management/find`, { type: MANAGEMENT.SETTING, id: SETTING.AUTH_USER_SESSION_TTL_MINUTES });
|
|
35
|
+
} catch {}
|
|
36
|
+
|
|
33
37
|
const sValue = this.uiPerfSetting?.value || JSON.stringify(DEFAULT_PERF_SETTING);
|
|
34
38
|
|
|
35
39
|
this.value = {
|
|
@@ -42,11 +46,13 @@ export default {
|
|
|
42
46
|
|
|
43
47
|
data() {
|
|
44
48
|
return {
|
|
45
|
-
uiPerfSetting:
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
49
|
+
uiPerfSetting: DEFAULT_PERF_SETTING,
|
|
50
|
+
authUserTTL: null,
|
|
51
|
+
bannerVal: {},
|
|
52
|
+
value: {},
|
|
53
|
+
errors: [],
|
|
54
|
+
gcStartedEnabled: null,
|
|
55
|
+
isInactivityThresholdValid: false,
|
|
50
56
|
};
|
|
51
57
|
},
|
|
52
58
|
|
|
@@ -56,9 +62,28 @@ export default {
|
|
|
56
62
|
|
|
57
63
|
return schema?.resourceMethods?.includes('PUT') ? _EDIT : _VIEW;
|
|
58
64
|
},
|
|
65
|
+
|
|
66
|
+
canSave() {
|
|
67
|
+
return this.value.inactivity.enabled ? this.isInactivityThresholdValid : true;
|
|
68
|
+
}
|
|
59
69
|
},
|
|
60
70
|
|
|
61
71
|
methods: {
|
|
72
|
+
validateInactivityThreshold(value) {
|
|
73
|
+
if (!this.authUserTTL?.value) {
|
|
74
|
+
this.isInactivityThresholdValid = true;
|
|
75
|
+
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (parseInt(value) > parseInt(this.authUserTTL?.value)) {
|
|
80
|
+
this.isInactivityThresholdValid = false;
|
|
81
|
+
|
|
82
|
+
return this.t('performance.inactivity.authUserTTL', { current: this.authUserTTL.value });
|
|
83
|
+
}
|
|
84
|
+
this.isInactivityThresholdValid = true;
|
|
85
|
+
},
|
|
86
|
+
|
|
62
87
|
async save(btnCB) {
|
|
63
88
|
this.uiPerfSetting.value = JSON.stringify(this.value);
|
|
64
89
|
this.errors = [];
|
|
@@ -89,8 +114,36 @@ export default {
|
|
|
89
114
|
</h1>
|
|
90
115
|
<div>
|
|
91
116
|
<div class="ui-perf-setting">
|
|
92
|
-
<!--
|
|
117
|
+
<!-- Inactivity -->
|
|
93
118
|
<div class="mt-20">
|
|
119
|
+
<h2>{{ t('performance.inactivity.title') }}</h2>
|
|
120
|
+
<p>{{ t('performance.inactivity.description') }}</p>
|
|
121
|
+
<Checkbox
|
|
122
|
+
v-model="value.inactivity.enabled"
|
|
123
|
+
:mode="mode"
|
|
124
|
+
:label="t('performance.inactivity.checkboxLabel')"
|
|
125
|
+
class="mt-10 mb-20"
|
|
126
|
+
:primary="true"
|
|
127
|
+
/>
|
|
128
|
+
<div class="ml-20">
|
|
129
|
+
<LabeledInput
|
|
130
|
+
v-model="value.inactivity.threshold"
|
|
131
|
+
:mode="mode"
|
|
132
|
+
:label="t('performance.inactivity.inputLabel')"
|
|
133
|
+
:disabled="!value.inactivity.enabled"
|
|
134
|
+
class="input mb-10"
|
|
135
|
+
type="number"
|
|
136
|
+
min="0"
|
|
137
|
+
:rules="[validateInactivityThreshold]"
|
|
138
|
+
/>
|
|
139
|
+
<span
|
|
140
|
+
v-clean-html="t('performance.inactivity.information', {}, true)"
|
|
141
|
+
:class="{ 'text-muted': !value.incrementalLoading.enabled }"
|
|
142
|
+
/>
|
|
143
|
+
</div>
|
|
144
|
+
</div>
|
|
145
|
+
<!-- Websocket Notifications -->
|
|
146
|
+
<div class="mt-40">
|
|
94
147
|
<h2>{{ t('performance.websocketNotification.label') }}</h2>
|
|
95
148
|
<p>{{ t('performance.websocketNotification.description') }}</p>
|
|
96
149
|
<Checkbox
|
|
@@ -296,6 +349,7 @@ export default {
|
|
|
296
349
|
<AsyncButton
|
|
297
350
|
class="pull-right mt-20"
|
|
298
351
|
mode="apply"
|
|
352
|
+
:disabled="!canSave"
|
|
299
353
|
@click="save"
|
|
300
354
|
/>
|
|
301
355
|
</div>
|
|
@@ -82,7 +82,7 @@ pushd ${BASE_DIR} > /dev/null
|
|
|
82
82
|
|
|
83
83
|
if [ "${GITHUB_BUILD}" == "true" ]; then
|
|
84
84
|
# Determine if gh-pages build is possible
|
|
85
|
-
if [[ "${GITHUB_BRANCH}" == "gh-pages" ]] && ! git show-ref -q
|
|
85
|
+
if [[ "${GITHUB_BRANCH}" == "gh-pages" ]] && ! git show-ref -q gh-pages; then
|
|
86
86
|
echo -e "${YELLOW}'gh-pages' branch not found, this branch must exist before running this script${RESET}"
|
|
87
87
|
exit 1
|
|
88
88
|
fi
|
package/store/index.js
CHANGED
|
@@ -1,32 +1,37 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
COUNT, NAMESPACE, NORMAN, MANAGEMENT, FLEET, UI, VIRTUAL_HARVESTER_PROVIDER, DEFAULT_WORKSPACE
|
|
4
|
-
} from '@shell/config/types';
|
|
5
|
-
import { CLUSTER as CLUSTER_PREF, NAMESPACE_FILTERS, LAST_NAMESPACE, WORKSPACE } from '@shell/store/prefs';
|
|
6
|
-
import { allHash, allHashSettled } from '@shell/utils/promise';
|
|
7
|
-
import { ClusterNotFoundError, ApiError } from '@shell/utils/error';
|
|
8
|
-
import { sortBy } from '@shell/utils/sort';
|
|
9
|
-
import { filterBy, findBy } from '@shell/utils/array';
|
|
10
|
-
import { BOTH, CLUSTER_LEVEL, NAMESPACED } from '@shell/store/type-map';
|
|
11
|
-
import { NAME as EXPLORER } from '@shell/config/product/explorer';
|
|
12
|
-
import { TIMED_OUT, LOGGED_OUT, _FLAGGED, UPGRADED } from '@shell/config/query-params';
|
|
1
|
+
import { BACK_TO } from '@shell/config/local-storage';
|
|
13
2
|
import { setBrand, setVendor } from '@shell/config/private-label';
|
|
14
|
-
import {
|
|
3
|
+
import { NAME as EXPLORER } from '@shell/config/product/explorer';
|
|
4
|
+
import { LOGGED_OUT, TIMED_OUT, UPGRADED, _FLAGGED } from '@shell/config/query-params';
|
|
15
5
|
import { SETTING } from '@shell/config/settings';
|
|
16
|
-
import
|
|
17
|
-
|
|
18
|
-
|
|
6
|
+
import {
|
|
7
|
+
COUNT,
|
|
8
|
+
DEFAULT_WORKSPACE,
|
|
9
|
+
FLEET,
|
|
10
|
+
MANAGEMENT,
|
|
11
|
+
NAMESPACE, NORMAN,
|
|
12
|
+
UI, VIRTUAL_HARVESTER_PROVIDER
|
|
13
|
+
} from '@shell/config/types';
|
|
19
14
|
import { BY_TYPE } from '@shell/plugins/dashboard-store/classify';
|
|
15
|
+
import Steve from '@shell/plugins/steve';
|
|
16
|
+
import { STEVE_MODEL_TYPES } from '@shell/plugins/steve/getters';
|
|
17
|
+
import { CLUSTER as CLUSTER_PREF, LAST_NAMESPACE, NAMESPACE_FILTERS, WORKSPACE } from '@shell/store/prefs';
|
|
18
|
+
import { BOTH, CLUSTER_LEVEL, NAMESPACED } from '@shell/store/type-map';
|
|
19
|
+
import { filterBy, findBy } from '@shell/utils/array';
|
|
20
|
+
import { ApiError, ClusterNotFoundError } from '@shell/utils/error';
|
|
21
|
+
import { gcActions, gcGetters } from '@shell/utils/gc/gc-root-store';
|
|
20
22
|
import {
|
|
21
|
-
NAMESPACE_FILTER_ALL_USER as ALL_USER,
|
|
22
|
-
NAMESPACE_FILTER_ALL_SYSTEM as ALL_SYSTEM,
|
|
23
23
|
NAMESPACE_FILTER_ALL_ORPHANS as ALL_ORPHANS,
|
|
24
|
-
|
|
24
|
+
NAMESPACE_FILTER_ALL_SYSTEM as ALL_SYSTEM,
|
|
25
|
+
NAMESPACE_FILTER_ALL_USER as ALL_USER,
|
|
25
26
|
NAMESPACE_FILTER_NAMESPACED_NO as NAMESPACED_NO,
|
|
26
27
|
NAMESPACE_FILTER_NAMESPACED_PREFIX as NAMESPACED_PREFIX,
|
|
28
|
+
NAMESPACE_FILTER_NAMESPACED_YES as NAMESPACED_YES,
|
|
27
29
|
splitNamespaceFilterKey,
|
|
28
30
|
} from '@shell/utils/namespace-filter';
|
|
29
|
-
import {
|
|
31
|
+
import { allHash, allHashSettled } from '@shell/utils/promise';
|
|
32
|
+
import { sortBy } from '@shell/utils/sort';
|
|
33
|
+
import { addParam } from '@shell/utils/url';
|
|
34
|
+
import semver from 'semver';
|
|
30
35
|
|
|
31
36
|
// Disables strict mode for all store instances to prevent warning about changing state outside of mutations
|
|
32
37
|
// because it's more efficient to do that sometimes.
|
|
@@ -591,7 +596,6 @@ export const mutations = {
|
|
|
591
596
|
state.isMultiCluster = isMultiCluster;
|
|
592
597
|
state.isRancher = isRancher;
|
|
593
598
|
},
|
|
594
|
-
|
|
595
599
|
clusterReady(state, ready) {
|
|
596
600
|
state.clusterReady = ready;
|
|
597
601
|
},
|
|
@@ -1122,5 +1126,16 @@ export const actions = {
|
|
|
1122
1126
|
commit(`setIsSingleProduct`, isSingleProduct);
|
|
1123
1127
|
},
|
|
1124
1128
|
|
|
1129
|
+
unsubscribe( { state, dispatch }) {
|
|
1130
|
+
// It would be nice to grab all vuex module stores that we've registered, apparently this is only possible via the
|
|
1131
|
+
// internal properties store._modules.root._children.
|
|
1132
|
+
// So instead loop through all state entries to find stores
|
|
1133
|
+
return Object.entries(state).filter(([storeName, storeState]) => {
|
|
1134
|
+
if (storeState?.allowStreaming) {
|
|
1135
|
+
dispatch(`${ storeName }/unsubscribe`);
|
|
1136
|
+
}
|
|
1137
|
+
});
|
|
1138
|
+
},
|
|
1139
|
+
|
|
1125
1140
|
...gcActions
|
|
1126
1141
|
};
|
package/utils/gc/gc.ts
CHANGED
|
@@ -29,7 +29,7 @@ class GarbageCollect {
|
|
|
29
29
|
* To avoid JSON.parse on the `ui-performance` setting keep a local cache
|
|
30
30
|
*/
|
|
31
31
|
private getUiPerfGarbageCollection = (rootState: any) => {
|
|
32
|
-
const uiPerfSetting = rootState.management.types[MANAGEMENT.SETTING]
|
|
32
|
+
const uiPerfSetting = rootState.management.types[MANAGEMENT.SETTING]?.list.find((s: any) => s.id === SETTING.UI_PERFORMANCE);
|
|
33
33
|
|
|
34
34
|
if (!uiPerfSetting || !uiPerfSetting.value) {
|
|
35
35
|
// Could be in the process of logging out
|