@rancher/shell 2.0.0 → 2.0.1

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 (73) hide show
  1. package/assets/translations/en-us.yaml +18 -3
  2. package/components/AlertTable.vue +17 -7
  3. package/components/GrafanaDashboard.vue +6 -4
  4. package/components/PromptRemove.vue +1 -0
  5. package/components/form/KeyValue.vue +1 -0
  6. package/components/form/Taints.vue +13 -7
  7. package/components/form/__tests__/Taints.test.ts +70 -0
  8. package/components/nav/Header.vue +1 -1
  9. package/components/nav/TopLevelMenu.vue +1 -4
  10. package/config/product/auth.js +1 -1
  11. package/config/router/navigation-guards/i18n.js +13 -0
  12. package/config/router/navigation-guards/index.js +2 -1
  13. package/creators/app/app.package.json +2 -1
  14. package/detail/__tests__/provisioning.cattle.io.cluster.test.ts +42 -0
  15. package/detail/provisioning.cattle.io.cluster.vue +4 -4
  16. package/dialog/DeactivateDriverDialog.vue +30 -11
  17. package/edit/auth/__tests__/oidc.test.ts +2 -2
  18. package/edit/token.vue +2 -1
  19. package/initialize/entry-helpers.js +10 -13
  20. package/list/management.cattle.io.feature.vue +4 -2
  21. package/middleware/authenticated.js +0 -19
  22. package/mixins/auth-config.js +1 -1
  23. package/models/driver.js +3 -2
  24. package/models/kontainerdriver.js +30 -13
  25. package/models/management.cattle.io.authconfig.js +2 -2
  26. package/models/nodedriver.js +30 -13
  27. package/package.json +3 -2
  28. package/pages/c/_cluster/apps/charts/install.vue +3 -2
  29. package/pages/c/_cluster/manager/drivers/kontainerDriver/index.vue +0 -3
  30. package/pages/c/_cluster/manager/drivers/nodeDriver/index.vue +1 -4
  31. package/pages/c/_cluster/uiplugins/InstallDialog.vue +2 -1
  32. package/promptRemove/pod.vue +15 -7
  33. package/scripts/publish-shell.sh +1 -0
  34. package/store/auth.js +1 -1
  35. package/store/index.js +1 -1
  36. package/utils/__tests__/kontainer.test.ts +89 -1
  37. package/utils/auth.js +1 -1
  38. package/utils/kontainer.ts +5 -1
  39. package/utils/version.js +2 -1
  40. package/rancher-components/components/Accordion/Accordion.test.ts +0 -45
  41. package/rancher-components/components/Accordion/Accordion.vue +0 -86
  42. package/rancher-components/components/Accordion/index.ts +0 -1
  43. package/rancher-components/components/BadgeState/BadgeState.test.ts +0 -12
  44. package/rancher-components/components/BadgeState/BadgeState.vue +0 -111
  45. package/rancher-components/components/BadgeState/index.ts +0 -1
  46. package/rancher-components/components/Banner/Banner.test.ts +0 -59
  47. package/rancher-components/components/Banner/Banner.vue +0 -244
  48. package/rancher-components/components/Banner/index.ts +0 -1
  49. package/rancher-components/components/Card/Card.test.ts +0 -37
  50. package/rancher-components/components/Card/Card.vue +0 -167
  51. package/rancher-components/components/Card/index.ts +0 -1
  52. package/rancher-components/components/Form/Checkbox/Checkbox.test.ts +0 -68
  53. package/rancher-components/components/Form/Checkbox/Checkbox.vue +0 -421
  54. package/rancher-components/components/Form/Checkbox/index.ts +0 -1
  55. package/rancher-components/components/Form/LabeledInput/LabeledInput.test.ts +0 -40
  56. package/rancher-components/components/Form/LabeledInput/LabeledInput.vue +0 -402
  57. package/rancher-components/components/Form/LabeledInput/index.ts +0 -1
  58. package/rancher-components/components/Form/Radio/RadioButton.test.ts +0 -33
  59. package/rancher-components/components/Form/Radio/RadioButton.vue +0 -293
  60. package/rancher-components/components/Form/Radio/RadioGroup.test.ts +0 -30
  61. package/rancher-components/components/Form/Radio/RadioGroup.vue +0 -259
  62. package/rancher-components/components/Form/Radio/index.ts +0 -2
  63. package/rancher-components/components/Form/TextArea/TextAreaAutoGrow.vue +0 -172
  64. package/rancher-components/components/Form/TextArea/index.ts +0 -1
  65. package/rancher-components/components/Form/ToggleSwitch/ToggleSwitch.test.ts +0 -94
  66. package/rancher-components/components/Form/ToggleSwitch/ToggleSwitch.vue +0 -152
  67. package/rancher-components/components/Form/ToggleSwitch/index.ts +0 -1
  68. package/rancher-components/components/Form/index.ts +0 -5
  69. package/rancher-components/components/LabeledTooltip/LabeledTooltip.vue +0 -156
  70. package/rancher-components/components/LabeledTooltip/index.ts +0 -1
  71. package/rancher-components/components/StringList/StringList.test.ts +0 -754
  72. package/rancher-components/components/StringList/StringList.vue +0 -650
  73. package/rancher-components/components/StringList/index.ts +0 -1
