@rancher/shell 0.3.16 → 0.3.17

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 (144) hide show
  1. package/assets/images/wechat-qr-code.jpg +0 -0
  2. package/assets/translations/en-us.yaml +69 -14
  3. package/assets/translations/zh-hans.yaml +87 -7
  4. package/chart/__tests__/S3.test.ts +50 -0
  5. package/chart/rancher-backup/S3.vue +21 -0
  6. package/chart/rancher-backup/index.vue +4 -0
  7. package/components/CommunityLinks.vue +1 -0
  8. package/components/FileDiff.vue +92 -85
  9. package/components/ResourceDetail/index.vue +4 -12
  10. package/components/ResourceList/index.vue +1 -1
  11. package/components/ResourceTable.vue +50 -2
  12. package/components/YamlEditor.vue +1 -0
  13. package/components/auth/RoleDetailEdit.vue +1 -0
  14. package/components/form/NameNsDescription.vue +28 -12
  15. package/components/form/NodeAffinity.vue +2 -2
  16. package/components/form/PodAffinity.vue +2 -2
  17. package/components/form/ResourceTabs/index.vue +8 -2
  18. package/components/form/Select.vue +16 -0
  19. package/components/form/__tests__/NodeAffinity.test.ts +38 -0
  20. package/components/form/__tests__/PodAffinity.test.ts +46 -0
  21. package/components/formatter/ClusterLink.vue +8 -4
  22. package/components/formatter/ImageName.vue +23 -0
  23. package/components/formatter/PodImages.vue +7 -1
  24. package/components/formatter/__tests__/ClusterLink.test.ts +101 -0
  25. package/components/nav/Header.vue +2 -2
  26. package/config/__test__/home-links.test.ts +62 -0
  27. package/config/home-links.js +15 -3
  28. package/config/labels-annotations.js +5 -1
  29. package/config/router.js +0 -4
  30. package/config/settings.ts +4 -0
  31. package/config/table-headers.js +6 -5
  32. package/config/uiplugins.js +50 -5
  33. package/core/plugin-helpers.js +20 -12
  34. package/core/plugin.ts +9 -0
  35. package/core/plugins.js +1 -1
  36. package/core/types-provisioning.ts +253 -0
  37. package/core/types.ts +17 -3
  38. package/detail/autoscaling.horizontalpodautoscaler/index.vue +50 -1
  39. package/detail/node.vue +6 -6
  40. package/detail/pod.vue +2 -6
  41. package/detail/provisioning.cattle.io.cluster.vue +46 -7
  42. package/detail/workload/index.vue +9 -9
  43. package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +62 -0
  44. package/edit/auth/github.vue +1 -0
  45. package/edit/autoscaling.horizontalpodautoscaler/hpa-scaling-rule.vue +130 -0
  46. package/edit/autoscaling.horizontalpodautoscaler/index.vue +79 -0
  47. package/edit/fleet.cattle.io.gitrepo.vue +18 -1
  48. package/edit/namespace.vue +9 -1
  49. package/edit/networking.k8s.io.ingress/RulePath.vue +0 -2
  50. package/edit/provisioning.cattle.io.cluster/AgentConfiguration.vue +1 -30
  51. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +79 -1
  52. package/edit/provisioning.cattle.io.cluster/index.vue +52 -0
  53. package/edit/provisioning.cattle.io.cluster/rke2.vue +330 -150
  54. package/edit/ui.cattle.io.navlink.vue +2 -1
  55. package/initialize/App.js +3 -13
  56. package/initialize/layouts.ts +26 -0
  57. package/list/provisioning.cattle.io.cluster.vue +8 -1
  58. package/middleware/authenticated.js +93 -5
  59. package/mixins/brand.js +39 -3
  60. package/mixins/child-hook.js +2 -2
  61. package/mixins/create-edit-view/impl.js +2 -2
  62. package/models/fleet.cattle.io.gitrepo.js +1 -0
  63. package/models/provisioning.cattle.io.cluster.js +9 -1
  64. package/package.json +2 -2
  65. package/pages/about.vue +8 -2
  66. package/pages/auth/login.vue +1 -1
  67. package/pages/auth/logout.vue +11 -3
  68. package/pages/c/_cluster/apps/charts/index.vue +5 -2
  69. package/pages/c/_cluster/apps/charts/install.vue +5 -0
  70. package/pages/c/_cluster/explorer/index.vue +1 -10
  71. package/pages/c/_cluster/uiplugins/AddExtensionRepos.vue +177 -0
  72. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +19 -3
  73. package/pages/c/_cluster/uiplugins/RemoveUIPlugins.vue +90 -21
  74. package/pages/c/_cluster/uiplugins/SetupUIPlugins.vue +107 -37
  75. package/pages/c/_cluster/uiplugins/index.vue +155 -44
  76. package/pages/docs/_doc.vue +9 -3
  77. package/pages/home.vue +5 -5
  78. package/pages/support/index.vue +10 -4
  79. package/pkg/auto-import.js +1 -1
  80. package/plugins/clean-tooltip-directive.js +1 -1
  81. package/plugins/dashboard-store/resource-class.js +35 -2
  82. package/plugins/plugin.js +9 -1
  83. package/rancher-components/BadgeState/BadgeState.vue +5 -1
  84. package/rancher-components/Banner/Banner.test.ts +51 -1
  85. package/rancher-components/Banner/Banner.vue +134 -53
  86. package/rancher-components/Card/Card.vue +24 -7
  87. package/rancher-components/Form/Checkbox/Checkbox.test.ts +20 -29
  88. package/rancher-components/Form/Checkbox/Checkbox.vue +45 -20
  89. package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +2 -8
  90. package/rancher-components/Form/LabeledInput/LabeledInput.vue +22 -10
  91. package/rancher-components/Form/Radio/RadioButton.vue +30 -13
  92. package/rancher-components/Form/Radio/RadioGroup.vue +26 -7
  93. package/rancher-components/Form/TextArea/TextAreaAutoGrow.vue +7 -6
  94. package/rancher-components/Form/ToggleSwitch/ToggleSwitch.test.ts +25 -38
  95. package/rancher-components/Form/ToggleSwitch/ToggleSwitch.vue +23 -11
  96. package/rancher-components/LabeledTooltip/LabeledTooltip.vue +19 -5
  97. package/rancher-components/StringList/StringList.test.ts +453 -49
  98. package/rancher-components/StringList/StringList.vue +44 -26
  99. package/scripts/extension/publish +2 -2
  100. package/scripts/typegen.sh +1 -0
  101. package/server/server-middleware.js +4 -12
  102. package/store/index.js +13 -0
  103. package/store/prefs.js +0 -3
  104. package/store/type-map.js +17 -29
  105. package/types/shell/index.d.ts +236 -83
  106. package/utils/kube.js +9 -0
  107. package/utils/object.js +27 -0
  108. package/utils/settings.ts +2 -2
  109. package/vue.config.js +3 -2
  110. package/components/.DS_Store +0 -0
  111. package/components/__tests__/.DS_Store +0 -0
  112. package/creators/pkg/package-lock.json +0 -37
  113. package/pages/safeMode.vue +0 -17
  114. package/rancher-components/components/BadgeState/BadgeState.spec.ts +0 -12
  115. package/rancher-components/components/BadgeState/BadgeState.vue +0 -111
  116. package/rancher-components/components/BadgeState/index.ts +0 -1
  117. package/rancher-components/components/Banner/Banner.test.ts +0 -63
  118. package/rancher-components/components/Banner/Banner.vue +0 -244
  119. package/rancher-components/components/Banner/index.ts +0 -1
  120. package/rancher-components/components/Card/Card.vue +0 -167
  121. package/rancher-components/components/Card/index.ts +0 -1
  122. package/rancher-components/components/Form/Checkbox/Checkbox.test.ts +0 -68
  123. package/rancher-components/components/Form/Checkbox/Checkbox.vue +0 -420
  124. package/rancher-components/components/Form/Checkbox/index.ts +0 -1
  125. package/rancher-components/components/Form/LabeledInput/LabeledInput.test.ts +0 -23
  126. package/rancher-components/components/Form/LabeledInput/LabeledInput.vue +0 -355
  127. package/rancher-components/components/Form/LabeledInput/index.ts +0 -1
  128. package/rancher-components/components/Form/Radio/RadioButton.vue +0 -287
  129. package/rancher-components/components/Form/Radio/RadioGroup.vue +0 -254
  130. package/rancher-components/components/Form/Radio/index.ts +0 -2
  131. package/rancher-components/components/Form/TextArea/TextAreaAutoGrow.vue +0 -170
  132. package/rancher-components/components/Form/TextArea/index.ts +0 -1
  133. package/rancher-components/components/Form/ToggleSwitch/ToggleSwitch.test.ts +0 -94
  134. package/rancher-components/components/Form/ToggleSwitch/ToggleSwitch.vue +0 -149
  135. package/rancher-components/components/Form/ToggleSwitch/index.ts +0 -1
  136. package/rancher-components/components/Form/index.ts +0 -5
  137. package/rancher-components/components/LabeledTooltip/LabeledTooltip.vue +0 -151
  138. package/rancher-components/components/LabeledTooltip/index.ts +0 -1
  139. package/rancher-components/components/StringList/StringList.test.ts +0 -484
  140. package/rancher-components/components/StringList/StringList.vue +0 -611
  141. package/rancher-components/components/StringList/index.ts +0 -1
  142. package/yarn-error.log +0 -196
  143. /package/rancher-components/{components/Card → Card}/Card.test.ts +0 -0
  144. /package/rancher-components/{components/Form → Form}/Radio/RadioButton.test.ts +0 -0
