@rancher/shell 0.3.27 → 0.3.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/assets/translations/en-us.yaml +4 -20
  2. package/assets/translations/zh-hans.yaml +0 -23
  3. package/chart/gatekeeper.vue +2 -11
  4. package/chart/istio.vue +1 -10
  5. package/chart/logging/index.vue +2 -11
  6. package/chart/monitoring/index.vue +1 -9
  7. package/chart/rancher-backup/index.vue +1 -9
  8. package/components/Carousel.vue +2 -1
  9. package/components/formatter/ClusterProvider.vue +1 -18
  10. package/components/nav/WindowManager/ContainerLogs.vue +22 -19
  11. package/config/product/manager.js +0 -13
  12. package/config/types.js +0 -4
  13. package/edit/management.cattle.io.project.vue +1 -52
  14. package/edit/management.cattle.io.setting.vue +31 -2
  15. package/edit/provisioning.cattle.io.cluster/Basics.vue +6 -107
  16. package/edit/provisioning.cattle.io.cluster/__tests__/Basics.tests.ts +0 -3
  17. package/edit/provisioning.cattle.io.cluster/rke2.vue +3 -128
  18. package/middleware/authenticated.js +4 -2
  19. package/models/__tests__/management.cattle.io.cluster.test.ts +19 -0
  20. package/models/__tests__/provisioning.cattle.io.cluster.test.ts +90 -0
  21. package/models/cluster.x-k8s.io.machine.js +1 -1
  22. package/models/management.cattle.io.cluster.js +4 -0
  23. package/models/management.cattle.io.project.js +0 -36
  24. package/models/management.cattle.io.setting.js +11 -7
  25. package/models/provisioning.cattle.io.cluster.js +16 -4
  26. package/package.json +1 -1
  27. package/pages/auth/setup.vue +38 -1
  28. package/pages/c/_cluster/apps/charts/__tests__/install.helper.test.ts +2 -17
  29. package/pages/c/_cluster/apps/charts/index.vue +0 -15
  30. package/pages/c/_cluster/apps/charts/install.helpers.js +2 -13
  31. package/pages/c/_cluster/apps/charts/install.vue +1 -1
  32. package/pages/c/_cluster/explorer/index.vue +0 -47
  33. package/pages/c/_cluster/manager/pages/_page.vue +4 -5
  34. package/rancher-components/BadgeState/BadgeState.vue +1 -5
  35. package/rancher-components/Banner/Banner.test.ts +1 -51
  36. package/rancher-components/Banner/Banner.vue +53 -134
  37. package/rancher-components/Card/Card.vue +7 -24
  38. package/rancher-components/Form/Checkbox/Checkbox.test.ts +29 -20
  39. package/rancher-components/Form/Checkbox/Checkbox.vue +20 -45
  40. package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +8 -2
  41. package/rancher-components/Form/LabeledInput/LabeledInput.vue +10 -22
  42. package/rancher-components/Form/Radio/RadioButton.vue +13 -30
  43. package/rancher-components/Form/Radio/RadioGroup.vue +7 -26
  44. package/rancher-components/Form/TextArea/TextAreaAutoGrow.vue +6 -7
  45. package/rancher-components/Form/ToggleSwitch/ToggleSwitch.test.ts +38 -25
  46. package/rancher-components/Form/ToggleSwitch/ToggleSwitch.vue +11 -23
  47. package/rancher-components/LabeledTooltip/LabeledTooltip.vue +5 -19
  48. package/rancher-components/StringList/StringList.test.ts +49 -453
  49. package/rancher-components/StringList/StringList.vue +58 -92
  50. package/rancher-components/components/StringList/StringList.test.ts +270 -0
  51. package/rancher-components/components/StringList/StringList.vue +57 -18
  52. package/store/prefs.js +0 -3
  53. package/types/shell/index.d.ts +22 -16
  54. package/utils/custom-validators.js +0 -2
  55. package/utils/error.js +16 -1
  56. package/utils/validators/formRules/__tests__/index.test.ts +49 -4
  57. package/utils/validators/formRules/index.ts +12 -9
  58. package/utils/validators/setting.js +6 -10
  59. package/.DS_Store +0 -0
  60. package/components/ChartPsp.vue +0 -76
  61. package/components/__tests__/ChartPsp.test.ts +0 -75
  62. package/components/formatter/__tests__/ClusterProvider.test.ts +0 -28
  63. package/rancher-components/Card/Card.test.ts +0 -37
  64. package/rancher-components/Form/Radio/RadioButton.test.ts +0 -31
  65. package/yarn-error.log +0 -200