@@ -572,7 +572,7 @@ authConfig:
572
572
  title: Are you sure? This update is irreversible.
573
573
  body: '<p><b>You may need to make some additional changes</b>. Please ensure the Azure AD app has the Directory.Read.All <b>Application</b> permission added to Microsoft Graph.<br> If any endpoints were customized while configuring Azure AD authentication in Rancher, they will not be automatically updated. </p>'
574
574
  oidc:
575
- oidc: Configure an OIDC account
575
+ genericoidc: Configure an OIDC account
576
576
  keycloakoidc: Configure a Keycloak OIDC account
577
577
  rancherUrl: Rancher URL
578
578
  clientId: Client ID
@@ -2225,7 +2225,22 @@ drivers:
2225
2225
  refresh: Refresh Kubernetes Metadata
2226
2226
  deactivate:
2227
2227
  title: Are you sure?
2228
- warning: You will no longer be able to edit the configuration of clusters using {name} driver. Resources in the provider will not be automatically removed.
2228
+ andOthers: |-
2229
+ {count, plural,
2230
+ =0 {}
2231
+ =1 { and <b>one other </b>}
2232
+ other { and <b>{count} other </b>}
2233
+ }
2234
+ warningDrivers: |-
2235
+ {count, plural,
2236
+ =1 { You will no longer be able to edit the configuration of clusters using {names} driver.}
2237
+ other { You will no longer be able to edit the configuration of clusters using {names} drivers.}
2238
+ }
2239
+ warning: |-
2240
+ {count, plural,
2241
+ =1 { {warningDrivers} Resources in the corresponding provider will not be automatically removed.}
2242
+ other { {warningDrivers} Resources in the corresponding providers will not be automatically removed.}
2243
+ }
2229
2244
 
2230
2245
  detailText:
2231
2246
  collapse: Hide
@@ -6582,7 +6597,7 @@ model:
6582
6597
  okta: Okta
6583
6598
  freeipa: FreeIPA
6584
6599
  googleoauth: Google
6585
- oidc: Generic OIDC
6600
+ genericoidc: Generic OIDC
6586
6601
  keycloakoidc: Keycloak
6587
6602
 
6588
6603
  cluster:
@@ -49,6 +49,7 @@ export default {
49
49
  ];
50
50
 