package/initialize/App.js CHANGED
@@ -1,23 +1,13 @@
1
1
  import Vue from 'vue';
2
2
 
3
- import {
4
- getMatchedComponentsInstances, getChildrenComponentInstancesUsingFetch, promisify, globalHandleError, sanitizeComponent
5
- } from '../utils/nuxt';
3
+ import { getMatchedComponentsInstances, getChildrenComponentInstancesUsingFetch, promisify, globalHandleError } from '../utils/nuxt';
6
4
  import NuxtError from '../layouts/error.vue';
7
5
  import NuxtLoading from '../components/nav/GlobalLoading.vue';
8
6
 
9
7
  import '../assets/styles/app.scss';
8
+ import { getLayouts } from './layouts';
10
9
 
11
- import blank from '../layouts/blank.vue';
12
- import defaultLayout from '../layouts/default.vue';
13
- import home from '../layouts/home.vue';
14
- import plain from '../layouts/plain.vue';
15
- import unauthenticated from '../layouts/unauthenticated.vue';
16
- import standalone from '../layouts/standalone.vue';
17
-
18
- const layouts = {
19
- _blank: sanitizeComponent(blank), _default: sanitizeComponent(defaultLayout), _home: sanitizeComponent(home), _plain: sanitizeComponent(plain), _unauthenticated: sanitizeComponent(unauthenticated), _standalone: sanitizeComponent(standalone)
20
- };
10
+ const layouts = getLayouts();
21
11
 
