@rancher/shell 0.1.4 → 0.2.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 (170) hide show
  1. package/assets/brand/suse/favicon.png +0 -0
  2. package/assets/images/generic-plugin.svg +1 -7
  3. package/assets/styles/global/_button.scss +1 -0
  4. package/assets/translations/en-us.yaml +129 -53
  5. package/assets/translations/zh-hans.yaml +18 -0
  6. package/components/CommunityLinks.vue +40 -49
  7. package/components/ExplorerProjectsNamespaces.vue +20 -3
  8. package/components/HarvesterServiceAddOnConfig.vue +10 -10
  9. package/components/LazyImage.vue +21 -8
  10. package/components/PromptRemove.vue +2 -2
  11. package/components/ResourceList/Masthead.vue +21 -1
  12. package/components/ResourceList/ResourceLoadingIndicator.vue +0 -8
  13. package/components/ResourceList/index.vue +42 -36
  14. package/components/ResourceTable.vue +19 -0
  15. package/components/SortableTable/THead.vue +311 -70
  16. package/components/SortableTable/advanced-filtering.js +272 -0
  17. package/components/SortableTable/filtering.js +90 -29
  18. package/components/SortableTable/index.vue +486 -280
  19. package/components/Tabbed/index.vue +25 -7
  20. package/components/TypeDescription.vue +10 -1
  21. package/components/fleet/FleetClusters.vue +6 -0
  22. package/components/fleet/FleetRepos.vue +7 -1
  23. package/components/form/Command.vue +5 -0
  24. package/components/form/EnvVars.vue +5 -0
  25. package/components/form/NameNsDescription.vue +3 -1
  26. package/components/form/NodeScheduling.vue +6 -1
  27. package/components/form/PodAffinity.vue +5 -0
  28. package/components/form/ServiceNameSelect.vue +5 -0
  29. package/components/form/ValueFromResource.vue +7 -1
  30. package/components/form/WorkloadPorts.vue +2 -2
  31. package/components/nav/TopLevelMenu.vue +2 -1
  32. package/config/home-links.js +155 -0
  33. package/config/private-label.js +1 -1
  34. package/config/product/manager.js +0 -2
  35. package/config/product/settings.js +1 -0
  36. package/config/product/uiplugins.js +2 -1
  37. package/config/settings.js +3 -1
  38. package/config/uiplugins.js +76 -6
  39. package/config/version.js +17 -0
  40. package/core/plugin.ts +12 -0
  41. package/core/plugins.js +29 -5
  42. package/core/types.ts +6 -0
  43. package/creators/app/{.eslintignore → files/.eslintignore} +0 -0
  44. package/creators/app/{.eslintrc.js → files/.eslintrc.js} +0 -0
  45. package/creators/app/{.vscode → files/.vscode}/settings.json +0 -0
  46. package/creators/app/{babel.config.js → files/babel.config.js} +0 -0
  47. package/creators/app/{nuxt.config.js → files/nuxt.config.js} +0 -0
  48. package/creators/app/{tsconfig.json → files/tsconfig.json} +2 -1
  49. package/creators/app/init +16 -17
  50. package/creators/app/package.json +7 -1
  51. package/creators/pkg/{babel.config.js → files/babel.config.js} +0 -0
  52. package/creators/pkg/{index.ts → files/index.ts} +0 -0
  53. package/creators/pkg/{tsconfig.json → files/tsconfig.json} +13 -12
  54. package/creators/pkg/{vue.config.js → files/vue.config.js} +0 -0
  55. package/creators/pkg/init +1 -1
  56. package/creators/pkg/package.json +1 -1
  57. package/creators/update/init +54 -0
  58. package/creators/update/package.json +20 -0
  59. package/creators/update/upgrade +56 -0
  60. package/creators/update/yarn-error.log +54 -0
  61. package/detail/provisioning.cattle.io.cluster.vue +1 -1
  62. package/detail/workload/index.vue +1 -0
  63. package/edit/persistentvolume/index.vue +48 -13
  64. package/edit/persistentvolumeclaim.vue +31 -13
  65. package/edit/provisioning.cattle.io.cluster/ACE.vue +2 -1
  66. package/edit/provisioning.cattle.io.cluster/DrainOptions.vue +0 -1
  67. package/edit/provisioning.cattle.io.cluster/rke2.vue +52 -43
  68. package/edit/service.vue +1 -1
  69. package/edit/workload/index.vue +19 -9
  70. package/edit/workload/mixins/workload.js +109 -114
  71. package/edit/workload/storage/index.vue +11 -17
  72. package/edit/workload/storage/persistentVolumeClaim/index.vue +5 -0
  73. package/edit/workload/storage/secret.vue +6 -1
  74. package/list/catalog.cattle.io.app.vue +10 -9
  75. package/list/catalog.cattle.io.clusterrepo.vue +6 -61
  76. package/list/cis.cattle.io.clusterscan.vue +12 -12
  77. package/list/fleet.cattle.io.bundle.vue +33 -28
  78. package/list/fleet.cattle.io.cluster.vue +26 -22
  79. package/list/fleet.cattle.io.clustergroup.vue +6 -0
  80. package/list/fleet.cattle.io.clusterregistrationtoken.vue +28 -24
  81. package/list/fleet.cattle.io.gitrepo.vue +25 -14
  82. package/list/helm.cattle.io.projecthelmchart.vue +52 -33
  83. package/list/logging.banzaicloud.io.clusterflow.vue +7 -12
  84. package/list/logging.banzaicloud.io.flow.vue +7 -14
  85. package/list/management.cattle.io.cluster.vue +26 -15
  86. package/list/management.cattle.io.feature.vue +13 -8
  87. package/list/management.cattle.io.user.vue +38 -19
  88. package/list/monitoring.coreos.com.alertmanagerconfig.vue +8 -15
  89. package/list/namespace.vue +14 -1
  90. package/list/node.vue +13 -16
  91. package/list/persistentvolume.vue +16 -9
  92. package/list/persistentvolumeclaim.vue +5 -8
  93. package/list/provisioning.cattle.io.cluster.vue +34 -8
  94. package/list/service.vue +24 -12
  95. package/list/ui.cattle.io.navlink.vue +6 -0
  96. package/list/workload.vue +2 -2
  97. package/middleware/authenticated.js +6 -0
  98. package/mixins/resource-fetch.js +12 -18
  99. package/mixins/resource-manager.js +126 -0
  100. package/models/catalog.cattle.io.uiplugin.js +4 -0
  101. package/models/management.cattle.io.cluster.js +9 -1
  102. package/models/pod.js +15 -5
  103. package/models/provisioning.cattle.io.cluster.js +4 -0
  104. package/models/workload.service.js +10 -0
  105. package/nuxt.config.js +2 -1
  106. package/package.json +1 -1
  107. package/pages/auth/login.vue +10 -0
  108. package/pages/auth/verify.vue +9 -0
  109. package/pages/c/_cluster/apps/charts/install.vue +119 -31
  110. package/pages/c/_cluster/settings/DefaultLinksEditor.vue +108 -0
  111. package/pages/c/_cluster/settings/links.vue +53 -101
  112. package/pages/c/_cluster/settings/performance.vue +90 -7
  113. package/pages/c/_cluster/uiplugins/DeveloperInstallDialog.vue +3 -3
  114. package/pages/c/_cluster/uiplugins/InstallDialog.vue +72 -21
  115. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +26 -7
  116. package/pages/c/_cluster/uiplugins/RemoveUIPlugins.vue +2 -7
  117. package/pages/c/_cluster/uiplugins/SetupUIPlugins.vue +23 -15
  118. package/pages/c/_cluster/uiplugins/UninstallDialog.vue +12 -5
  119. package/pages/c/_cluster/uiplugins/index.vue +223 -72
  120. package/pages/support/index.vue +31 -142
  121. package/plugins/dashboard-store/actions.js +19 -0
  122. package/plugins/dashboard-store/getters.js +20 -3
  123. package/plugins/dashboard-store/mutations.js +13 -7
  124. package/plugins/plugin.js +18 -15
  125. package/plugins/steve/getters.js +12 -0
  126. package/plugins/version.js +21 -0
  127. package/promptRemove/mixin/roleDeletionCheck.js +15 -1
  128. package/rancher-components/Form/TextArea/TextAreaAutoGrow.vue +6 -7
  129. package/rancher-components/components/BadgeState/BadgeState.spec.ts +12 -0
  130. package/rancher-components/components/BadgeState/BadgeState.vue +107 -0
  131. package/rancher-components/components/BadgeState/index.ts +1 -0
  132. package/rancher-components/components/Banner/Banner.test.ts +13 -0
  133. package/rancher-components/components/Banner/Banner.vue +163 -0
  134. package/rancher-components/components/Banner/index.ts +1 -0
  135. package/rancher-components/components/Card/Card.vue +150 -0
  136. package/rancher-components/components/Card/index.ts +1 -0
  137. package/rancher-components/components/Form/Checkbox/Checkbox.test.ts +77 -0
  138. package/rancher-components/components/Form/Checkbox/Checkbox.vue +395 -0
  139. package/rancher-components/components/Form/Checkbox/index.ts +1 -0
  140. package/rancher-components/components/Form/LabeledInput/LabeledInput.test.ts +29 -0
  141. package/rancher-components/components/Form/LabeledInput/LabeledInput.vue +343 -0
  142. package/rancher-components/components/Form/LabeledInput/index.ts +1 -0
  143. package/rancher-components/components/Form/Radio/RadioButton.vue +270 -0
  144. package/rancher-components/components/Form/Radio/RadioGroup.vue +235 -0
  145. package/rancher-components/components/Form/Radio/index.ts +2 -0
  146. package/rancher-components/components/Form/TextArea/TextAreaAutoGrow.vue +168 -0
  147. package/rancher-components/components/Form/TextArea/index.ts +1 -0
  148. package/rancher-components/components/Form/ToggleSwitch/ToggleSwitch.test.ts +107 -0
  149. package/rancher-components/components/Form/ToggleSwitch/ToggleSwitch.vue +137 -0
  150. package/rancher-components/components/Form/ToggleSwitch/index.ts +1 -0
  151. package/rancher-components/components/Form/index.ts +5 -0
  152. package/rancher-components/components/LabeledTooltip/LabeledTooltip.vue +137 -0
  153. package/rancher-components/components/LabeledTooltip/index.ts +1 -0
  154. package/scripts/publish-shell.sh +39 -6
  155. package/scripts/record-deps.js +37 -0
  156. package/scripts/test-plugins-build.sh +8 -5
  157. package/scripts/typegen.sh +84 -0
  158. package/store/auth.js +3 -0
  159. package/store/index.js +12 -3
  160. package/store/type-map.js +2 -0
  161. package/types/shell/index.d.ts +3046 -0
  162. package/utils/favicon.js +8 -2
  163. package/utils/gc/gc-interval.ts +40 -0
  164. package/utils/gc/gc-root-store.js +76 -0
  165. package/utils/gc/gc-route-changed.ts +44 -0
  166. package/utils/gc/gc-types.ts +21 -0
  167. package/utils/gc/gc.ts +282 -0
  168. package/config/footer.js +0 -18
  169. package/creators/pkg/nuxt.config.js +0 -6
  170. package/yarn-error.log +0 -195