@@ -99,42 +99,6 @@ export default class Project extends HybridModel {
99
99
  // and the PUT request should have a query param _replace=true.
100
100
  const newValue = await norman.save({ replace: forceReplaceOnReq });
101
101
 
102
- let retryCount = 0;
103
-
104
- const finishProjectCreation = async() => {
105
- try {
106
- await newValue.doAction('setpodsecuritypolicytemplate', { podSecurityPolicyTemplateId: this.spec.podSecurityPolicyTemplateId || null });
107
- } catch (err) {
108
- if ( err.status === 409 || err.status === 403 ) {
109
- // The backend updates each new project soon after it is created,
110
- // so there is a chance of a 409 resource conflict or forbidden error. If that happens,
111
- // retry the action.
112
-
113
- // The 403 permission error can happen due to the user requesting
114
- // the project before the project is fully created and the project
115
- // permissions are assigned to the user so we allow some
116
- // time for that process to complete.
117
-
118
- if (retryCount < 3) {
119
- retryCount++;
120
- await setTimeout(() => {
121
- // Delay for three seconds to avoid another failure due to latency.
122
- finishProjectCreation();
123
- }, '3000');
124
- } else {
125
- throw err;
126
- }
127
- }
128
- }
129
- };
130
-
131
- // Only update PSP template if the value changed
132
- const newPSPTemplateID = this.spec.podSecurityPolicyTemplateId || null;
133
-
134
- if (norman.podSecurityPolicyTemplateId !== newPSPTemplateID) {
135
- await finishProjectCreation();
136
- }
137
-
138
102
  return newValue;
139
103
  }
140
104
 
@@ -1,5 +1,6 @@
1
1
  import { ALLOWED_SETTINGS } from '@shell/config/settings';
2
2
  import HybridModel from '@shell/plugins/steve/hybrid-class';
3
+ import { isServerUrl } from '@shell/utils/validators/setting';
3
4
 