22
12
  export default {
23
13
  render(h) {
@@ -0,0 +1,26 @@
1
+ import { sanitizeComponent } from '@shell/utils/nuxt';
2
+ import blank from '@shell/layouts/blank.vue';
3
+ import defaultLayout from '@shell/layouts/default.vue';
4
+ import home from '@shell/layouts/home.vue';
5
+ import plain from '@shell/layouts/plain.vue';
6
+ import unauthenticated from '@shell/layouts/unauthenticated.vue';
7
+ import standalone from '@shell/layouts/standalone.vue';
8
+
9
+ export type Component = { [key: string]: any };
10
+ export type Layouts = { [key: string]: Component };
11
+ const layouts: Layouts = { };
12
+
13
+ export function getLayouts(): Layouts {
14
+ return layouts;
15
+ }
16
+
17
+ export function registerLayout(name: string, component: Component): void {
18
+ layouts[`_${ name }`] = sanitizeComponent(component);
19
+ }
20
+
21
+ registerLayout('blank', blank) ;
22
+ registerLayout('default', defaultLayout) ;
23
+ registerLayout('home', home) ;
24
+ registerLayout('plain', plain) ;
25
+ registerLayout('unauthenticated', unauthenticated) ;
26
+ registerLayout('standalone', standalone);
@@ -135,6 +135,13 @@ export default {
135
135
  },
136
136
 
137
137
  harvesterEnabled: mapFeature(HARVESTER_FEATURE),
138
+
139
+ nonStandardNamespaces() {
140
+ // Show the namespace grouping option if there's clusters with namespaces other than 'fleet-default' or 'fleet-local'
141
+ // This will be used when there's clusters from extension based provisioners
142
+ // We should re-visit this for scaling reasons
143
+ return this.filteredRows.some((c) => c.metadata.namespace !== 'fleet-local' && c.metadata.namespace !== 'fleet-default');
144
+ }
138
145
  },
139
146
 
140
147
  $loadingResources() {
@@ -182,7 +189,7 @@ export default {
182
189
  <ResourceTable
183
190
  :schema="schema"
184
191
  :rows="filteredRows"
185
- :namespaced="false"
192
+ :namespaced="nonStandardNamespaces"
186
193
  :loading="loading"
187
194
  :use-query-params-for-simple-filtering="useQueryParamsForSimpleFiltering"
188
195
  :data-testid="'cluster-list'"
@@ -26,6 +26,16 @@ const getPackageFromRoute = (route) => {
26
26
  return arraySafe.find((m) => !!m.pkg)?.pkg;
27
27
  };
28
28
 
29
+ const getResourceFromRoute = (to) => {
30
+ let resource = to.params?.resource;
31
+
32
+ if (!resource) {
33
+ resource = findMeta(to, 'resource');
34
+ }
35
+
36
+ return resource;
37
+ };
38
+
29
39
  let beforeEachSetup = false;
30
40
 
31
41
  function findMeta(route, key) {
@@ -71,9 +81,18 @@ export function getProductFromRoute(to) {
71
81
  return product;
72
82
  }
73
83
 
74
- function setProduct(store, to) {
84
+ function setProduct(store, to, redirect) {
75
85
  let product = getProductFromRoute(to);
76
86
 
87
+ // since all products are hardcoded as routes (ex: c-local-explorer), if we match the wildcard route it means that the product does not exist
88
+ if ((product && (!to.matched.length || (to.matched.length && to.matched[0].path === '/c/:cluster/:product'))) ||
89
+ // if the product grabbed from the route is not registered, then we don't have it!
90
+ (product && !store.getters['type-map/isProductRegistered'](product))) {
91
+ store.dispatch('loadingError', new Error(store.getters['i18n/t']('nav.failWhale.productNotFound', { productNotFound: product }, true)));
92
+
93
+ return () => redirect(302, '/fail-whale');
94
+ }
95
+
77
96
  if ( !product ) {
78
97
  product = EXPLORER;
79
98
  }
@@ -92,6 +111,42 @@ function setProduct(store, to) {
92
111
  // There might be management catalog items in it vs cluster.
93
112
  store.commit('catalog/reset');
94
113
  }
114
+
115
+ return false;
116
+ }
117
+
118
+ /**
119
+ * Check that the resource is valid, if not redirect to fail whale
120
+ *
121
+ * This requires that
122
+ * - product is set
123
+ * - product's store is set and setup (so we can check schema's within it)
124
+ * - product's store has the schemaFor getter (extension stores might not have it)
125
+ * - there's a resource associated with route (meta or param)
126
+ */
127
+ function invalidResource(store, to, redirect) {
128
+ const product = store.getters['currentProduct'];
129
+ const inStore = product?.inStore;
130
+ const schemaFor = store.getters[`${ inStore }/schemaFor`]; // There's a chance we're in an extension's product who's store could be anything
131
+ const resource = getResourceFromRoute(to);
132
+
133
+ // In order to check a resource is valid we need all of these
134
+ if (!product || !inStore || !schemaFor || !resource) {
135
+ return false;
136
+ }
137
+
138
+ // Resource is valid if a schema exists for it (standard resource, spoofed resource) or it's a virtual resource
139
+ const validResource = schemaFor(resource) || store.getters['type-map/isVirtual'](resource);
140
+
141
+ if (validResource) {
142
+ return false;
143
+ }
144
+
145
+ // Unknown resource, redirect to fail whale
146
+
147
+ store.dispatch('loadingError', new Error(store.getters['i18n/t']('nav.failWhale.resourceNotFound', { resource }, true)));
148
+
149
+ return () => redirect(302, '/fail-whale');
95
150
  }
96
151
 
97
152
  export default async function({
@@ -281,19 +336,39 @@ export default async function({
281
336
  store.dispatch('gcRouteChanged', route);
282
337
 
283
338
  // Load stuff
339
+ let localCheckResource = false;
340
+
284
341
  await applyProducts(store, $plugin);
342
+
285
343
  // Setup a beforeEach hook once to keep track of the current product
286
344
  if ( !beforeEachSetup ) {
287
345
  beforeEachSetup = true;
346
+ // This only needs to happen when beforeEach hook hasn't run (the initial load)
347
+ localCheckResource = true;
288
348
 
289
349
  store.app.router.beforeEach((to, from, next) => {
290
350
  // NOTE - This beforeEach runs AFTER this middleware. So anything in this middleware that requires it must set it manually
291
- setProduct(store, to);
351
+ let redirected = setProduct(store, to, redirect);
352
+
353
+ if (redirected) {
354
+ return redirected();
355
+ }
356
+
357
+ redirected = invalidResource(store, to, redirect);
358
+
359
+ if (redirected) {
360
+ return redirected();
361
+ }
362
+
292
363
  next();
293
364
  });
294
365
 
295
366
  // Call it for the initial pageload
296
- setProduct(store, route);
367
+ const redirected = setProduct(store, route, redirect);
368
+
369
+ if (redirected) {
370
+ return redirected();
371
+ }
297
372
 
298
373
  if (process.client) {
299
374
  store.app.router.afterEach((to, from) => {
@@ -376,7 +451,12 @@ export default async function({
376
451
  // When fleet moves to it's own package this should be moved to pkg onEnter/onLeave
377
452
  if ((oldProduct === FLEET_NAME || product === FLEET_NAME) && oldProduct !== product) {
378
453
  // See note above for store.app.router.beforeEach, need to setProduct manually, for the moment do this in a targeted way
379
- setProduct(store, route);
454
+ const redirected = setProduct(store, route, redirect);
455
+
456
+ if (redirected) {
457
+ return redirected();
458
+ }
459
+
380
460
  store.commit('updateWorkspace', {
381
461
  value: store.getters['prefs/get'](WORKSPACE) || DEFAULT_WORKSPACE,
382
462
  getters: store.getters
@@ -397,6 +477,14 @@ export default async function({
397
477
  })
398
478
  ]);
399
479
 
480
+ if (localCheckResource) {
481
+ const redirected = invalidResource(store, route, redirect);
482
+
483
+ if (redirected) {
484
+ return redirected();
485
+ }
486
+ }
487
+
400
488
  if (!clusterId) {
401
489
  clusterId = store.getters['defaultClusterId']; // This needs the cluster list, so no parallel
402
490
  const isSingleProduct = store.getters['isSingleProduct'];
@@ -422,7 +510,7 @@ export default async function({
422
510
  return redirect(302, '/home');
423
511
  } else {
424
512
  // Sets error 500 if lost connection to API
425
- store.commit('setError', { error: e, locationError: new Error('Auth Middleware') });
513
+ store.commit('setError', { error: e, locationError: new Error(store.getters['i18n/t']('nav.failWhale.authMiddleware')) });
426
514
 
427
515
  return redirect(302, '/fail-whale');
428
516
  }
package/mixins/brand.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { mapGetters } from 'vuex';
1
2
  import { CATALOG, MANAGEMENT } from '@shell/config/types';
2
3
  import { getVendor } from '@shell/config/private-label';
3
4
  import { SETTING } from '@shell/config/settings';
@@ -5,6 +6,12 @@ import { findBy } from '@shell/utils/array';
5
6
  import { createCssVars } from '@shell/utils/color';
6
7
  import { _ALL_IF_AUTHED } from '@shell/plugins/dashboard-store/actions';
7
8
 
9
+ const cspAdaptorApp = ['rancher-csp-adapter', 'rancher-csp-billing-adapter'];
10
+
11
+ export const hasCspAdapter = (apps) => {
12
+ return apps?.find((a) => cspAdaptorApp.includes(a.metadata?.name));
13
+ };
14
+
8
15
  export default {
9
16
  async fetch() {
10
17
  // For the login page, the schemas won't be loaded - we don't need the apps in this case
@@ -23,13 +30,19 @@ export default {
23
30
  }
24
31
  });
25
32
  } catch (e) {}
33
+
34
+ // Setting this up front will remove `computed` churn, and we only care that we've initialised them
35
+ this.haveAppsAndSettings = !!this.apps && !!this.globalSettings;
26
36
  },
27
37
 
28
38
  data() {
29
- return { apps: [], globalSettings: [] };
39
+ return {
40
+ apps: null, globalSettings: null, haveAppsAndSettings: null
41
+ };
30
42
  },
31
43
 
32
44
  computed: {
45
+ ...mapGetters({ loggedIn: 'auth/loggedIn' }),
33
46
 
34
47
  brand() {
35
48
  const setting = findBy(this.globalSettings, 'id', SETTING.BRAND);
@@ -61,7 +74,23 @@ export default {
61
74
  },
62
75
 
63
76
  cspAdapter() {
64
- return findBy(this.apps, 'metadata.name', 'rancher-csp-adapter' );
77
+ if (!this.canCalcCspAdapter) {
78
+ // We only have a watch on cspAdapter to kick off persisting the brand setting.
79
+ // So we need to ensure we don't return an undefined here... which would match the undefined gave if no csp app was found...
80
+ // .. and wouldn't kick off the watcher
81
+ return '';
82
+ }
83
+
84
+ // Note! this used to be `findBy(this.app)` however for that case we lost reactivity on the collection
85
+ // (computed fires before fetch, fetch happens and update apps, computed would not fire again - even with vue.set)
86
+ // So use `.find` in method instead
87
+ return hasCspAdapter(this.apps);
88
+ },
89
+
90
+ canCalcCspAdapter() {
91
+ // We need to take consider the loggedIn state, as the brand mixin is used in the logout page where we can be in a mixed state
92
+ // (things in store but user has no auth to make changes)
93
+ return this.loggedIn && this.haveAppsAndSettings;
65
94
  }
66
95
  },
67
96
 
@@ -90,7 +119,13 @@ export default {
90
119
  },
91
120
 
92
121
  cspAdapter(neu) {
122
+ if (!this.canCalcCspAdapter) {
123
+ return;
124
+ }
125
+
126
+ // The brand setting will only get updated if...
93
127
  if (neu && !this.brand) {
128
+ // 1) There should be a brand... but there's no brand setting
94
129
  const brandSetting = findBy(this.globalSettings, 'id', SETTING.BRAND);
95
130
 
96
131
  if (brandSetting) {
@@ -109,7 +144,8 @@ export default {
109
144
  } else if (!neu) {
110
145
  const brandSetting = findBy(this.globalSettings, 'id', SETTING.BRAND);
111
146
 
112
- if (brandSetting) {
147
+ if (brandSetting && brandSetting.value !== '') {
148
+ // 2) There should not be a brand... but there is a brand setting
113
149
  brandSetting.value = '';
114
150
  brandSetting.save();
115
151
  }
@@ -20,8 +20,8 @@ export default {
20
20
  });
21
21
  },
22
22
 
23
- registerAfterHook(boundFn, name, priority) {
24
- this._registerHook(AFTER_SAVE_HOOKS, boundFn, name, priority);
23
+ registerAfterHook(boundFn, name, priority = 99, boundFnContext) {
24
+ this._registerHook(AFTER_SAVE_HOOKS, boundFn, name, priority, boundFnContext);
25
25
  },
26
26
 
27
27
  async applyHooks(key, ...args) {
@@ -124,7 +124,7 @@ export default {
124
124
  }
125
125
 
126
126
  try {
127
- await this.applyHooks(BEFORE_SAVE_HOOKS);
127
+ await this.applyHooks(BEFORE_SAVE_HOOKS, this.value);
128
128
 
129
129
  // Remove the labels map if it's empty
130
130
  if ( this.value?.metadata?.labels && Object.keys(this.value.metadata.labels || {}).length === 0 ) {
@@ -152,7 +152,7 @@ export default {
152
152
  await this.$store.dispatch('cluster/findAll', { type: this.value.type, opt: { force: true } }, { root: true });
153
153
  }
154
154
 
155
- await this.applyHooks(AFTER_SAVE_HOOKS);
155
+ await this.applyHooks(AFTER_SAVE_HOOKS, this.value);
156
156
  buttonDone && buttonDone(true);
157
157
 
158
158
  this.done();
@@ -30,6 +30,7 @@ export default class GitRepo extends SteveModel {
30
30
 
31
31
  spec.paths = spec.paths || [];
32
32
  spec.clientSecretName = spec.clientSecretName || null;
33
+ spec.correctDrift = { enabled: false };
33
34
 
34
35
  set(this, 'spec', spec);
35
36
  set(this, 'metadata', meta);
@@ -9,10 +9,11 @@ import { ucFirst } from '@shell/utils/string';
9
9
  import { compare } from '@shell/utils/version';
10
10
  import { AS, MODE, _VIEW, _YAML } from '@shell/config/query-params';
11
11
  import { HARVESTER_NAME as HARVESTER } from '@shell/config/features';
12
+ import { CAPI as CAPI_ANNOTATIONS } from '@shell/config/labels-annotations';
12
13
 
13
14
  /**
14
15
  * Class representing Cluster resource.
15
- * @extends SteveModal
16
+ * @extends SteveModel
16
17
  */
17
18
  export default class ProvCluster extends SteveModel {
18
19
  get details() {
@@ -381,6 +382,13 @@ export default class ProvCluster extends SteveModel {
381
382
  }
382
383
 
383
384
  get machineProvider() {
385
+ // First check annotation - useful for clusters created by extension providers
386
+ const fromAnnotation = this.annotations?.[CAPI_ANNOTATIONS.UI_CUSTOM_PROVIDER];
387
+
388
+ if (fromAnnotation) {
389
+ return fromAnnotation;
390
+ }
391
+
384
392
  if (this.isHarvester) {
385
393
  return HARVESTER;
386
394
  } else if ( this.isImported ) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rancher/shell",
3
- "version": "0.3.16",
3
+ "version": "0.3.17",
4
4
  "description": "Rancher Dashboard Shell",
5
5
  "repository": "https://github.com/rancherlabs/dashboard",
6
6
  "license": "Apache-2.0",
@@ -69,7 +69,7 @@
69
69
  "d3-selection": "1.4.1",
70
70
  "dagre-d3": "0.6.4",
71
71
  "dayjs": "1.8.29",
72
- "diff2html": "2.11.2",
72
+ "diff2html": "3.4.24",
73
73
  "dompurify": "2.4.5",
74
74
  "eslint": "7.32.0",
75
75
  "eslint-config-standard": "16.0.3",
package/pages/about.vue CHANGED
@@ -6,6 +6,7 @@ import { MANAGEMENT } from '@shell/config/types';
6
6
  import { SETTING } from '@shell/config/settings';
7
7
  import { getVendor } from '@shell/config/private-label';
8
8
  import { downloadFile } from '@shell/utils/download';
9
+ import { mapGetters } from 'vuex';
9
10
 
10
11
  export default {
11
12
  layout: 'plain',
@@ -22,6 +23,7 @@ export default {
22
23
  };
23
24
  },
24
25
  computed: {
26
+ ...mapGetters(['releaseNotesUrl']),
25
27
  rancherVersion() {
26
28
  return this.settings.find((s) => s.id === SETTING.VERSION_RANCHER);
27
29
  },
@@ -168,9 +170,13 @@ export default {
168
170
  </tr>
169
171
  </table>
170
172
  <p class="pt-20">
171
- <nuxt-link :to="{ path: 'docs/whats-new'}">
173
+ <a
174
+ :href="releaseNotesUrl"
175
+ target="_blank"
176
+ rel="nofollow noopener noreferrer"
177
+ >
172
178
  {{ t('about.versions.releaseNotes') }}
173
- </nuxt-link>
179
+ </a>
174
180
  </p>
175
181
  <template v-if="downloadImageList.length">
176
182
  <h3 class="pt-40">
@@ -544,7 +544,7 @@ export default {
544
544
 
545
545
  .gutless {
546
546
  height: 100vh;
547
- .span-6 {
547
+ & > .span-6 {
548
548
  overflow-y: auto;
549
549
  display: flex;
550
550
  flex-direction: column;
@@ -11,8 +11,16 @@ export default {
11
11
 
12
12
  <template>
13
13
  <main class="main-layout">
14
- <h1 class="text-center mt-50">
15
- Logging Out&hellip;
16
- </h1>
14
+ <div>
15
+ <h1 v-t="'logout.message'" />
16
+ </div>
17
17
  </main>
18
18
  </template>
19
+ <style lang="scss" scoped>
20
+ main > div {
21
+ display: flex;
22
+ align-items: center;
23
+ justify-content: center;
24
+ height: 100vh;
25
+ }
26
+ </style>
@@ -242,7 +242,7 @@ export default {
242
242
  },
243
243
 
244
244
  created() {
245
- const release = this.currentCluster?.status?.version.gitVersion || '';
245
+ const release = this.currentCluster?.status?.version?.gitVersion || '';
246
246
  const isRKE2 = release.includes('rke2');
247
247
  const version = release.match(/\d+/g);
248
248
 
@@ -352,7 +352,10 @@ export default {
352
352
  <div v-else>
353
353
  <header>
354
354
  <div class="title">
355
- <h1 class="m-0">
355
+ <h1
356
+ data-testid="charts-header-title"
357
+ class="m-0"
358
+ >
356
359
  {{ t('catalog.charts.header') }}
357
360
  </h1>
358
361
  </div>
@@ -1251,6 +1251,10 @@ export default {
1251
1251
  this.steps[0].ready = okRequires && okChart;
1252
1252
  },
1253
1253
 
1254
+ updateStepTwoReady(update) {
1255
+ this.updateStep('helmValues', { ready: update });
1256
+ },
1257
+
1254
1258
  getOptionLabel(opt) {
1255
1259
  return opt?.chartNameDisplay;
1256
1260
  },
@@ -1567,6 +1571,7 @@ export default {
1567
1571
  @warn="e=>errors.push(e)"
1568
1572
  @register-before-hook="registerBeforeHook"
1569
1573
  @register-after-hook="registerAfterHook"
1574
+ @valid="updateStepTwoReady($event)"
1570
1575
  />
1571
1576
  </Tabbed>
1572
1577
  <template v-else>
@@ -27,7 +27,7 @@ import {
27
27
  STATE,
28
28
  } from '@shell/config/table-headers';
29
29
 
30
- import { mapPref, CLUSTER_TOOLS_TIP, PSP_DEPRECATION_BANNER } from '@shell/store/prefs';
30
+ import { mapPref, PSP_DEPRECATION_BANNER } from '@shell/store/prefs';
31
31
  import { haveV1Monitoring, monitoringStatus } from '@shell/utils/monitoring';
32
32
  import Tabbed from '@shell/components/Tabbed';
33
33
  import Tab from '@shell/components/Tabbed/Tab';
@@ -173,7 +173,6 @@ export default {
173
173
  return this.$store.getters['management/all'](MANAGEMENT.CLUSTER);
174
174
  },
175
175
 
176
- hideClusterToolsTip: mapPref(CLUSTER_TOOLS_TIP),
177
176
  hidePspDeprecationBanner: mapPref(PSP_DEPRECATION_BANNER),
178
177
 
179
178
  hasV1Monitoring() {
@@ -441,14 +440,6 @@ export default {
441
440
  :raw="true"
442
441
  />
443
442
  </Banner>
444
- <Banner
445
- v-if="!hideClusterToolsTip"
446
- :closable="true"
447
- class="cluster-tools-tip"
448
- color="info"
449
- label-key="cluster.toolsTip"
450
- @close="hideClusterToolsTip = true"
451
- />
452
443
  <div
453
444
  class="cluster-dashboard-glance"
454
445
  >