@@ -1,26 +1,10 @@
1
1
  import { mapGetters } from 'vuex';
2
- import {
3
- COUNT, MANAGEMENT, POD, WORKLOAD_TYPES, WORKLOAD, SECRET
4
- } from '@shell/config/types';
2
+ import { COUNT, MANAGEMENT } from '@shell/config/types';
5
3
  import { SETTING, DEFAULT_PERF_SETTING } from '@shell/config/settings';
6
4
 
7
5
  // Number of pages to fetch when loading incrementally
8
6
  const PAGES = 4;
9
7
 
10
- // restrict advanced features of manual refresh and incremental loading to these resource types
11
- export const TYPES_RESTRICTED = [
12
- SECRET,
13
- POD,
14
- WORKLOAD_TYPES.DEPLOYMENT,
15
- WORKLOAD_TYPES.CRON_JOB,
16
- WORKLOAD_TYPES.DAEMON_SET,
17
- WORKLOAD_TYPES.JOB,
18
- WORKLOAD_TYPES.STATEFUL_SET,
19
- WORKLOAD_TYPES.REPLICA_SET,
20
- WORKLOAD_TYPES.REPLICATION_CONTROLLER,
21
- WORKLOAD
22
- ];
23
-
24
8
  export default {
25
9
  data() {
26
10
  // fetching the settings related to manual refresh from global settings
@@ -66,7 +50,17 @@ export default {
66
50
  }
67
51
  },
68
52
 
69
- computed: { ...mapGetters({ refreshFlag: 'resource-fetch/refreshFlag' }) },
53
+ computed: {
54
+ ...mapGetters({ refreshFlag: 'resource-fetch/refreshFlag' }),
55
+ rows() {
56
+ const inStore = this.$store.getters['currentStore'](this.resource);
57
+
58
+ return this.$store.getters[`${ inStore }/all`](this.resource);
59
+ },
60
+ loading() {
61
+ return this.rows.length ? false : this.$fetchState.pending;
62
+ },
63
+ },
70
64
  watch: {
71
65
  refreshFlag(neu) {
72
66
  // this is where the data assignment will trigger the update of the list view...
@@ -0,0 +1,126 @@
1
+ import { mapGetters } from 'vuex';
2
+ import { allHashSettled } from '@shell/utils/promise';
3
+
4
+ export default {
5
+ computed: { ...mapGetters(['currentCluster']) },
6
+ data() {
7
+ return { isLoadingSecondaryResources: false };
8
+ },
9
+ methods: {
10
+ /**
11
+ *
12
+ * Function resourceManagerFetchSecondaryResources
13
+ * This method is used to fetch what is called "secondary resources", which can be defined as resources that are needed to populate
14
+ * the page/component itself (ex: used as options on a Select) but don't need to be put into Vuex store or watched to get constant updates.
15
+ * This method allows to fetch resources for a given namespace to reduce the amount of results instead of needing to fetch all and filtering afterwards.
16
+ *
17
+ *
18
+ * @param {String} resourceData.namespace - Namespace identifier
19
+ * @param {Object} resourceData.data - Object containing info about the data needed to be fetched and how it should be parsed. Note: The KEY NEEDS to be the resource TYPE!
20
+ * @param {Array} resourceData.data[TYPE].applyTo - The array of operations needed to be performed for the specific data TYPE
21
+ * @param {String} resourceData.data[TYPE].applyTo[x].var - The 'this' property name that should be populated with the data fetched
22
+ * @param {Boolean} resourceData.data[TYPE].applyTo[x].classify - Whether the data fetched should have a model applied to it
23
+ * @param {Function} resourceData.data[TYPE].applyTo[x].parsingFunc - Optional parsing function if the fetched data needs to be parsed
24
+ * @param {Boolean} onlyNamespaced - Only fetch namespaced resources
25
+ */
26
+ async resourceManagerFetchSecondaryResources(resourceData, onlyNamespaced = false) {
27
+ const requests = {};
28
+ const namespace = resourceData.namespace;
29
+
30
+ // Only fetch types if the user is allowed to...
31
+ Object.keys(resourceData.data).forEach((type) => {
32
+ const schema = this.$store.getters['cluster/schemaFor'](type);
33
+
34
+ if (schema) {
35
+ let url = schema.links.collection;
36
+
37
+ if (schema?.attributes?.namespaced && namespace) {
38
+ const parts = url.split('/');
39
+
40
+ parts.splice(parts.length - 2, 0, `api`);
41
+ parts.splice(parts.length - 1, 0, `namespaces/${ namespace }`);
42
+ url = parts.join('/');
43
+ } else if (onlyNamespaced) {
44
+ // Type isn't namespaced and we've been requested to only fetch namespaced types
45
+ return;
46
+ }
47
+
48
+ requests[type] = this.$store.dispatch('cluster/request', { url });
49
+ }
50
+ });
51
+
52
+ if (Object.keys(requests).length) {
53
+ // this is the flag/variable that we need to apply to all places that rely on this data. Ex: LabeledSelect
54
+ this.isLoadingSecondaryResources = true;
55
+ const hash = await allHashSettled(requests);
56
+ const types = Object.keys(hash);
57
+
58
+ for (let i = 0; i < types.length; i++) {
59
+ const type = types[i];
60
+ const status = hash[type].status;
61
+ // if it's namespaced, we get the data on 'items' prop, for non-namespaced it's 'data' prop...
62
+ const requestData = hash[type].value.items || hash[type].value.data || hash[type].value;
63
+ const schema = this.$store.getters['cluster/schemaFor'](type);
64
+
65
+ if (status === 'fulfilled' && resourceData.data[type] && resourceData.data[type].applyTo?.length) {
66
+ for (let y = 0; y < resourceData.data[type].applyTo.length; y++) {
67
+ const apply = resourceData.data[type].applyTo[y];
68
+ let resources = requestData;
69
+
70
+ if (schema?.attributes?.namespaced) {
71
+ // The resources returned when requesting namespaced types do not contain id, type and links properties.
72
+ // This isn't perfect, or universally applicable, but will work for the current set of use cases
73
+ // To make this more generic
74
+ // - id param = this.$store.getters['cluster/keyFieldForType'](type)
75
+ // - id value = new dashboard-store getter, overwritten by steve store getter
76
+ requestData.forEach((item) => {
77
+ item.type = type;
78
+ item.id = `${ item.metadata.namespace }/${ item.metadata.name }`;
79
+ });
80
+ }
81
+
82
+ if (apply.classify) {
83
+ resources = await this.$store.dispatch('cluster/createMany', requestData);
84
+ }
85
+
86
+ if (apply.parsingFunc) {
87
+ this[apply.var] = apply.parsingFunc(resources);
88
+ } else {
89
+ this[apply.var] = resources;
90
+ }
91
+ }
92
+ } else if (status === 'rejected') {
93
+ console.error(`Resource Manager - secondary data request for type ${ type } has failed`, status.error); // eslint-disable-line no-console
94
+ }
95
+ }
96
+
97
+ this.isLoadingSecondaryResources = false;
98
+ }
99
+ },
100
+
101
+ /**
102
+ * Clear the cached secondary resources
103
+ *
104
+ * @param {*} resourceData See resourceManagerFetchSecondaryResources
105
+ * @param {*} onlyNamespaced Clear only namespaced resources
106
+ */
107
+ resourceManagerClearSecondaryResources(resourceData, onlyNamespaced = false) {
108
+ Object.keys(resourceData.data).forEach((type) => {
109
+ const schema = this.$store.getters['cluster/schemaFor'](type);
110
+
111
+ if (schema) {
112
+ if (!schema?.attributes?.namespaced && onlyNamespaced) {
113
+ // resource isn't namespaced and we're only interested in namespaced resources
114
+ return;
115
+ }
116
+
117
+ for (let y = 0; y < resourceData.data[type].applyTo.length; y++) {
118
+ const apply = resourceData.data[type].applyTo[y];
119
+
120
+ this[apply.var] = [];
121
+ }
122
+ }
123
+ });
124
+ }
125
+ },
126
+ };
@@ -31,4 +31,8 @@ export default class UIPlugin extends SteveModel {
31
31
  get isDeveloper() {
32
32
  return this.pluginMetadata?.developer === 'true';
33
33
  }
34
+
35
+ get plugin() {
36
+ return this.spec?.plugin || {};
37
+ }
34
38
  }
@@ -1,6 +1,6 @@
1
1
  import Vue from 'vue';
2
2
  import { CATALOG, CLUSTER_BADGE } from '@shell/config/labels-annotations';
3
- import { NODE, FLEET, MANAGEMENT } from '@shell/config/types';
3
+ import { NODE, FLEET, MANAGEMENT, CAPI } from '@shell/config/types';
4
4
  import { insertAt } from '@shell/utils/array';
5
5
  import { downloadFile } from '@shell/utils/download';
6
6
  import { parseSi } from '@shell/utils/units';
@@ -430,4 +430,12 @@ export default class MgmtCluster extends HybridModel {
430
430
  get nodes() {
431
431
  return this.$getters['all'](MANAGEMENT.NODE).filter(node => node.id.startsWith(this.id));
432
432
  }
433
+
434
+ get provClusterId() {
435
+ const verb = this.isLocal ? 'to' : 'from';
436
+ const from = `${ verb }Type`;
437
+ const id = `${ verb }Id`;
438
+
439
+ return this.metadata.relationships.find(r => r[from] === CAPI.RANCHER_CLUSTER)?.[id];
440
+ }
433
441
  }
package/models/pod.js CHANGED
@@ -182,19 +182,29 @@ export default class Pod extends WorkloadService {
182
182
  }
183
183
 
184
184
  save() {
185
+ const prev = { ...this };
186
+
185
187
  const { metadata, spec } = this.spec.template;
186
188
 
187
189
  this.spec = {
188
190
  ...this.spec,
189
- metadata: {
190
- ...this.metadata,
191
- ...metadata
192
- },
193
191
  ...spec
194
192
  };
195
193
 
194
+ this.metadata = {
195
+ ...this.metadata,
196
+ ...metadata
197
+ };
198
+
196
199
  delete this.spec.template;
197
200
 
198
- return this._save(...arguments);
201
+ // IF there is an error POD world model get overwritten
202
+ // For the workloads this need be reset back
203
+ return this._save(...arguments).catch((e) => {
204
+ this.spec = prev.spec;
205
+ this.metadata = prev.metadata;
206
+
207
+ return Promise.reject(e);
208
+ });
199
209
  }
200
210
  }
@@ -679,6 +679,10 @@ export default class ProvCluster extends SteveModel {
679
679
  }
680
680
 
681
681
  get supportsWindows() {
682
+ if (this.isK3s || this.isImportedK3s) {
683
+ return false;
684
+ }
685
+
682
686
  if ( this.isRke1 ) {
683
687
  return this.mgmt?.spec?.windowsPreferedCluster || false;
684
688
  }
@@ -123,6 +123,11 @@ export default class WorkloadService extends SteveModel {
123
123
 
124
124
  return containers;
125
125
  }
126
+
127
+ if ( this.spec.containers ) {
128
+ return this.spec.containers;
129
+ }
130
+
126
131
  const { spec:{ template:{ spec:{ containers } } } } = this;
127
132
 
128
133
  return containers;
@@ -135,6 +140,11 @@ export default class WorkloadService extends SteveModel {
135
140
 
136
141
  return initContainers;
137
142
  }
143
+
144
+ if (this.spec.initContainers) {
145
+ return this.spec.initContainers;
146
+ }
147
+
138
148
  const { spec:{ template:{ spec:{ initContainers } } } } = this;
139
149
 
140
150
  return initContainers;
package/nuxt.config.js CHANGED
@@ -582,6 +582,7 @@ export default function(dir, _appConfig) {
582
582
  { src: path.join(NUXT_SHELL, 'plugins/plugin'), ssr: false }, // Load dyanmic plugins
583
583
  { src: path.join(NUXT_SHELL, 'plugins/codemirror-loader'), ssr: false },
584
584
  { src: path.join(NUXT_SHELL, 'plugins/formatters'), ssr: false }, // Populate formatters cache for sorted table
585
+ { src: path.join(NUXT_SHELL, 'plugins/version'), ssr: false }, // Makes a fetch to the backend to get version metadata
585
586
  ],
586
587
 
587
588
  // Proxy: https://github.com/nuxt-community/proxy-module#options
@@ -595,8 +596,8 @@ export default function(dir, _appConfig) {
595
596
  '/v3-public': proxyOpts(api), // Rancher Unauthed API
596
597
  '/api-ui': proxyOpts(api), // Browser API UI
597
598
  '/meta': proxyMetaOpts(api), // Browser API UI
598
- '/rancherversion': proxyPrimeOpts(api), // Rancher version endpoint
599
599
  '/v1-*': proxyOpts(api), // SAML, KDM, etc
600
+ '/rancherversion': proxyPrimeOpts(api), // Rancher version endpoint
600
601
  // These are for Ember embedding
601
602
  '/c/*/edit': proxyOpts('https://127.0.0.1:8000'), // Can't proxy all of /c because that's used by Vue too
602
603
  '/k/': proxyOpts('https://127.0.0.1:8000'),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rancher/shell",
3
- "version": "0.1.4",
3
+ "version": "0.2.1",
4
4
  "description": "Rancher Dashboard Shell",
5
5
  "repository": "https://github.com/rancherlabs/dashboard",
6
6
  "license": "Apache-2.0",
@@ -26,6 +26,7 @@ import {
26
26
  setBrand,
27
27
  setVendor
28
28
  } from '@shell/config/private-label';
29
+ import loadPlugins from '@shell/plugins/plugin';
29
30
 
30
31
  export default {
31
32
  name: 'Login',
@@ -255,6 +256,15 @@ export default {
255
256
  this.$cookies.remove(USERNAME);
256
257
  }
257
258
 
259
+ // User logged with local login - we don't do any redirect/reload, so the boot-time plugin will not run again to laod the plugins
260
+ // so we manually load them here - other SSO auth providers bounce out and back to the Dashboard, so on the bounce-back
261
+ // the plugins will load via the boot-time plugin
262
+ await loadPlugins({
263
+ app: this.$store.app,
264
+ store: this.$store,
265
+ $plugin: this.$store.$plugin
266
+ });
267
+
258
268
  if (this.firstLogin || user[0]?.mustChangePassword) {
259
269
  this.$store.dispatch('auth/setInitialPass', this.password);
260
270
  this.$router.push({ name: 'auth-setup' });
@@ -2,6 +2,8 @@
2
2
  import { GITHUB_CODE, GITHUB_NONCE, BACK_TO } from '@shell/config/query-params';
3
3
  import { get } from '@shell/utils/object';
4
4
  import { base64Decode } from '@shell/utils/crypto';
5
+ import loadPlugins from '@shell/plugins/plugin';
6
+
5
7
  const samlProviders = ['ping', 'adfs', 'keycloak', 'okta', 'shibboleth'];
6
8
 
7
9
  function reply(err, code) {
@@ -60,6 +62,13 @@ export default {
60
62
  if ( res._status === 200) {
61
63
  const backTo = route.query[BACK_TO] || '/';
62
64
 
65
+ // Load plugins
66
+ await loadPlugins({
67
+ app: store.app,
68
+ store,
69
+ $plugin: store.$plugin
70
+ });
71
+
63
72
  redirect(backTo);
64
73
  } else {
65
74
  redirect(`/auth/login?err=${ escape(res) }`);
@@ -5,13 +5,13 @@ import isEqual from 'lodash/isEqual';
5
5
  import { mapPref, DIFF } from '@shell/store/prefs';
6
6
  import { mapFeature, MULTI_CLUSTER, LEGACY } from '@shell/store/features';
7
7
  import { mapGetters } from 'vuex';
8
-
9
8
  import { Banner } from '@components/Banner';
10
9
  import ButtonGroup from '@shell/components/ButtonGroup';
11
10
  import ChartReadme from '@shell/components/ChartReadme';
12
11
  import { Checkbox } from '@components/Form/Checkbox';
13
12
  import LabeledSelect from '@shell/components/form/LabeledSelect';
14
13
  import { LabeledInput } from '@components/Form/LabeledInput';
14
+ import { LabeledTooltip } from '@components/LabeledTooltip';
15
15
  import LazyImage from '@shell/components/LazyImage';
16
16
  import Loading from '@shell/components/Loading';
17
17
  import NameNsDescription from '@shell/components/form/NameNsDescription';
@@ -24,7 +24,7 @@ import Wizard from '@shell/components/Wizard';
24
24
  import TypeDescription from '@shell/components/TypeDescription';
25
25
  import ChartMixin from '@shell/mixins/chart';
26
26
  import ChildHook, { BEFORE_SAVE_HOOKS, AFTER_SAVE_HOOKS } from '@shell/mixins/child-hook';
27
- import { CATALOG, MANAGEMENT, DEFAULT_WORKSPACE } from '@shell/config/types';
27
+ import { CATALOG, MANAGEMENT, DEFAULT_WORKSPACE, CAPI } from '@shell/config/types';
28
28
  import {
29
29
  CHART, FROM_CLUSTER, FROM_TOOLS, HIDE_SIDE_NAV, NAMESPACE, REPO, REPO_TYPE, VERSION, _FLAGGED
30
30
  } from '@shell/config/query-params';
@@ -61,6 +61,7 @@ export default {
61
61
  Checkbox,
62
62
  LabeledInput,
63
63
  LabeledSelect,
64
+ LabeledTooltip,
64
65
  LazyImage,
65
66
  Loading,
66
67
  NameNsDescription,
@@ -93,10 +94,12 @@ export default {
93
94
 
94
95
  this.errors = [];
95
96
 
96
- this.defaultRegistrySetting = await this.$store.dispatch('management/find', {
97
- type: MANAGEMENT.SETTING,
98
- id: 'system-default-registry'
99
- });
97
+ // If the chart doesn't contain system `systemDefaultRegistry` properties there's no point applying them
98
+ if (this.showCustomRegistry) {
99
+ this.clusterRegistry = await this.getClusterRegistry();
100
+ this.globalRegistry = await this.getGlobalRegistry();
101
+ this.defaultRegistrySetting = this.clusterRegistry || this.globalRegistry;
102
+ }
100
103
 
101
104
  this.serverUrlSetting = await this.$store.dispatch('management/find', {
102
105
  type: MANAGEMENT.SETTING,
@@ -281,6 +284,16 @@ export default {
281
284
  */
282
285
  this.chartValues = merge(merge({}, this.versionInfo?.values || {}), userValues);
283
286
 
287
+ if (this.showCustomRegistry) {
288
+ const existingRegistry = this.chartValues?.global?.systemDefaultRegistry || this.chartValues?.global?.cattle?.systemDefaultRegistry;
289
+
290
+ delete this.chartValues?.global?.systemDefaultRegistry;
291
+ delete this.chartValues?.global?.cattle?.systemDefaultRegistry;
292
+
293
+ this.customRegistrySetting = existingRegistry || this.defaultRegistrySetting;
294
+ this.showCustomRegistryInput = !!this.customRegistrySetting;
295
+ }
296
+
284
297
  /* Serializes an object as a YAML document */
285
298
  this.valuesYaml = saferDump(this.chartValues);
286
299
 
@@ -292,6 +305,7 @@ export default {
292
305
  this.loadedVersionValues = this.versionInfo?.values || {};
293
306
  this.loadedVersion = this.version?.key;
294
307
  }
308
+
295
309
  /* Check if chart exists and if required values exist */
296
310
  this.updateStepOneReady();
297
311
 
@@ -317,13 +331,16 @@ export default {
317
331
  };
318
332
 
319
333
  return {
320
- defaultRegistrySetting: null,
334
+ defaultRegistrySetting: '',
335
+ customRegistrySetting: '',
321
336
  serverUrlSetting: null,
322
337
  chartValues: null,
338
+ clusterRegistry: '',
323
339
  originalYamlValues: null,
324
340
  previousYamlValues: null,
325
341
  errors: null,
326
342
  existing: null,
343
+ globalRegistry: '',
327
344
  forceNamespace: null,
328
345
  loadedVersion: null,
329
346
  loadedVersionValues: null,
@@ -335,22 +352,22 @@ export default {
335
352
  valuesYaml: '',
336
353
  project: null,
337
354
  migratedApp: false,
338
-
339
355
  defaultCmdOpts,
340
- customCmdOpts: { ...defaultCmdOpts },
356
+ customCmdOpts: { ...defaultCmdOpts },
341
357
 
342
358
  nameDisabled: false,
343
359
 
344
- preFormYamlOption: VALUES_STATE.YAML,
345
- formYamlOption: VALUES_STATE.YAML,
346
- showDiff: false,
347
- showValuesComponent: true,
348
- showQuestions: true,
349
- showSlideIn: false,
350
- shownReadmeWindows: [],
351
- componentHasTabs: false,
352
- showCommandStep: false,
353
- isNamespaceNew: false,
360
+ preFormYamlOption: VALUES_STATE.YAML,
361
+ formYamlOption: VALUES_STATE.YAML,
362
+ showDiff: false,
363
+ showValuesComponent: true,
364
+ showQuestions: true,
365
+ showSlideIn: false,
366
+ shownReadmeWindows: [],
367
+ componentHasTabs: false,
368
+ showCommandStep: false,
369
+ showCustomRegistryInput: false,
370
+ isNamespaceNew: false,
354
371
 
355
372
  stepBasic: {
356
373
  name: 'basics',
@@ -645,6 +662,23 @@ export default {
645
662
  }
646
663
 
647
664
  return null;
665
+ },
666
+
667
+ /**
668
+ * Check if the chart contains `systemDefaultRegistry` properties. If not we shouldn't apply the setting (or show the UI for them)
669
+ */
670
+ showCustomRegistry() {
671
+ const global = this.versionInfo?.values?.global || {};
672
+
673
+ return global.systemDefaultRegistry !== undefined || global.cattle?.systemDefaultRegistry !== undefined;
674
+ },
675
+
676
+ /**
677
+ * True if we should apply/save the custom registry value. This should be false if matching the global registry
678
+ * (we shouldn't save the global registry to avoid issues on upgrade where we might confused it with a custom one)
679
+ */
680
+ applyCustomRegistry() {
681
+ return this.customRegistrySetting !== this.globalRegistry;
648
682
  }
649
683
  },
650
684
 
@@ -744,6 +778,42 @@ export default {
744
778
  },
745
779
 
746
780
  methods: {
781
+ async getClusterRegistry() {
782
+ const hasPermissionToSeeProvCluster = this.$store.getters[`management/schemaFor`](CAPI.RANCHER_CLUSTER);
783
+
784
+ if (hasPermissionToSeeProvCluster) {
785
+ const mgmCluster = this.$store.getters['currentCluster'];
786
+ const provCluster = await this.$store.dispatch('management/find', {
787
+ type: CAPI.RANCHER_CLUSTER,
788
+ id: mgmCluster.provClusterId
789
+ });
790
+
791
+ if (provCluster.isRke2) { // isRke2 returns true for both RKE2 and K3s clusters.
792
+ const agentConfig = provCluster.spec.rkeConfig.machineSelectorConfig.find(x => !x.machineLabelSelector).config;
793
+
794
+ // If a cluster scoped registry exists,
795
+ // it should be used by default.
796
+ const clusterRegistry = agentConfig?.['system-default-registry'] || '';
797
+
798
+ if (clusterRegistry) {
799
+ return clusterRegistry;
800
+ }
801
+ }
802
+ }
803
+ },
804
+
805
+ async getGlobalRegistry() {
806
+ // Use the global registry as a fallback.
807
+ // If it is an empty string, the container
808
+ // runtime will pull images from docker.io.
809
+ const globalRegistry = await this.$store.dispatch('management/find', {
810
+ type: MANAGEMENT.SETTING,
811
+ id: 'system-default-registry'
812
+ });
813
+
814
+ return globalRegistry.value;
815
+ },
816
+
747
817
  updateValue(value) {
748
818
  if (this.$refs.yaml) {
749
819
  this.$refs.yaml.updateValue(value);
@@ -905,7 +975,7 @@ export default {
905
975
  const cluster = this.currentCluster;
906
976
  const projects = this.$store.getters['management/all'](MANAGEMENT.PROJECT);
907
977
  const systemProjectId = projects.find(p => p.spec?.displayName === 'System')?.id?.split('/')?.[1] || '';
908
- const defaultRegistry = this.defaultRegistrySetting?.value || '';
978
+
909
979
  const serverUrl = this.serverUrlSetting?.value || '';
910
980
  const isWindows = (cluster.workerOSs || []).includes(WINDOWS);
911
981
  const pathPrefix = cluster?.spec?.rancherKubernetesEngineConfig?.prefixPath || '';
@@ -913,8 +983,15 @@ export default {
913
983
 
914
984
  setIfNotSet(cattle, 'clusterId', cluster?.id);
915
985
  setIfNotSet(cattle, 'clusterName', cluster?.nameDisplay);
916
- setIfNotSet(cattle, 'systemDefaultRegistry', defaultRegistry);
917
- setIfNotSet(global, 'systemDefaultRegistry', defaultRegistry);
986
+ if (this.showCustomRegistry) {
987
+ // If this is the current global registry leave it blank. This avoids the scenario on upgrade where a previous global registry that's
988
+ // been updated is confused with a custom user registry
989
+ const registry = this.applyCustomRegistry ? this.customRegistrySetting : '';
990
+
991
+ set(cattle, 'systemDefaultRegistry', registry);
992
+ set(global, 'systemDefaultRegistry', registry);
993
+ }
994
+
918
995
  setIfNotSet(global, 'cattle.systemProjectId', systemProjectId);
919
996
  setIfNotSet(cattle, 'url', serverUrl);
920
997
  setIfNotSet(cattle, 'rkePathPrefix', pathPrefix);
@@ -939,7 +1016,6 @@ export default {
939
1016
  }
940
1017
 
941
1018
  const cluster = this.$store.getters['currentCluster'];
942
- const defaultRegistry = this.defaultRegistrySetting?.value || '';
943
1019
  const serverUrl = this.serverUrlSetting?.value || '';
944
1020
  const isWindows = (cluster.workerOSs || []).includes(WINDOWS);
945
1021
  const pathPrefix = cluster?.spec?.rancherKubernetesEngineConfig?.prefixPath || '';
@@ -948,7 +1024,6 @@ export default {
948
1024
  if ( values.global?.cattle ) {
949
1025
  deleteIfEqual(values.global.cattle, 'clusterId', cluster?.id);
950
1026
  deleteIfEqual(values.global.cattle, 'clusterName', cluster?.nameDisplay);
951
- deleteIfEqual(values.global.cattle, 'systemDefaultRegistry', defaultRegistry);
952
1027
  deleteIfEqual(values.global.cattle, 'url', serverUrl);
953
1028
  deleteIfEqual(values.global.cattle, 'rkePathPrefix', pathPrefix);
954
1029
  deleteIfEqual(values.global.cattle, 'rkeWindowsPathPrefix', windowsPathPrefix);
@@ -966,10 +1041,6 @@ export default {
966
1041
  delete values.global.cattle;
967
1042
  }
968
1043
 
969
- if ( values.global ) {
970
- deleteIfEqual(values.global, 'systemDefaultRegistry', defaultRegistry);
971
- }
972
-
973
1044
  if ( !Object.keys(values.global || {}).length ) {
974
1045
  delete values.global;
975
1046
  }
@@ -1289,7 +1360,6 @@ export default {
1289
1360
  </div>
1290
1361
  </div>
1291
1362
  <NameNsDescription
1292
- v-if="chart"
1293
1363
  v-model="value"
1294
1364
  :description-hidden="true"
1295
1365
  :mode="mode"
@@ -1316,13 +1386,31 @@ export default {
1316
1386
  />
1317
1387
  </template>
1318
1388
  </NameNsDescription>
1389
+ <Checkbox v-model="showCommandStep" class="mb-20" :label="t('catalog.install.steps.helmCli.checkbox', { action, existing: !!existing })" />
1390
+
1391
+ <Checkbox
1392
+ v-if="showCustomRegistry"
1393
+ v-model="showCustomRegistryInput"
1394
+ class="mb-20"
1395
+ :label="t('catalog.chart.registry.custom.checkBoxLabel')"
1396
+ :tooltip="t('catalog.chart.registry.tooltip')"
1397
+ />
1398
+ <div v-if="showCustomRegistry" class="row">
1399
+ <div class="col span-6">
1400
+ <LabeledInput
1401
+ v-if="showCustomRegistryInput"
1402
+ v-model="customRegistrySetting"
1403
+ label-key="catalog.chart.registry.custom.inputLabel"
1404
+ placeholder-key="catalog.chart.registry.custom.placeholder"
1405
+ :min-height="30"
1406
+ />
1407
+ </div>
1408
+ </div>
1319
1409
  <div class="step__values__controls--spacer" style="flex:1">
1320
1410
  &nbsp;
1321
1411
  </div>
1322
1412
  <Banner v-if="isNamespaceNew" color="info" v-html="t('catalog.install.steps.basics.createNamespace', {namespace: value.metadata.namespace}, true) ">
1323
1413
  </Banner>
1324
-
1325
- <Checkbox v-model="showCommandStep" class="mb-20" :label="t('catalog.install.steps.helmCli.checkbox', { action, existing: !!existing })" />
1326
1414
  </div>
1327
1415
  </template>
1328
1416
  <template #clusterTplVersion>