@rancher/shell 0.3.7 → 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.
@@ -1249,7 +1249,7 @@ cluster:
1249
1249
  memory: Memory
1250
1250
  disk: Disk
1251
1251
  image: Image
1252
- network:
1252
+ network:
1253
1253
  title: Networks
1254
1254
  network: Network
1255
1255
  addNetwork: Add Network
@@ -1827,7 +1827,7 @@ cluster:
1827
1827
  option: Default - RKE2 Embedded
1828
1828
  defaultPodSecurityAdmissionConfigurationTemplateName:
1829
1829
  label: Pod Security Admission Configuration Template
1830
- option:
1830
+ option:
1831
1831
  none: (None)
1832
1832
  default: Default - RKE2 Embedded
1833
1833
  cisProfile:
@@ -2202,16 +2202,16 @@ fleet:
2202
2202
  error: Error
2203
2203
  ready: Ready
2204
2204
  errors: Errors
2205
- workspaces:
2205
+ workspaces:
2206
2206
  tabs:
2207
2207
  restrictions: Allowed Target Namespaces
2208
2208
  timeout: Workspace creation timeout. It's possible the workspace was created. We suggest checking the workspace page before trying to create another.
2209
- restrictions:
2209
+ restrictions:
2210
2210
  addTitle: 'allowedTargetNamespaces'
2211
2211
  addLabel: Add
2212
2212
  banner: "{count, plural,
2213
2213
  =0 { Adding namespaces here will create a GitRepoRestriction. }
2214
- 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}
2215
2215
  }"
2216
2216
  footer:
2217
2217
  docs: Docs
@@ -2260,7 +2260,7 @@ gatekeeperIndex:
2260
2260
  unavailable: OPA + Gatekeeper is not available in the system-charts catalog.
2261
2261
  violations: Violations
2262
2262
 
2263
- gatekeeperInstall:
2263
+ gatekeeperInstall:
2264
2264
  auditInterval: Auto Interval
2265
2265
  constraintViolationsLimit: Constraint Violations Limit
2266
2266
  runtimeDefaultSeccompProfile: Enable Runtime Default Seccomp Profile
@@ -3952,6 +3952,15 @@ podDisruptionBudget:
3952
3952
  maxUnavailable:
3953
3953
  label: Max. unavailable Pods
3954
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
3955
3964
 
3956
3965
  # Rancher Extensions
3957
3966
  plugins:
@@ -4051,7 +4060,7 @@ plugins:
4051
4060
  podSecurityAdmission:
4052
4061
  name: Pod Security Admission
4053
4062
  description: Define the admission control mode you want to use for the pod security
4054
- banner:
4063
+ banner:
4055
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'
4056
4065
  labels:
4057
4066
  enforce: Enforce
@@ -4065,7 +4074,7 @@ podSecurityAdmission:
4065
4074
  restricted: restricted
4066
4075
  version:
4067
4076
  placeholder: 'Version (default: latest)'
4068
- exemptions:
4077
+ exemptions:
4069
4078
  title: Exemptions
4070
4079
  description: Allow the creation of pods for specific Usernames, RuntimeClassNames, and Namespaces that would otherwise be prohibited due to the policies set above.
4071
4080
  placeholder: Enter a comma separated list of {psaExemptionsControl}
@@ -5186,7 +5195,7 @@ storageClass:
5186
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>'
5187
5196
  harvesterhci:
5188
5197
  title: Harvester (CSI)
5189
- warning:
5198
+ warning:
5190
5199
  unSatisfiesVersion: Please upgrade your Harvester CSI driver version to use this feature. (csi-driver >= v0.1.15)
5191
5200
  hostStorageClass:
5192
5201
  label: Host Storage Class
@@ -6131,8 +6140,6 @@ workload:
6131
6140
  pod: Pod
6132
6141
  containers: Containers
6133
6142
 
6134
-
6135
-
6136
6143
  ##############################
6137
6144
  # Model Properties
6138
6145
  ##############################
@@ -6778,7 +6785,7 @@ typeLabel:
6778
6785
  {count, plural,
6779
6786
  one { Cluster Registration Token }
6780
6787
  other { Cluster Registration Tokens }
6781
- }
6788
+ }
6782
6789
 
6783
6790
  action:
6784
6791
  clone: Clone
@@ -7002,6 +7009,14 @@ performance:
7002
7009
  label: Websocket Web Worker
7003
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.
7004
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
+
7005
7020
 
7006
7021
  banner:
7007
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>
@@ -140,6 +140,10 @@ export const ALLOWED_SETTINGS: GlobalSetting = {
140
140
  };
141
141
 
142
142
  export const DEFAULT_PERF_SETTING = {
143
+ inactivity: {
144
+ enabled: false,
145
+ threshold: 900,
146
+ },
143
147
  incrementalLoading: {
144
148
  enabled: true,
145
149
  threshold: 1500,
@@ -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
@@ -12,6 +12,7 @@ const targets = {
12
12
  const files = [
13
13
  'tsconfig.json',
14
14
  'vue.config.js',
15
+ '.gitignore',
15
16
  '.eslintignore',
16
17
  '.eslintrc.js',
17
18
  'babel.config.js',
@@ -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.machinePoolDefaults?.hostnameLengthLimit;
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.machinePoolDefaults = this.value.machinePoolDefaults || {};
1104
- this.value.machinePoolDefaults.hostnameLengthLimit = 15;
1097
+ this.value.defaultHostnameLengthLimit = NETBIOS_TRUNCATION_LENGTH;
1105
1098
  } else {
1106
- delete this.value.machinePoolDefaults.hostnameLengthLimit;
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
 
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rancher/shell",
3
- "version": "0.3.7",
3
+ "version": "0.3.8",
4
4
  "description": "Rancher Dashboard Shell",
5
5
  "repository": "https://github.com/rancherlabs/dashboard",
6
6
  "license": "Apache-2.0",
@@ -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.total
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 (e) {
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: DEFAULT_PERF_SETTING,
46
- bannerVal: {},
47
- value: {},
48
- errors: [],
49
- gcStartedEnabled: null
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
- <!-- Websocket Notifications -->
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>
package/store/index.js CHANGED
@@ -1,32 +1,37 @@
1
- import Steve from '@shell/plugins/steve';
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 { addParam } from '@shell/utils/url';
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 semver from 'semver';
17
- import { BACK_TO } from '@shell/config/local-storage';
18
- import { STEVE_MODEL_TYPES } from '@shell/plugins/steve/getters';
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
- NAMESPACE_FILTER_NAMESPACED_YES as NAMESPACED_YES,
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 { gcActions, gcGetters } from '@shell/utils/gc/gc-root-store';
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
  };