4
5
  export default class Setting extends HybridModel {
5
6
  get fromEnv() {
@@ -31,12 +32,15 @@ export default class Setting extends HybridModel {
31
32
  }
32
33
 
33
34
  get customValidationRules() {
34
- return [
35
- {
36
- path: 'value',
37
- translationKey: 'setting.serverUrl.https',
38
- validators: [`isHttps:${ this.metadata.name }`]
39
- },
40
- ];
35
+ const out = [];
36
+
37
+ if (isServerUrl(this.metadata.name)) {
38
+ out.push({
39
+ path: 'value',
40
+ validators: ['required', 'https', 'url', 'trailingForwardSlash']
41
+ });
42
+ }
43
+
44
+ return out;
41
45
  }
42
46
  }
@@ -239,10 +239,24 @@ export default class ProvCluster extends SteveModel {
239
239
  return providers.includes(this.provisioner);
240
240
  }
241
241
 
242
+ get isLocal() {
243
+ return this.mgmt?.isLocal;
244
+ }
245
+
242
246
  get isImported() {
243
247
  // As of Rancher v2.6.7, this returns false for imported K3s clusters,
244
248
  // in which this.provisioner is `k3s`.
245
- return this.provisioner === 'imported';
249
+
250
+ const isImportedProvisioner = this.provisioner === 'imported';
251
+ const isImportedSpecialCases = this.mgmt?.providerForEmberParam === 'import' ||
252
+ // when imported cluster is GKE
253
+ !!this.mgmt?.spec?.gkeConfig?.imported ||
254
+ // or AKS
255
+ !!this.mgmt?.spec?.aksConfig?.imported ||
256
+ // or EKS
257
+ !!this.mgmt?.spec?.eksConfig?.imported;
258
+
259
+ return !this.isLocal && (isImportedProvisioner || (!this.isRke2 && !this.mgmt?.machineProvider && isImportedSpecialCases));
246
260
  }
247
261
 
248
262
  get isCustom() {
@@ -262,8 +276,6 @@ export default class ProvCluster extends SteveModel {
262
276
  }
263
277
 
264
278
  get isImportedK3s() {
265
- // As of Rancher v2.6.7, this returns false for imported K3s clusters,
266
- // in which this.provisioner is `k3s`.
267
279
  return this.isImported && this.isK3s;
268
280
  }
269
281
 
@@ -280,7 +292,7 @@ export default class ProvCluster extends SteveModel {
280
292
  }
281
293
 
282
294
  get isRke1() {
283
- return !!this.mgmt?.spec?.rancherKubernetesEngineConfig;
295
+ return !!this.mgmt?.spec?.rancherKubernetesEngineConfig || this.labels['provider.cattle.io'] === 'rke';
284
296
  }
285
297
 
286
298
  get isHarvester() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rancher/shell",
3
- "version": "0.3.27",
3
+ "version": "0.3.28",
4
4
  "description": "Rancher Dashboard Shell",
5
5
  "repository": "https://github.com/rancherlabs/dashboard",
6
6
  "license": "Apache-2.0",
@@ -18,6 +18,10 @@ import Password from '@shell/components/form/Password';
18
18
  import { applyProducts } from '@shell/store/type-map';
19
19
  import BrandImage from '@shell/components/BrandImage';
20
20
  import { waitFor } from '@shell/utils/async';
21
+ import { Banner } from '@components/Banner';
22
+ import FormValidation from '@shell/mixins/form-validation';
23
+ import isUrl from 'is-url';
24
+ import { isLocalhost } from '@shell/utils/validators/setting';
21
25
 
22
26
  const calcIsFirstLogin = (store) => {
23
27
  const firstLoginSetting = store.getters['management/byId'](MANAGEMENT.SETTING, SETTING.FIRST_LOGIN);
@@ -36,11 +40,18 @@ const calcMustChangePassword = async(store) => {
36
40
  export default {
37
41
  layout: 'unauthenticated',
38
42
 
43
+ mixins: [FormValidation],
44
+
39
45
  data() {
40
46
  return {
41
47
  passwordOptions: [
42
48
  { label: this.t('setup.useRandom'), value: true },
43
49
  { label: this.t('setup.useManual'), value: false }],
50
+ fvFormRuleSets: [{
51
+ path: 'serverUrl',
52
+ rootObject: this,
53
+ rules: ['required', 'https', 'url', 'trailingForwardSlash']
54
+ }]
44
55
  };
45
56
  },
46
57
 
@@ -78,7 +89,7 @@ export default {
78
89
  },
79
90
 
80
91
  components: {
81
- AsyncButton, LabeledInput, CopyToClipboard, Checkbox, RadioGroup, Password, BrandImage
92
+ AsyncButton, LabeledInput, CopyToClipboard, Checkbox, RadioGroup, Password, BrandImage, Banner
82
93
  },
83
94
 
84
95
  async asyncData({ route, req, store }) {
@@ -191,6 +202,10 @@ export default {
191
202
  }
192
203
  }
193
204
 
205
+ if (!isUrl(this.serverUrl) || this.fvGetPathErrors(['serverUrl']).length > 0) {
206
+ return false;
207
+ }
208
+
194
209
  return true;
195
210
  },
196
211
 
@@ -198,6 +213,10 @@ export default {
198
213
  const out = findBy(this.principals, 'me', true);
199
214
 
200
215
  return out;
216
+ },
217
+
218
+ showLocalhostWarning() {
219
+ return isLocalhost(this.serverUrl);
201
220
  }
202
221
  },
203
222
 
@@ -265,6 +284,10 @@ export default {
265
284
  done() {
266
285
  this.$router.replace('/');
267
286
  },
287
+
288
+ onServerUrlChange(value) {
289
+ this.serverUrl = value.trim();
290
+ },
268
291
  },
269
292
  };
270
293
  </script>
@@ -368,10 +391,24 @@ export default {
368
391
  />
369
392
  </p>
370
393
  <div class="mt-20">
394
+ <Banner
395
+ v-if="showLocalhostWarning"
396
+ color="warning"
397
+ :label="t('validation.setting.serverUrl.localhost')"
398
+ />
399
+ <Banner
400
+ v-for="(err, i) in fvGetPathErrors(['serverUrl'])"
401
+ :key="i"
402
+ color="error"
403
+ :label="err"
404
+ />
371
405
  <LabeledInput
372
406
  v-model="serverUrl"
373
407
  :label="t('setup.serverUrl.label')"
374
408
  data-testid="setup-server-url"
409
+ :rules="fvGetAndReportPathRules('serverUrl')"
410
+ :required="true"
411
+ @input="onServerUrlChange"
375
412
  />
376
413
  </div>
377
414
  </template>
@@ -2,30 +2,15 @@ import { ignoreVariables } from '@shell/pages/c/_cluster/apps/charts/install.hel
2
2
 
3
3
  describe('fX: ignoreVariables', () => {
4
4
  describe.each([['epinio', 'global.rbac.pspEnabled']])('given chart %p with path %p', (name, path) => {
5
- it.each([
6
- ['v1.24.11+rke2r1'],
7
- ])('should not return variable path list if cluster has k8s version %p', (version) => {
8
- const cluster = { kubernetesVersion: version };
9
- const data = {
10
- chart: { name },
11
- values: { global: { rbac: { pspEnabled: undefined } } }
12
- };
13
-
14
- const paths = ignoreVariables(cluster, data);
15
-
16
- expect(paths).toStrictEqual([]);
17
- });
18
-
19
5
  it.each([
20
6
  ['v1.25.11+rke2r1'],
21
- ])('should return questions if cluster has k8s version %p', (version) => {
22
- const cluster = { kubernetesVersion: version };
7
+ ])('should return questions', () => {
23
8
  const data = {
24
9
  chart: { name },
25
10
  values: { global: { rbac: { pspEnabled: undefined } } }
26
11
  };
27
12
 
28
- const paths = ignoreVariables(cluster, data);
13
+ const paths = ignoreVariables(data);
29
14
 
30
15
  expect(paths).toStrictEqual([path]);
31
16
  });
@@ -54,7 +54,6 @@ export default {
54
54
  searchQuery: null,
55
55
  showDeprecated: null,
56
56
  showHidden: null,
57
- isPspLegacy: false,
58
57
  chartOptions: [
59
58
  {
60
59
  label: 'Browse',
@@ -241,14 +240,6 @@ export default {
241
240
  }
242
241
  },
243
242
 
244
- created() {
245
- const release = this.currentCluster?.status?.version?.gitVersion || '';
246
- const isRKE2 = release.includes('rke2');
247
- const version = release.match(/\d+/g);
248
-
249
- this.isPspLegacy = version?.length ? isRKE2 && (+version[0] === 1 && +version[1] < 25) : false;
250
- },
251
-
252
243
  methods: {
253
244
  colorForChart(chart) {
254
245
  const repos = this.repoOptions;
@@ -377,12 +368,6 @@ export default {
377
368
  />
378
369
  </div>
379
370
 
380
- <Banner
381
- v-if="isPspLegacy"
382
- color="warning"
383
- :label="t('catalog.chart.banner.legacy')"
384
- />
385
-
386
371
  <TypeDescription resource="chart" />
387
372
  <div class="left-right-split">
388
373
  <Select
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Return list of variables to filter chart questions
3
3
  */
4
- export const ignoreVariables = (cluster, data) => {
4
+ export const ignoreVariables = (data) => {
5
5
  const pspChartMap = {
6
6
  epinio: 'global.rbac.pspEnabled',
7
7
  longhorn: 'enablePSP',
@@ -11,16 +11,5 @@ export const ignoreVariables = (cluster, data) => {
11
11
  };
12
12
  const path = pspChartMap[data.chart.name];
13
13
 
14
- if (path) {
15
- const clusterVersion = cluster?.kubernetesVersion || '';
16
- const version = clusterVersion.match(/\d+/g);
17
- const isRequiredVersion = version?.length ? +version[0] === 1 && +version[1] < 25 : false;
18
-
19
- // Provide path as question variable to be ignored
20
- if (!isRequiredVersion) {
21
- return [path];
22
- }
23
- }
24
-
25
- return [];
14
+ return [path];
26
15
  };
@@ -473,7 +473,7 @@ export default {
473
473
  * Return list of variables to filter chart questions
474
474
  */
475
475
  ignoreVariables() {
476
- return ignoreVariables(this.currentCluster, this.versionInfo);
476
+ return ignoreVariables(this.versionInfo);
477
477
  },
478
478
 
479
479
  namespaceIsNew() {
@@ -15,7 +15,6 @@ import {
15
15
  WORKLOAD_TYPES,
16
16
  COUNT,
17
17
  CATALOG,
18
- PSP,
19
18
  } from '@shell/config/types';
20
19
  import { setPromiseResult } from '@shell/utils/promise';
21
20
  import AlertTable from '@shell/components/AlertTable';
@@ -27,7 +26,6 @@ import {
27
26
  STATE,
28
27
  } from '@shell/config/table-headers';
29
28
 
30
- import { mapPref, PSP_DEPRECATION_BANNER } from '@shell/store/prefs';
31
29
  import { haveV1Monitoring, monitoringStatus, canViewGrafanaLink } from '@shell/utils/monitoring';
32
30
  import Tabbed from '@shell/components/Tabbed';
33
31
  import Tab from '@shell/components/Tabbed/Tab';
@@ -156,20 +154,6 @@ export default {
156
154
  ...mapGetters(['currentCluster']),
157
155
  ...monitoringStatus(),
158
156
 
159
- displayPspDeprecationBanner() {
160
- const cluster = this.currentCluster;
161
- const major = cluster.status?.version?.major ? parseInt(cluster.status?.version?.major) : 0;
162
- const minor = cluster.status?.version?.minor ? parseInt(cluster.status?.version?.minor) : 0;
163
-
164
- if (major === 1 && minor >= 21 && minor < 25) {
165
- const clusterCounts = this.$store.getters[`cluster/all`](COUNT)?.[0]?.counts;
166
-
167
- return !!clusterCounts?.[PSP]?.summary?.count;
168
- }
169
-
170
- return false;
171
- },
172
-
173
157
  nodes() {
174
158
  return this.$store.getters['cluster/all'](NODE);
175
159
  },
@@ -178,8 +162,6 @@ export default {
178
162
  return this.$store.getters['management/all'](MANAGEMENT.CLUSTER);
179
163
  },
180
164
 
181
- hidePspDeprecationBanner: mapPref(PSP_DEPRECATION_BANNER),
182
-
183
165
  hasV1Monitoring() {
184
166
  return haveV1Monitoring(this.$store.getters);
185
167
  },
@@ -434,17 +416,6 @@ export default {
434
416
  </div>
435
417
  </div>
436
418
  </header>
437
- <Banner
438
- v-if="displayPspDeprecationBanner && !hidePspDeprecationBanner"
439
- :closable="true"
440
- color="warning"
441
- @close="hidePspDeprecationBanner = true"
442
- >
443
- <t
444
- k="landing.deprecatedPsp"
445
- :raw="true"
446
- />
447
- </Banner>
448
419
  <div
449
420
  class="cluster-dashboard-glance"
450
421
  >
@@ -478,14 +449,6 @@ export default {
478
449
  :show-tooltip="true"
479
450
  /></span>
480
451
  </div>
481
- <p
482
- v-if="displayPspDeprecationBanner && hidePspDeprecationBanner"
483
- v-clean-tooltip="t('landing.deprecatedPsp')"
484
- class="alt-psp-deprecation-info"
485
- >
486
- <span>{{ t('landing.psps') }}</span>
487
- <i class="icon icon-warning" />
488
- </p>
489
452
  <div :style="{'flex':1}" />
490
453
  <div v-if="!monitoringStatus.v2 && !monitoringStatus.v1">
491
454
  <n-link
@@ -734,16 +697,6 @@ export default {
734
697
  margin-top: 0;
735
698
  }
736
699
 
737
- .alt-psp-deprecation-info {
738
- display: flex;
739
- align-items: center;
740
- color: var(--warning);
741
-
742
- span {
743
- margin-right: 4px;
744
- }
745
- }
746
-
747
700
  .monitoring-install {
748
701
  display: flex;
749
702
  margin-left: 10px;
@@ -7,11 +7,10 @@ export default {
7
7
  data() {
8
8
  return {
9
9
  PAGES: {
10
- 'rke-drivers': '/n/drivers/cluster',
11
- 'rke-templates': '/g/rke-templates/index',
12
- 'cloud-credentials': '/g/security/cloud-credentials',
13
- 'node-templates': '/n/node-templates',
14
- 'pod-security-policies': '/g/security/policies',
10
+ 'rke-drivers': '/n/drivers/cluster',
11
+ 'rke-templates': '/g/rke-templates/index',
12
+ 'cloud-credentials': '/g/security/cloud-credentials',
13
+ 'node-templates': '/n/node-templates',
15
14
  }
16
15
  };
17
16
  }
@@ -60,11 +60,7 @@ export default Vue.extend({
60
60
 
61
61
  <template>
62
62
  <span :class="{'badge-state': true, [bg]: true}">
63
- <i
64
- v-if="icon"
65
- class="icon"
66
- :class="{[icon]: true, 'mr-5': !!msg}"
67
- />{{ msg }}
63
+ <i v-if="icon" class="icon" :class="{[icon]: true, 'mr-5': !!msg}" />{{ msg }}
68
64
  </span>
69
65
  </template>
70
66
 
@@ -1,63 +1,13 @@
1
1
  import { mount } from '@vue/test-utils';
2
2
  import { Banner } from './index';
3
- import { cleanHtmlDirective } from '@shell/plugins/clean-html-directive';
4
3
 
5
4
  describe('component: Banner', () => {
6
5
  it('should display text based on label', () => {
7
6
  const label = 'test';
8
- const wrapper = mount(
9
- Banner,
10
- {
11
- directives: { cleanHtmlDirective },
12
- propsData: { label }
13
- });
7
+ const wrapper = mount(Banner, { propsData: { label } });
14
8
 
15
9
  const element = wrapper.find('span').element;
16
10
 
17
11
  expect(element.textContent).toBe(label);
18
12
  });
19
-
20
- it('should display an icon', () => {
21
- const icon = 'my-icon';
22
- const wrapper = mount(Banner, { propsData: { icon } });
23
-
24
- const element = wrapper.find(`.${ icon }`).element;
25
-
26
- expect(element.classList).toContain(icon);
27
- });
28
-
29
- it('should not display an icon', () => {
30
- const wrapper = mount(Banner);
31
-
32
- const element = wrapper.find(`[data-testid="banner-icon"]`).element;
33
-
34
- expect(element).not.toBeDefined();
35
- });
36
-
37
- it('should emit close event', () => {
38
- const wrapper = mount(Banner, { propsData: { closable: true } });
39
- const element = wrapper.find(`[data-testid="banner-close"]`).element;
40
-
41
- element.click();
42
-
43
- expect(wrapper.emitted('close')).toHaveLength(1);
44
- });
45
-
46
- it('should add the right color', () => {
47
- const color = 'red';
48
- const wrapper = mount(Banner, { propsData: { color } });
49
-
50
- const element = wrapper.element;
51
-
52
- expect(element.classList).toContain(color);
53
- });
54
-
55
- it('should stack the banner messages', () => {
56
- const stacked = true;
57
- const wrapper = mount(Banner, { propsData: { stacked } });
58
-
59
- const element = wrapper.find(`[data-testid="banner-content"]`).element;
60
-
61
- expect(element.classList).toContain('stacked');
62
- });
63
13
  });