51
51
  return {
52
+ inStore: this.$store.getters['currentProduct'].inStore,
52
53
  alertManagerPoller: new Poller(
53
54
  this.loadAlertManagerEvents,
54
55
  ALERTMANAGER_POLL_RATE_MS,
@@ -69,15 +70,24 @@ export default {
69
70
  },
70
71
 
71
72
  methods: {
72
- async loadAlertManagerEvents() {
73
- const inStore = this.$store.getters['currentProduct'].inStore;
74
- const alertsEvents = await this.$store.dispatch(
75
- `${ inStore }/request`,
76
- { url: `/k8s/clusters/${ this.currentCluster.id }/api/v1/namespaces/${ this.monitoringNamespace }/services/http:${ this.alertServiceEndpoint }:9093/proxy/api/v1/alerts` }
73
+ async fetchAlertManagerEvents(version) {
74
+ return await this.$store.dispatch(
75
+ `${ this.inStore }/request`,
76
+ { url: `/k8s/clusters/${ this.currentCluster.id }/api/v1/namespaces/${ this.monitoringNamespace }/services/http:${ this.alertServiceEndpoint }:9093/proxy/api/${ version }/alerts` }
77
77
  );
78
+ },
79
+
80
+ async loadAlertManagerEvents() {
81
+ let alertEvents;
82
+
83
+ try {
84
+ alertEvents = await this.fetchAlertManagerEvents('v2');
85
+ } catch (err) {
86
+ alertEvents = await this.fetchAlertManagerEvents('v1').then((res) => res?.data);
87
+ }
78
88
 
79
- if (alertsEvents.data) {
80
- this.allAlerts = alertsEvents.data;
89
+ if (alertEvents) {
90
+ this.allAlerts = alertEvents;
81
91
  }
82
92
  },
83
93
 
@@ -114,10 +114,12 @@ export default {
114
114
  this.interval = setInterval(() => {
115
115
  try {
116
116
  const graphWindow = this.$refs.frame?.contentWindow;
117
- const errorElements = graphWindow.document.getElementsByClassName('alert-error');
118
- const errorCornerElements = graphWindow.document.getElementsByClassName('panel-info-corner--error');
119
- const panelInFullScreenElements = graphWindow.document.getElementsByClassName('panel-in-fullscreen');
120
- const panelContainerElements = graphWindow.document.getElementsByClassName('panel-container');
117
+
118
+ // Note. getElementsByClassName won't work, following a grafana bump class names are now unique - for example css-2qng6u-panel-container
119
+ const errorElements = graphWindow.document.querySelectorAll('[class$="alert-error');
120
+ const errorCornerElements = graphWindow.document.querySelectorAll('[class$="panel-info-corner--error');
121
+ const panelInFullScreenElements = graphWindow.document.querySelectorAll('[class$="panel-in-fullscreen');
122
+ const panelContainerElements = graphWindow.document.querySelectorAll('[class$="panel-container');
121
123
  const error = errorElements.length > 0 || errorCornerElements.length > 0;
122
124
  const loaded = panelInFullScreenElements.length > 0 || panelContainerElements.length > 0;
123
125
  const errorMessageElms = graphWindow.document.getElementsByTagName('pre');
@@ -376,6 +376,7 @@ export default {
376
376
  :value="toRemove"
377
377
  :names="names"
378
378
  :type="type"
379
+ :done-location="doneLocation"
379
380
  @errors="e => error = e"
380
381
  @done="done"
381
382
  />
@@ -758,6 +758,7 @@ export default {
758
758
  :name="'col:' + c"
759
759
  :row="row"
760
760
  :queue-update="queueUpdate"
761
+ :i="i"
761
762
  />
762
763
  </div>
763
764
  <div
@@ -3,10 +3,10 @@ import KeyValue from '@shell/components/form/KeyValue';
3
3
  import { _VIEW } from '@shell/config/query-params';
4
4
  import Select from '@shell/components/form/Select';
5
5
 
6
- const EFFECT_VALUES = {
7
- NO_SCHEDULE: 'NoSchedule',
8
- PREFER_NO_SCHEDULE: 'PreferNoSchedule',
9
- NO_EXECUTE: 'NoExecute',
6
+ const DEFAULT_EFFECT_VALUES = {
7
+ NoSchedule: 'NoSchedule',
8
+ PreferNoSchedule: 'PreferNoSchedule',
9
+ NoExecute: 'NoExecute',
10
10
  };
11
11
 
12
12
  export default {
@@ -24,11 +24,15 @@ export default {
24
24
  disabled: {
25
25
  default: false,
26
26
  type: Boolean
27
+ },
28
+ effectValues: {
29
+ type: Object,
30
+ default: () => DEFAULT_EFFECT_VALUES
27
31
  }
28
32
  },
29
33
 
30
34
  data() {
31
- return { effectOptions: Object.values(EFFECT_VALUES).map((v) => ({ label: v, value: v })) };
35
+ return { effectOptions: Object.keys(this.effectValues).map((k) => ({ label: this.effectValues[k], value: k })) };
32
36
  },
33
37
 
34
38
  computed: {
@@ -43,7 +47,7 @@ export default {
43
47
  },
44
48
 
45
49
  defaultAddData() {
46
- return { effect: EFFECT_VALUES.NO_SCHEDULE };
50
+ return { effect: this.effectOptions[0].value };
47
51
  }
48
52
  }
49
53
  };
@@ -53,6 +57,7 @@ export default {
53
57
  <div class="taints">
54
58
  <KeyValue
55
59
  v-model="localValue"
60
+ data-testid="taints-keyvalue"
56
61
  :title="t('tableHeaders.taints')"
57
62
  :mode="mode"
58
63
  :as-map="false"
@@ -69,9 +74,10 @@ export default {
69
74
  {{ t('tableHeaders.effect') }}
70
75
  </template>
71
76
 
72
- <template #col:effect="{row, queueUpdate}">
77
+ <template #col:effect="{row, queueUpdate, i}">
73
78
  <Select
74
79
  v-model="row.effect"
80
+ :data-testid="`taints-effect-row-${i}`"
75
81
  :options="effectOptions"
76
82
  :disabled="disabled"
77
83
  class="compact-select"
@@ -0,0 +1,70 @@
1
+ import { mount } from '@vue/test-utils';
2
+ import Taints from '@shell/components/form/Taints.vue';
3
+
4
+ describe('component: Taints', () => {
5
+ it('should accept custom effect values', async() => {
6
+ const customEffects = { FOO_EFFECT: 'foo', BAR_EFFECT: 'bar' };
7
+
8
+ const wrapper = mount(Taints, {
9
+ propsData: {
10
+ value: [{ effect: 'FOO_EFFECT', value: 'abc' }],
11
+ effectValues: customEffects
12
+ }
13
+ });
14
+
15
+ const firstEffectInput = wrapper.find('[data-testid="taints-effect-row-0"]');
16
+
17
+ expect(firstEffectInput.exists()).toBe(true);
18
+
19
+ expect(firstEffectInput.props().value).toBe('FOO_EFFECT');
20
+ expect(firstEffectInput.props().options).toStrictEqual([{ value: 'FOO_EFFECT', label: 'foo' }, { value: 'BAR_EFFECT', label: 'bar' }]);
21
+
22
+ const taintKV = wrapper.find('[data-testid="taints-keyvalue"]');
23
+
24
+ taintKV.vm.add();
25
+ await wrapper.vm.$nextTick();
26
+
27
+ const secondEffectInput = wrapper.find('[data-testid="taints-effect-row-1"]');
28
+
29
+ expect(secondEffectInput.exists()).toBe(true);
30
+
31
+ expect(secondEffectInput.props().value).toStrictEqual('FOO_EFFECT');
32
+ expect(wrapper.vm.defaultAddData).toStrictEqual({ effect: 'FOO_EFFECT' });
33
+ });
34
+
35
+ it('should use default effect values of NoSchedule, PreferNoSchedule, and PreferNoExecute', async() => {
36
+ const expectedEffectOptions = [
37
+ { label: 'NoSchedule', value: 'NoSchedule' },
38
+ { label: 'PreferNoSchedule', value: 'PreferNoSchedule' },
39
+
40
+ { label: 'NoExecute', value: 'NoExecute' },
41
+
42
+ ];
43
+
44
+ const wrapper = mount(Taints, { propsData: { value: [{ effect: '', value: 'abc' }] } });
45
+
46
+ const firstEffectInput = wrapper.find('[data-testid="taints-effect-row-0"]');
47
+
48
+ expect(firstEffectInput.exists()).toBe(true);
49
+
50
+ expect(firstEffectInput.props().value).toBe('');
51
+ expect(firstEffectInput.props().options).toStrictEqual(expectedEffectOptions);
52
+ });
53
+
54
+ it('should set the effect value to NoSchedule by default', async() => {
55
+ const wrapper = mount(Taints, { propsData: { value: [] } });
56
+
57
+ const taintKV = wrapper.find('[data-testid="taints-keyvalue"]');
58
+
59
+ taintKV.vm.add();
60
+ await wrapper.vm.$nextTick();
61
+
62
+ const effectInput = wrapper.find('[data-testid="taints-effect-row-0"]');
63
+
64
+ expect(effectInput.exists()).toBe(true);
65
+
66
+ expect(effectInput.props().value).toStrictEqual('NoSchedule');
67
+
68
+ expect(wrapper.vm.defaultAddData).toStrictEqual({ effect: 'NoSchedule' });
69
+ });
70
+ });
@@ -133,7 +133,7 @@ export default {
133
133
  // Don't show if the header is in 'simple' mode
134
134
  const notSimple = !this.simple;
135
135
  // One of these must be enabled, otherwise t here's no component to show
136
- const validFilterSettings = this.currentProduct.showNamespaceFilter || this.currentProduct.showWorkspaceSwitcher;
136
+ const validFilterSettings = this.currentProduct?.showNamespaceFilter || this.currentProduct?.showWorkspaceSwitcher;
137
137
 
138
138
  return validClusterOrProduct && notSimple && validFilterSettings;
139
139
  },
@@ -1090,7 +1090,7 @@ export default {
1090
1090
  }
1091
1091
  }
1092
1092
 
1093
- > i {
1093
+ > i, > img {
1094
1094
  display: block;
1095
1095
  width: 42px;
1096
1096
  font-size: $icon-size;
@@ -1102,9 +1102,6 @@ export default {
1102
1102
  margin-right: 16px;
1103
1103
  fill: var(--link);
1104
1104
  }
1105
- img {
1106
- margin-right: 16px;
1107
- }
1108
1105
 
1109
1106
  &.router-link-active, &.active-menu-link {
1110
1107
  background: var(--primary-hover-bg);
@@ -174,7 +174,7 @@ export function init(store) {
174
174
  componentForType(`${ MANAGEMENT.AUTH_CONFIG }/googleoauth`, 'auth/googleoauth');
175
175
  componentForType(`${ MANAGEMENT.AUTH_CONFIG }/azuread`, 'auth/azuread');
176
176
  componentForType(`${ MANAGEMENT.AUTH_CONFIG }/keycloakoidc`, 'auth/oidc');
177
- componentForType(`${ MANAGEMENT.AUTH_CONFIG }/oidc`, 'auth/oidc');
177
+ componentForType(`${ MANAGEMENT.AUTH_CONFIG }/genericoidc`, 'auth/oidc');
178
178
 
179
179
  basicType([
180
180
  'config',
@@ -0,0 +1,13 @@
1
+ export function install(router, context) {
2
+ router.beforeEach((to, from, next) => loadI18n(to, from, next, context));
3
+ }
4
+
5
+ export async function loadI18n(to, from, next, { store }) {
6
+ try {
7
+ await store.dispatch('i18n/init');
8
+ } catch (e) {
9
+ console.error('Failed to initialize i18n', e); // eslint-disable-line no-console
10
+ }
11
+
12
+ next();
13
+ }
@@ -1,6 +1,7 @@
1
1
  import { install as installLoadInitialSettings } from '@shell/config/router/navigation-guards/load-initial-settings';
2
2
  import { install as installAttemptFirstLogin } from '@shell/config/router/navigation-guards/attempt-first-login';
3
3
  import { install as installAuthentication } from '@shell/config/router/navigation-guards/authentication';
4
+ import { install as installI18N } from '@shell/config/router/navigation-guards/i18n';
4
5
 
5
6
  /**
6
7
  * Install our router navigation guards. i.e. router.beforeEach(), router.afterEach()
@@ -9,7 +10,7 @@ export function installNavigationGuards(router, context) {
9
10
  // NOTE: the order of the installation matters.
10
11
  // Be intentional when adding, removing or modifying the guards that are installed.
11
12
 
12
- const navigationGuardInstallers = [installLoadInitialSettings, installAttemptFirstLogin, installAuthentication];
13
+ const navigationGuardInstallers = [installLoadInitialSettings, installAttemptFirstLogin, installAuthentication, installI18N];
13
14
 
14
15
  navigationGuardInstallers.forEach((installer) => installer(router, context));
15
16
  }
@@ -8,6 +8,7 @@
8
8
  "dependencies": {},
9
9
  "resolutions": {
10
10
  "**/webpack": "4",
11
- "@types/node": "^16"
11
+ "@types/node": "^16",
12
+ "glob": "7.2.3"
12
13
  }
13
14
  }
@@ -73,5 +73,47 @@ describe('view: provisioning.cattle.io.cluster', () => {
73
73
 
74
74
  expect(wrapper.vm.showRegistration).toStrictEqual(false);
75
75
  });
76
+
77
+ it('should SHOW if custom/imported cluster and the cluster is active', async() => {
78
+ const value = {
79
+ isCustom: true,
80
+ isImported: true,
81
+ mgmt: {
82
+ hasLink: () => jest.fn(),
83
+ linkFor: () => '',
84
+ isReady: true
85
+ }
86
+ };
87
+
88
+ const wrapper = shallowMount(ProvisioningCattleIoCluster, {
89
+ mocks,
90
+ propsData: { value },
91
+ });
92
+
93
+ await wrapper.setData({ clusterToken: {} });
94
+
95
+ expect(wrapper.vm.showRegistration).toStrictEqual(true);
96
+ });
97
+
98
+ it('should NOT show if imported cluster and the cluster is active', async() => {
99
+ const value = {
100
+ isCustom: false,
101
+ isImported: true,
102
+ mgmt: {
103
+ hasLink: () => jest.fn(),
104
+ linkFor: () => '',
105
+ isReady: true
106
+ }
107
+ };
108
+
109
+ const wrapper = shallowMount(ProvisioningCattleIoCluster, {
110
+ mocks,
111
+ propsData: { value },
112
+ });
113
+
114
+ await wrapper.setData({ clusterToken: {} });
115
+
116
+ expect(wrapper.vm.showRegistration).toStrictEqual(false);
117
+ });
76
118
  });
77
119
  });
@@ -512,14 +512,14 @@ export default {
512
512
  return false;
513
513
  }
514
514
 
515
- if ( this.value.isImported ) {
516
- return !this.value.mgmt?.isReady && this.extDetailTabs.registration;
517
- }
518
-
519
515
  if ( this.value.isCustom ) {
520
516
  return this.extDetailTabs.registration;
521
517
  }
522
518
 
519
+ if ( this.value.isImported ) {
520
+ return !this.value.mgmt?.isReady && this.extDetailTabs.registration;
521
+ }
522
+
523
523
  // Hosted kubernetes providers with private endpoints need the registration tab
524
524
  // https://github.com/rancher/dashboard/issues/6036
525
525
  // https://github.com/rancher/dashboard/issues/4545
@@ -3,6 +3,8 @@ import AsyncButton from '@shell/components/AsyncButton';
3
3
  import { Card } from '@components/Card';
4
4
  import { Banner } from '@components/Banner';
5
5
  import { exceptionToErrorsArray } from '@shell/utils/error';
6
+ import { resourceNames } from '@shell/utils/string';
7
+ import { mapGetters } from 'vuex';
6
8
 
7
9
  export default {
8
10
  components: {
@@ -12,20 +14,35 @@ export default {
12
14
  },
13
15
 
14
16
  props: {
15
- url: {
16
- type: String,
17
- default: null,
17
+ drivers: {
18
+ type: Array,
19
+ required: true
18
20
  },
19
- name: {
20
- type: String,
21
- default: null,
21
+ driverType: {
22
+ type: String,
23
+ required: true
22
24
  }
23
25
  },
24
26
 
25
27
  data() {
26
28
  return { errors: [] };
27
29
  },
30
+ computed: {
31
+ formattedText() {
32
+ const namesSliced = this.drivers.map((obj) => obj.nameDisplay).slice(0, 5);
33
+ const remaining = this.drivers.length - namesSliced.length;
34
+
35
+ const plusMore = this.t('drivers.deactivate.andOthers', { count: remaining });
36
+ const names = resourceNames(namesSliced, plusMore, this.t);
37
+ const count = remaining || namesSliced.length;
38
+ const warningDrivers = this.t('drivers.deactivate.warningDrivers', { names, count });
39
+
40
+ return this.t('drivers.deactivate.warning', { warningDrivers, count: namesSliced.length });
41
+ },
42
+ ...mapGetters({ t: 'i18n/t' }),
43
+ },
28
44
  methods: {
45
+ resourceNames,
29
46
  close(buttonDone) {
30
47
  if (buttonDone && typeof buttonDone === 'function') {
31
48
  buttonDone(true);
@@ -34,10 +51,12 @@ export default {
34
51
  },
35
52
  async apply(buttonDone) {
36
53
  try {
37
- await this.$store.dispatch('rancher/request', {
38
- url: this.url,
39
- method: 'post'
40
- });
54
+ await Promise.all(this.drivers.map(
55
+ (driver) => this.$store.dispatch('rancher/request', {
56
+ url: `v3/${ this.driverType }/${ escape(driver.id) }?action=deactivate`,
57
+ method: 'POST'
58
+ })
59
+ ));
41
60
 
42
61
  this.close(buttonDone);
43
62
  } catch (err) {
@@ -64,7 +83,7 @@ export default {
64
83
  <template #body>
65
84
  <div class="pl-10 pr-10">
66
85
  <div class="text info mb-10 mt-20">
67
- <span v-clean-html="t('drivers.deactivate.warning', {name})" />
86
+ <span v-clean-html="formattedText" />
68
87
  </div>
69
88
  <Banner
70
89
  v-for="(err, i) in errors"
@@ -19,14 +19,14 @@ const validScope = 'openid profile email';
19
19
 
20
20
  const mockModel = {
21
21
  enabled: false,
22
- id: 'oidc',
22
+ id: 'genericoidc',
23
23
  rancherUrl: validRancherUrl,
24
24
  issuer: validIssuer,
25
25
  authEndpoint: validAuthEndpoint,
26
26
  scope: validScope,
27
27
  clientId: validClientId,
28
28
  clientSecret: validClientSecret,
29
- type: 'oidcConfig',
29
+ type: 'genericOIDCConfig',
30
30
  };
31
31
 
32
32
  const mockedAuthConfigMixin = {
package/edit/token.vue CHANGED
@@ -13,6 +13,7 @@ import Select from '@shell/components/form/Select';
13
13
  import CreateEditView from '@shell/mixins/create-edit-view';
14
14
  import { diffFrom } from '@shell/utils/time';
15
15
  import { filterHiddenLocalCluster, filterOnlyKubernetesClusters } from '@shell/utils/cluster';
16
+ import { SETTING } from '@shell/config/settings';
16
17
 
17
18
  export default {
18
19
  components: {
@@ -29,7 +30,7 @@ export default {
29
30
 
30
31
  data() {
31
32
  // Get the setting that defines the max token TTL allowed (in minutes)
32
- const maxTTLSetting = this.$store.getters['management/byId'](MANAGEMENT.SETTING, 'auth-token-max-ttl-minutes');
33
+ const maxTTLSetting = this.$store.getters['management/byId'](MANAGEMENT.SETTING, SETTING.AUTH_TOKEN_MAX_TTL_MINUTES);
33
34
  let maxTTL = 0;
34
35
 
35
36
  try {
@@ -3,6 +3,7 @@ import { updatePageTitle } from '@shell/utils/title';
3
3
  import { getVendor } from '@shell/config/private-label';
4
4
  import middleware from '@shell/config/middleware.js';
5
5
  import { withQuery } from 'ufo';
6
+ import dynamicPluginLoader from '@shell/pkg/dynamic-plugin-loader';
6
7
 
7
8
  // Global variable used on mount, updated on route change and used in the render function
8
9
  let app;
@@ -196,23 +197,19 @@ async function render(to, from, next) {
196
197
 
197
198
  // If no Components matched, generate 404
198
199
  if (!Components.length) {
199
- // Call the authenticated middleware. This used to attempt to load the error layout but because it was missing it would:
200
- // 1. load the default layout instead
201
- // 2. then call the authenticated middleware
202
- // 3. Authenticated middleware would then load plugins and check to see if there was a valid route and navigate to that if it existed
203
- // 4. This would allow harvester cluster pages to load on page reload
204
- // We should really make authenticated middleware do less...
205
- await callMiddleware.call(this, [{ options: { middleware: ['authenticated'] } }], app.context);
200
+ // Handle the loading of dynamic plugins (Harvester) because we only want to attempt to load those plugins and routes if we first couldn't find a page.
201
+ // We should probably get rid of this concept entirely and just load plugins at the start.
202
+ await app.context.store.dispatch('loadManagement');
203
+ const newLocation = await dynamicPluginLoader.check({ route: { path: window.location.pathname }, store: app.context.store });
206
204
 
207
- // We used to have i18n middleware which was called each time we called middleware. This is also needed to support harvester because of the way harvester loads as outlined in the comment above
208
- await this.$store.dispatch('i18n/init');
205
+ // If we have a new location, double check that it's actually valid
206
+ const resolvedRoute = newLocation?.path ? app.context.store.app.router.resolve({ path: newLocation.path.replace(/^\/{0,1}dashboard/, '') }) : null;
209
207
 
210
- if (nextCalled) {
211
- return;
208
+ if (resolvedRoute?.route.matched.length) {
209
+ // Note - don't use `redirect` or `store.app.route` (breaks feature by failing to run middleware in default layout)
210
+ return next(resolvedRoute.resolved.path);
212
211
  }
213
212
 
214
- // Show error page
215
- // this.error({ statusCode: 404, message: 'This page could not be found' });
216
213
  errorRedirect(this, new Error('404: This page could not be found'));
217
214
 
218
215
  return next();
@@ -163,14 +163,16 @@ export default {
163
163
  const response = await this.$axios.get(url, { timeout: 5000 });
164
164
 
165
165
  if (response?.status === 200) {
166
- this.rows = await this.$store.dispatch('management/findAll', { type: this.resource, opt: { force: true } });
166
+ await this.$store.dispatch('management/findAll', { type: this.resource, opt: { force: true } });
167
167
  btnCB(true);
168
168
  this.close();
169
169
  this.waiting = false;
170
170
  }
171
171
  } catch (e) {}
172
172
 
173
- this.waitForBackend(btnCB, id);
173
+ if (this.waiting) {
174
+ this.waitForBackend(btnCB, id);
175
+ }
174
176
  }, 5000);
175
177
  },
176
178
 
@@ -2,7 +2,6 @@ import { DEFAULT_WORKSPACE } from '@shell/config/types';
2
2
  import { applyProducts } from '@shell/store/type-map';
3
3
  import { ClusterNotFoundError, RedirectToError } from '@shell/utils/error';
4
4
  import { get } from '@shell/utils/object';
5
- import dynamicPluginLoader from '@shell/pkg/dynamic-plugin-loader';
6
5
  import { AFTER_LOGIN_ROUTE, WORKSPACE } from '@shell/store/prefs';
7
6
  import { BACK_TO } from '@shell/config/local-storage';
8
7
  import { NAME as FLEET_NAME } from '@shell/config/product/fleet.js';
@@ -107,24 +106,6 @@ export default async function({
107
106
  });
108
107
  }
109
108
 
110
- if (!route.matched?.length) {
111
- // If there are no matching routes we could be trying to nav to a page belonging to a dynamic plugin which needs loading
112
- await Promise.all([
113
- ...always,
114
- ]);
115
-
116
- // If a plugin claims the route and is loaded correctly we'll get a route back
117
- const newLocation = await dynamicPluginLoader.check({ route, store });
118
-
119
- // If we have a new location, double check that it's actually valid
120
- const resolvedRoute = newLocation ? store.app.router.resolve(newLocation) : null;
121
-
122
- if (resolvedRoute?.route.matched.length) {
123
- // Note - don't use `redirect` or `store.app.route` (breaks feature by failing to run middleware in default layout)
124
- return next(newLocation);
125
- }
126
- }
127
-
128
109
  // Ensure that the activeNamespaceCache is updated given the change of context either from or to a place where it uses workspaces
129
110
  // When fleet moves to it's own package this should be moved to pkg onEnter/onLeave
130
111
  if ((oldProduct === FLEET_NAME || product === FLEET_NAME) && oldProduct !== product) {
@@ -273,7 +273,7 @@ export default {
273
273
 
274
274
  // KeyCloakOIDCConfig --> OIDCConfig
275
275
  set(this.model, 'rancherUrl', `${ serverUrl }/verify-auth`);
276
- set(this.model, 'scope', BASE_SCOPES.oidc[0]);
276
+ set(this.model, 'scope', this.model.id === 'keycloakoidc' ? BASE_SCOPES.keycloakoidc[0] : BASE_SCOPES.genericoidc[0]);
277
277
  break;
278
278
  }
279
279
 
package/models/driver.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { DESCRIPTION } from '@shell/config/labels-annotations';
2
2
  import NormanModel from '@shell/plugins/steve/norman-class';
3
3
  import { KONTAINER_TO_DRIVER } from './management.cattle.io.kontainerdriver';
4
+ import capitalize from 'lodash/capitalize';
4
5
 
5
6
  export default class Driver extends NormanModel {
6
7
  get canViewYaml() {
@@ -20,12 +21,12 @@ export default class Driver extends NormanModel {
20
21
  }
21
22
  }
22
23
 
23
- return KONTAINER_TO_DRIVER[this.id] || this.id;
24
+ return KONTAINER_TO_DRIVER[this.id] || this.name || this.id;
24
25
  }
25
26
 
26
27
  get nameDisplay() {
27
28
  const path = `cluster.provider.${ this.driverName }`;
28
- const label = this.driverName || this.name || this.id;
29
+ const label = capitalize(this.driverName);
29
30
 
30
31
  return this.$rootGetters['i18n/withFallback'](path, label);
31
32
  }