@rancher/shell 3.0.5-rc.8 → 3.0.5

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 (199) hide show
  1. package/assets/styles/base/_color.scss +4 -1
  2. package/assets/styles/global/_tooltip.scss +7 -4
  3. package/assets/styles/themes/_dark.scss +11 -0
  4. package/assets/styles/themes/_light.scss +13 -1
  5. package/assets/styles/themes/_modern.scss +22 -0
  6. package/assets/translations/en-us.yaml +147 -19
  7. package/assets/translations/zh-hans.yaml +0 -1
  8. package/chart/monitoring/grafana/index.vue +8 -2
  9. package/components/ActionMenuShell.vue +3 -1
  10. package/components/Cron/CronExpressionEditor.vue +299 -0
  11. package/components/Cron/CronExpressionEditorModal.vue +247 -0
  12. package/components/Cron/CronTooltip.vue +87 -0
  13. package/components/Cron/types.ts +13 -0
  14. package/components/ForceDirectedTreeChart/composable.ts +11 -0
  15. package/components/PodSecurityAdmission.vue +2 -0
  16. package/components/PromptModal.vue +1 -1
  17. package/components/Resource/Detail/Card/__tests__/StateCard.test.ts +1 -0
  18. package/components/Resource/Detail/CopyToClipboard.vue +78 -0
  19. package/components/Resource/Detail/FetchLoader/__tests__/composables.test.ts +69 -0
  20. package/components/Resource/Detail/FetchLoader/composables.ts +27 -0
  21. package/components/Resource/Detail/Metadata/Annotations/__tests__/index.test.ts +1 -1
  22. package/components/Resource/Detail/Metadata/Annotations/index.vue +1 -1
  23. package/components/Resource/Detail/Metadata/IdentifyingInformation/__tests__/identifying-fields.test.ts +13 -61
  24. package/components/Resource/Detail/Metadata/IdentifyingInformation/__tests__/index.test.ts +33 -6
  25. package/components/Resource/Detail/Metadata/IdentifyingInformation/identifying-fields.ts +24 -38
  26. package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +25 -5
  27. package/components/Resource/Detail/Metadata/KeyValue.vue +12 -23
  28. package/components/Resource/Detail/Metadata/KeyValueRow.vue +144 -0
  29. package/components/Resource/Detail/Metadata/Labels/__tests__/index.test.ts +1 -0
  30. package/components/Resource/Detail/Metadata/Labels/index.vue +1 -0
  31. package/components/Resource/Detail/Metadata/__tests__/KeyValue.test.ts +30 -32
  32. package/components/Resource/Detail/Metadata/__tests__/KeyValueRow.test.ts +108 -0
  33. package/components/Resource/Detail/Metadata/__tests__/composables.test.ts +0 -3
  34. package/components/Resource/Detail/Metadata/__tests__/index.test.ts +12 -5
  35. package/components/Resource/Detail/Metadata/composables.ts +1 -4
  36. package/components/Resource/Detail/Metadata/index.vue +1 -0
  37. package/components/Resource/Detail/Preview/Content.vue +63 -0
  38. package/components/Resource/Detail/Preview/Preview.vue +128 -0
  39. package/components/Resource/Detail/Preview/__tests__/Content.spec.ts +71 -0
  40. package/components/Resource/Detail/Preview/__tests__/Preview.spec.ts +121 -0
  41. package/components/Resource/Detail/ResourcePopover/ResourcePopoverCard.vue +141 -0
  42. package/components/Resource/Detail/ResourcePopover/__tests__/ResourcePopoverCard.test.ts +136 -0
  43. package/components/Resource/Detail/ResourcePopover/__tests__/index.test.ts +245 -0
  44. package/components/Resource/Detail/ResourcePopover/index.vue +226 -0
  45. package/components/Resource/Detail/SpacedRow.vue +1 -0
  46. package/components/Resource/Detail/TitleBar/__tests__/composables.test.ts +0 -5
  47. package/components/Resource/Detail/TitleBar/__tests__/index.test.ts +1 -1
  48. package/components/Resource/Detail/TitleBar/composables.ts +1 -3
  49. package/components/Resource/Detail/TitleBar/index.vue +2 -29
  50. package/components/Resource/Detail/ViewOptions/composable.ts +9 -0
  51. package/components/Resource/Detail/ViewOptions/index.vue +41 -0
  52. package/components/Resource/Detail/__tests__/CopyToClipboard.spec.ts +82 -0
  53. package/components/ResourceDetail/Masthead/legacy.vue +0 -19
  54. package/components/ResourceDetail/index.vue +1 -26
  55. package/components/ResourceTable.vue +24 -0
  56. package/components/SortableTable/index.vue +7 -1
  57. package/components/SortableTable/paging.js +3 -0
  58. package/components/Tabbed/Tab.vue +43 -1
  59. package/components/Tabbed/index.vue +3 -1
  60. package/components/__tests__/Cron/CronExpressionEditor.test.ts +151 -0
  61. package/components/__tests__/Cron/CronExpressionEditorModal.test.ts +81 -0
  62. package/components/auth/login/saml.vue +86 -0
  63. package/components/form/LabeledSelect.vue +8 -8
  64. package/components/form/ProjectMemberEditor.vue +2 -0
  65. package/components/form/ResourceTabs/composable.ts +54 -0
  66. package/components/form/ResourceTabs/index.vue +10 -7
  67. package/components/form/Select.vue +13 -10
  68. package/components/form/__tests__/LabeledSelect.test.ts +133 -0
  69. package/components/form/__tests__/Select.test.ts +134 -0
  70. package/components/nav/Header.vue +6 -5
  71. package/composables/useExtensionManager.ts +17 -0
  72. package/config/home-links.js +12 -0
  73. package/config/labels-annotations.js +0 -1
  74. package/config/page-actions.js +0 -1
  75. package/config/product/explorer.js +3 -1
  76. package/config/product/fleet.js +2 -7
  77. package/config/product/manager.js +0 -5
  78. package/config/query-params.js +1 -0
  79. package/config/router/navigation-guards/clusters.js +2 -1
  80. package/config/router/navigation-guards/products.js +1 -1
  81. package/config/store.js +2 -0
  82. package/core/extension-manager-impl.js +518 -0
  83. package/core/plugins.js +35 -468
  84. package/core/types.ts +8 -2
  85. package/detail/__tests__/autoscaling.horizontalpodautoscaler.test.ts +1 -0
  86. package/detail/catalog.cattle.io.app.vue +7 -4
  87. package/detail/fleet.cattle.io.bundle.vue +1 -5
  88. package/detail/fleet.cattle.io.cluster.vue +3 -2
  89. package/detail/fleet.cattle.io.gitrepo.vue +76 -49
  90. package/detail/fleet.cattle.io.helmop.vue +78 -49
  91. package/dialog/AddonConfigConfirmationDialog.vue +1 -1
  92. package/dialog/GenericPrompt.vue +1 -1
  93. package/dialog/ImportDialog.vue +9 -2
  94. package/dialog/InstallExtensionDialog.vue +18 -10
  95. package/dialog/SloDialog.vue +1 -1
  96. package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +2 -1
  97. package/edit/__tests__/resources.cattle.io.restore.test.ts +106 -0
  98. package/edit/auth/oidc.vue +106 -6
  99. package/edit/auth/saml.vue +5 -5
  100. package/edit/cloudcredential.vue +31 -17
  101. package/edit/constraints.gatekeeper.sh.constraint/index.vue +10 -2
  102. package/edit/fleet.cattle.io.cluster.vue +19 -0
  103. package/edit/fleet.cattle.io.gitrepo.vue +23 -16
  104. package/edit/monitoring.coreos.com.alertmanagerconfig/index.vue +12 -11
  105. package/edit/monitoring.coreos.com.alertmanagerconfig/receiverConfig.vue +11 -1
  106. package/edit/provisioning.cattle.io.cluster/index.vue +14 -19
  107. package/edit/provisioning.cattle.io.cluster/rke2.vue +11 -3
  108. package/edit/provisioning.cattle.io.cluster/tabs/AddOnAdditionalManifest.vue +1 -0
  109. package/edit/provisioning.cattle.io.cluster/tabs/AddOnConfig.vue +1 -0
  110. package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +1 -0
  111. package/edit/provisioning.cattle.io.cluster/tabs/etcd/S3Config.vue +1 -0
  112. package/edit/provisioning.cattle.io.cluster/tabs/registries/index.vue +2 -0
  113. package/edit/provisioning.cattle.io.cluster/tabs/upgrade/DrainOptions.vue +6 -0
  114. package/edit/resources.cattle.io.restore.vue +5 -8
  115. package/initialize/install-plugins.js +1 -3
  116. package/list/__tests__/workload.test.ts +1 -0
  117. package/list/workload.vue +8 -1
  118. package/machine-config/components/GCEImage.vue +6 -5
  119. package/machine-config/google.vue +11 -6
  120. package/mixins/__tests__/auth-config.test.ts +4 -6
  121. package/mixins/__tests__/chart.test.ts +139 -1
  122. package/mixins/auth-config.js +33 -10
  123. package/mixins/chart.js +58 -18
  124. package/models/__tests__/namespace.test.ts +69 -0
  125. package/models/apps.statefulset.js +8 -10
  126. package/models/chart.js +5 -1
  127. package/models/fleet-application.js +16 -46
  128. package/models/fleet.cattle.io.bundle.js +1 -38
  129. package/models/fleet.cattle.io.gitrepo.js +4 -0
  130. package/models/fleet.cattle.io.helmop.js +4 -0
  131. package/models/management.cattle.io.cluster.js +1 -1
  132. package/models/management.cattle.io.project.js +12 -0
  133. package/models/namespace.js +30 -0
  134. package/models/workload.js +4 -1
  135. package/package.json +10 -10
  136. package/pages/auth/login.vue +8 -3
  137. package/pages/auth/logout.vue +6 -5
  138. package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +26 -11
  139. package/pages/c/_cluster/apps/charts/chart.vue +29 -20
  140. package/pages/c/_cluster/apps/charts/index.vue +1 -0
  141. package/pages/c/_cluster/apps/charts/install.vue +6 -5
  142. package/pages/c/_cluster/explorer/tools/__tests__/index.test.ts +102 -12
  143. package/pages/c/_cluster/explorer/tools/index.vue +145 -254
  144. package/pages/c/_cluster/manager/cloudCredential/index.vue +18 -1
  145. package/pages/c/_cluster/manager/drivers/kontainerDriver/index.vue +12 -2
  146. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +1 -1
  147. package/pages/c/_cluster/uiplugins/__tests__/index.spec.ts +318 -0
  148. package/pages/c/_cluster/uiplugins/index.vue +221 -363
  149. package/pages/home.vue +1 -9
  150. package/plugins/axios.js +3 -2
  151. package/plugins/dashboard-store/resource-class.js +49 -0
  152. package/plugins/ember-cookie.js +7 -3
  153. package/plugins/steve/subscribe.js +4 -2
  154. package/public/index.html +2 -1
  155. package/rancher-components/Card/Card.vue +1 -1
  156. package/rancher-components/Form/Checkbox/Checkbox.vue +1 -1
  157. package/rancher-components/Form/Radio/RadioButton.vue +1 -1
  158. package/rancher-components/Form/Radio/RadioGroup.vue +1 -1
  159. package/rancher-components/LabeledTooltip/LabeledTooltip.vue +1 -11
  160. package/rancher-components/Pill/RcCounterBadge/RcCounterBadge.test.ts +53 -0
  161. package/rancher-components/Pill/RcCounterBadge/RcCounterBadge.vue +65 -0
  162. package/rancher-components/Pill/RcCounterBadge/index.ts +1 -0
  163. package/rancher-components/Pill/RcCounterBadge/types.ts +7 -0
  164. package/rancher-components/Pill/RcStatusBadge/RcStatusBadge.vue +1 -1
  165. package/rancher-components/Pill/RcStatusBadge/index.ts +1 -1
  166. package/rancher-components/Pill/RcStatusIndicator/RcStatusIndicator.vue +3 -3
  167. package/rancher-components/Pill/RcStatusIndicator/types.ts +1 -1
  168. package/rancher-components/Pill/RcTag/RcTag.test.ts +64 -0
  169. package/rancher-components/Pill/RcTag/RcTag.vue +94 -0
  170. package/rancher-components/Pill/RcTag/index.ts +1 -0
  171. package/rancher-components/Pill/RcTag/types.ts +9 -0
  172. package/rancher-components/Pill/types.ts +1 -0
  173. package/rancher-components/RcItemCard/RcItemCard.vue +1 -0
  174. package/rancher-components/RcItemCard/RcItemCardAction.vue +12 -0
  175. package/scripts/test-plugins-build.sh +0 -1
  176. package/store/__tests__/catalog.test.ts +63 -0
  177. package/store/__tests__/cookies.test.ts +72 -0
  178. package/store/auth.js +33 -10
  179. package/store/catalog.js +2 -2
  180. package/store/cookies.ts +30 -0
  181. package/store/prefs.js +10 -5
  182. package/store/type-map.js +3 -15
  183. package/types/extension-manager.ts +26 -0
  184. package/types/shell/index.d.ts +123 -27
  185. package/utils/__tests__/product.test.ts +129 -0
  186. package/utils/__tests__/resource.test.ts +87 -0
  187. package/utils/alertmanagerconfig.js +2 -2
  188. package/utils/auth.js +4 -77
  189. package/utils/product.ts +39 -0
  190. package/utils/resource.ts +35 -0
  191. package/utils/select.js +0 -24
  192. package/utils/validators/formRules/__tests__/index.test.ts +3 -0
  193. package/utils/validators/formRules/index.ts +2 -1
  194. package/vue.config.js +1 -1
  195. package/components/Resource/Detail/Metadata/Rectangle.vue +0 -34
  196. package/components/Resource/Detail/Metadata/__tests__/Rectangle.test.ts +0 -24
  197. package/components/ResourceDetail/Masthead/__tests__/legacy.test.ts +0 -65
  198. package/utils/cookie-universal.js +0 -10
  199. /package/components/{ForceDirectedTreeChart.vue → ForceDirectedTreeChart/index.vue} +0 -0
@@ -165,6 +165,7 @@ export default {
165
165
  <LabeledInput
166
166
  v-if="!config.skipSSLVerify"
167
167
  v-model:value="config.endpointCA"
168
+ :mode="mode"
168
169
  type="multiline"
169
170
  :label="t('cluster.rke2.etcd.s3config.endpointCA.label')"
170
171
  :placeholder="ccData.defaultEndpointCA"
@@ -78,6 +78,7 @@ export default {
78
78
  <div class="row">
79
79
  <Checkbox
80
80
  :value="showCustomRegistryInput"
81
+ :mode="mode"
81
82
  :label="t('cluster.privateRegistry.label')"
82
83
  data-testid="registries-enable-checkbox"
83
84
  @update:value="$emit('custom-registry-changed', $event)"
@@ -90,6 +91,7 @@ export default {
90
91
  <div class="col span-6">
91
92
  <LabeledInput
92
93
  :value="registryHost"
94
+ :mode="mode"
93
95
  label-key="catalog.chart.registry.custom.inputLabel"
94
96
  placeholder-key="catalog.chart.registry.custom.placeholder"
95
97
  :min-height="30"
@@ -102,6 +102,7 @@ export default {
102
102
  <div class="mt-20">
103
103
  <Checkbox
104
104
  v-model:value="deleteEmptyDirData"
105
+ :mode="mode"
105
106
  label-key="cluster.rke2.drain.deleteEmptyDir.label"
106
107
  tooltip-key="cluster.rke2.drain.deleteEmptyDir.tooltip"
107
108
  @update:value="update"
@@ -110,6 +111,7 @@ export default {
110
111
  <div>
111
112
  <Checkbox
112
113
  v-model:value="force"
114
+ :mode="mode"
113
115
  label="Delete standalone pods"
114
116
  label-key="cluster.rke2.drain.force.label"
115
117
  tooltip="Delete standalone pods which are not managed by a Workload controller (Deployment, Job, etc). Draining will fail if this is not set and there are standalone pods."
@@ -120,12 +122,14 @@ export default {
120
122
  <div>
121
123
  <Checkbox
122
124
  v-model:value="customGracePeriod"
125
+ :mode="mode"
123
126
  label-key="cluster.rke2.drain.gracePeriod.checkboxLabel"
124
127
  @update:value="update"
125
128
  />
126
129
  <UnitInput
127
130
  v-if="customGracePeriod"
128
131
  v-model:value="gracePeriod"
132
+ :mode="mode"
129
133
  label-key="cluster.rke2.drain.gracePeriod.inputLabel"
130
134
  :suffix="t('suffix.seconds', {count: timeout})"
131
135
  class="mb-10"
@@ -135,12 +139,14 @@ export default {
135
139
  <div>
136
140
  <Checkbox
137
141
  v-model:value="customTimeout"
142
+ :mode="mode"
138
143
  label-key="cluster.rke2.drain.timeout.checkboxLabel"
139
144
  @update:value="update"
140
145
  />
141
146
  <UnitInput
142
147
  v-if="customTimeout"
143
148
  v-model:value="timeout"
149
+ :mode="mode"
144
150
  label-key="cluster.rke2.drain.timeout.inputLabel"
145
151
  :suffix="t('suffix.seconds', {count: timeout})"
146
152
  class="drain-timeout"
@@ -159,19 +159,16 @@ export default {
159
159
  if (neu === 'useDefault') {
160
160
  delete this.value.spec.storageLocation;
161
161
  delete this.value.spec.backupFilename;
162
- } else if (!this.value.spec.storageLocation && neu === 'configureS3') {
163
- this.value.spec['storageLocation'] = { s3: {} };
164
- this.s3 = this.value.spec.storageLocation.s3;
165
- }
166
- if (neu === 'useBackup') {
162
+ this.s3 = {};
163
+ } else if (neu === 'configureS3') {
164
+ this.s3 = this.value.spec.storageLocation?.s3 || {};
165
+ this.value.spec.storageLocation = { s3: this.s3 };
166
+ } else if (neu === 'useBackup') {
167
167
  delete this.value.spec.storageLocation;
168
168
 
169
169
  if (this.availableBackups.length === 1) {
170
170
  this.updateTargetBackup(this.availableBackups[0]);
171
171
  }
172
- } else {
173
- delete this.value.spec.backupFilename;
174
- this.value.spec.storageLocation = { s3: this.s3 };
175
172
  }
176
173
  },
177
174
 
@@ -12,7 +12,6 @@ import i18n from '@shell/plugins/i18n';
12
12
  import globalFormatters from '@shell/plugins/global-formatters';
13
13
 
14
14
  import axios from '@shell/utils/axios';
15
- import cookieUniversal from '@shell/utils/cookie-universal';
16
15
  import config from '@shell/utils/config';
17
16
  import axiosShell from '@shell/plugins/axios';
18
17
  import codeMirror from '@shell/plugins/codemirror-loader';
@@ -47,7 +46,7 @@ export async function installPlugins(vueApp) {
47
46
  }
48
47
 
49
48
  export async function installInjectedPlugins(app, vueApp) {
50
- const pluginDefinitions = [config, cookieUniversal, axios, plugins, pluginsLoader, axiosShell, intNumber, codeMirror, nuxtClientInit, replaceAll, plugin, steveCreateWorker, emberCookie, internalApiPlugin];
49
+ const pluginDefinitions = [config, axios, plugins, pluginsLoader, axiosShell, intNumber, codeMirror, nuxtClientInit, replaceAll, plugin, steveCreateWorker, emberCookie, internalApiPlugin];
51
50
 
52
51
  const installations = pluginDefinitions.map(async(pluginDefinition) => {
53
52
  if (typeof pluginDefinition === 'function') {
@@ -64,7 +63,6 @@ export async function installInjectedPlugins(app, vueApp) {
64
63
  // If there's any performance reasons this can be done concurrently with all of the installation promises above but I felt it was organizationally better to keep both i18n items together.
65
64
  await app.store.dispatch('i18n/init');
66
65
 
67
- // Order matters here. This is coming after the other plugins specifically so $cookies can be installed. i18n/init relies on prefs/get which relies on $cookies.
68
66
  vueApp.use(i18n, { store: app.store });
69
67
  }
70
68
 
@@ -37,6 +37,7 @@ describe('component: workload', () => {
37
37
  'prefs/get': () => resource,
38
38
  'cluster/schemaFor': () => {},
39
39
  'cluster/all': () => [{}],
40
+ 'features/get': () => false,
40
41
  }
41
42
  },
42
43
  $fetchState: {
package/list/workload.vue CHANGED
@@ -5,6 +5,7 @@ import {
5
5
  } from '@shell/config/types';
6
6
  import ResourceFetch from '@shell/mixins/resource-fetch';
7
7
  import PaginatedResourceTable from '@shell/components/PaginatedResourceTable';
8
+ import { STEVE_CACHE } from '@shell/store/features';
8
9
 
9
10
  const workloadSchema = {
10
11
  id: 'workload',
@@ -84,11 +85,17 @@ export default {
84
85
  },
85
86
 
86
87
  data() {
88
+ const allTypes = this.$route.params.resource === workloadSchema.id;
89
+
90
+ if (allTypes && this.$store.getters['features/get'](STEVE_CACHE)) {
91
+ this.$store.dispatch('loadingError', new Error(this.t('nav.failWhale.resourceListNotFound', { resource: workloadSchema.id }, true)));
92
+
93
+ return;
94
+ }
87
95
  // Ensure these are set on load (to determine if the NS filter is required) rather than too late on `fetch`
88
96
  const { loadResources, loadIndeterminate } = $loadingResources(this.$route, this.$store);
89
97
 
90
98
  const { params:{ resource: type } } = this.$route;
91
- const allTypes = this.$route.params.resource === workloadSchema.id;
92
99
  const schema = type !== workloadSchema.id ? this.$store.getters['cluster/schemaFor'](type) : workloadSchema;
93
100
  const paginationEnabled = !allTypes && this.$store.getters[`cluster/paginationEnabled`]?.({ id: type });
94
101
 
@@ -43,6 +43,10 @@ export default {
43
43
  type: String,
44
44
  default: _CREATE,
45
45
  },
46
+ poolCreateMode: {
47
+ type: Boolean,
48
+ default: true
49
+ },
46
50
  location: {
47
51
  type: Object,
48
52
  required: true
@@ -85,7 +89,7 @@ export default {
85
89
  },
86
90
  created() {
87
91
  this.debouncedLoadFamilies = debounce(this.getFamilies, 500);
88
- if (this.mode !== _CREATE) {
92
+ if (!this.poolCreateMode) {
89
93
  this.imageProjects = `${ this.getProjectFromImage() }`;
90
94
  }
91
95
  },
@@ -111,9 +115,6 @@ export default {
111
115
 
112
116
  };
113
117
  },
114
- isCreate() {
115
- return this.mode === _CREATE;
116
- },
117
118
  project() {
118
119
  return this.value.project;
119
120
  },
@@ -284,7 +285,7 @@ export default {
284
285
  async getImages(val) {
285
286
  this.loadingImages = true;
286
287
  try {
287
- const isOriginal = !this.isCreate && this.machineImage === this.getImageNameFromImage(this.originalMachineImage);
288
+ const isOriginal = !this.poolCreateMode && this.machineImage === this.getImageNameFromImage(this.originalMachineImage);
288
289
 
289
290
  this.machineImages = await this.getImagesInProject(val, this.showDeprecated);
290
291
  // If we had to reload list of images, we need to reset selected image if it is no longer in the list,
@@ -79,7 +79,11 @@ export default {
79
79
  disabled: {
80
80
  type: Boolean,
81
81
  default: false
82
- }
82
+ },
83
+ poolCreateMode: {
84
+ type: Boolean,
85
+ default: true
86
+ },
83
87
  },
84
88
 
85
89
  async fetch() {
@@ -123,7 +127,7 @@ export default {
123
127
  };
124
128
  },
125
129
  created() {
126
- if (this.mode === _CREATE) {
130
+ if (this.poolCreateMode) {
127
131
  this.$emit('validationChanged', false);
128
132
  this.value.project = this.projectId;
129
133
  for (const key in this.defaultConfig) {
@@ -157,7 +161,7 @@ export default {
157
161
  'value.setExternalFirewallRulePrefix'(neu) {
158
162
  if (!neu) {
159
163
  this.value.openPort = [];
160
- } else if (this.isCreate) {
164
+ } else if (this.poolCreateMode) {
161
165
  this.value.openPort.push('6443');
162
166
  } else {
163
167
  this.value.openPort = this.originalOpenPort.length > 0 ? this.originalOpenPort : ['6443'];
@@ -334,7 +338,7 @@ export default {
334
338
 
335
339
  if (!cur ) {
336
340
  // If default is not actually available, reset
337
- if (this.isCreate) {
341
+ if (this.poolCreateMode) {
338
342
  this.value.diskType = '';
339
343
  }
340
344
  } else {
@@ -446,6 +450,7 @@ export default {
446
450
  :credentialId="credentialId"
447
451
  :projectId="value.project"
448
452
  :originalMachineImage="originalMachineImage"
453
+ :pool-create-mode="poolCreateMode"
449
454
  :mode="mode"
450
455
  :location="location"
451
456
  :rules="{machineImage: fvGetAndReportPathRules('machineImage')}"
@@ -494,7 +499,7 @@ export default {
494
499
  label-key="cluster.machineConfig.gce.network.label"
495
500
  :mode="mode"
496
501
  :options="networkOptions"
497
- :disabled="!isCreate"
502
+ :disabled="!poolCreateMode"
498
503
  option-key="name"
499
504
  option-label="label"
500
505
  :loading="loadingNetworks"
@@ -508,7 +513,7 @@ export default {
508
513
  label-key="cluster.machineConfig.gce.subnetwork.label"
509
514
  :mode="mode"
510
515
  :options="subnetworkOptions"
511
- :disabled="!isCreate"
516
+ :disabled="!poolCreateMode"
512
517
  option-key="name"
513
518
  option-label="name"
514
519
  :loading="loadingNetworks"
@@ -1,6 +1,7 @@
1
1
  import { mount } from '@vue/test-utils';
2
2
  import authConfigMixin from '@shell/mixins/auth-config';
3
-
3
+ import childHook from '@shell/mixins/child-hook';
4
+ //
4
5
  describe('mixin: authConfigMixin', () => {
5
6
  describe('method: save', () => {
6
7
  const componentMock = (model: any) => ({
@@ -11,10 +12,7 @@ describe('mixin: authConfigMixin', () => {
11
12
  computed: { principal: () => ({ me: {} }) },
12
13
  global: {
13
14
  mocks: {
14
- $store: {
15
- dispatch: () => model,
16
- commit: () => ({ 'auth/loggedInAs': jest.fn() }),
17
- },
15
+ $store: { dispatch: () => model },
18
16
  $route: {
19
17
  params: { id: '123' },
20
18
  query: { mode: 'edit' },
@@ -24,7 +22,7 @@ describe('mixin: authConfigMixin', () => {
24
22
  });
25
23
  const FakeComponent = {
26
24
  render() {},
27
- mixins: [authConfigMixin],
25
+ mixins: [authConfigMixin, childHook],
28
26
  methods: { applyHooks: jest.fn() },
29
27
  };
30
28
 
@@ -29,7 +29,10 @@ describe('chartMixin', () => {
29
29
  return () => 'repo';
30
30
  },
31
31
  'catalog/chart': () => {
32
- return { id: chartId };
32
+ return {
33
+ id: chartId,
34
+ matchingInstalledApps: []
35
+ };
33
36
  },
34
37
  'i18n/t': () => jest.fn()
35
38
  }
@@ -101,4 +104,139 @@ describe('chartMixin', () => {
101
104
  expect(warnings).toHaveLength(expected as number);
102
105
  }
103
106
  );
107
+
108
+ describe('fetchChart', () => {
109
+ it('should call catalog/version with showDeprecated', async() => {
110
+ const mockStore = {
111
+ dispatch: jest.fn(() => Promise.resolve()),
112
+ getters: {
113
+ currentCluster: () => {},
114
+ isRancher: () => true,
115
+ 'catalog/repo': () => {
116
+ return () => 'repo';
117
+ },
118
+ 'catalog/chart': () => {
119
+ return {
120
+ id: 'chart-id', versions: [{ version: '1.0.0' }], matchingInstalledApps: []
121
+ };
122
+ },
123
+ 'catalog/version': jest.fn(),
124
+ 'prefs/get': () => {},
125
+ 'i18n/t': () => jest.fn()
126
+ }
127
+ };
128
+
129
+ const DummyComponent = {
130
+ mixins: [ChartMixin],
131
+ template: '<div></div>',
132
+ };
133
+
134
+ const wrapper = mount(
135
+ DummyComponent,
136
+ {
137
+ global: {
138
+ mocks: {
139
+ $store: mockStore,
140
+ $route: {
141
+ query: {
142
+ chart: 'chart_name',
143
+ deprecated: 'true'
144
+ }
145
+ }
146
+ }
147
+ }
148
+ });
149
+
150
+ await wrapper.vm.fetchChart();
151
+
152
+ expect(mockStore.getters['catalog/version']).toHaveBeenCalledWith(expect.objectContaining({ showDeprecated: true }));
153
+ });
154
+ });
155
+
156
+ describe('action', () => {
157
+ const DummyComponent = {
158
+ mixins: [ChartMixin],
159
+ template: '<div></div>',
160
+ };
161
+
162
+ const mockStore = {
163
+ dispatch: jest.fn(() => Promise.resolve()),
164
+ getters: { 'i18n/t': (key: string) => key }
165
+ };
166
+
167
+ it('should return "install" action when not installed', () => {
168
+ const wrapper = mount(DummyComponent, {
169
+ data: () => ({ existing: null }),
170
+ global: { mocks: { $store: mockStore } }
171
+ });
172
+
173
+ expect(wrapper.vm.action).toStrictEqual({
174
+ name: 'install',
175
+ tKey: 'install',
176
+ icon: 'icon-plus',
177
+ });
178
+ });
179
+
180
+ it('should return "editVersion" action when installed and on same version', () => {
181
+ const wrapper = mount(DummyComponent, {
182
+ data: () => ({
183
+ existing: { spec: { chart: { metadata: { version: '1.0.0' } } } },
184
+ version: { version: '1.0.0' }
185
+ }),
186
+ global: {
187
+ mocks: {
188
+ $store: mockStore,
189
+ $route: { query: {} }
190
+ }
191
+ }
192
+ });
193
+
194
+ expect(wrapper.vm.action).toStrictEqual({
195
+ name: 'editVersion',
196
+ tKey: 'edit',
197
+ icon: 'icon-edit',
198
+ });
199
+ });
200
+
201
+ it('should return "upgrade" action when installed and on a newer version', () => {
202
+ const wrapper = mount(DummyComponent, {
203
+ data: () => ({
204
+ existing: { spec: { chart: { metadata: { version: '1.0.0' } } } },
205
+ version: { version: '1.0.1' }
206
+ }),
207
+ global: {
208
+ mocks: {
209
+ $store: mockStore,
210
+ $route: { query: {} }
211
+ }
212
+ }
213
+ });
214
+
215
+ expect(wrapper.vm.action).toStrictEqual({
216
+ name: 'upgradeVersion',
217
+ tKey: 'upgrade',
218
+ icon: 'icon-upgrade-alt',
219
+ });
220
+ });
221
+ it('should return "downgrade" action when installed and on an older version', () => {
222
+ const wrapper = mount(DummyComponent, {
223
+ data: () => ({
224
+ existing: { spec: { chart: { metadata: { version: '1.0.1' } } } },
225
+ version: { version: '1.0.0' }
226
+ }),
227
+ global: {
228
+ mocks: {
229
+ $store: mockStore,
230
+ $route: { query: {} }
231
+ }
232
+ }
233
+ });
234
+
235
+ expect(wrapper.vm.action).toStrictEqual({
236
+ name: 'downgrade',
237
+ tKey: 'downgrade',
238
+ icon: 'icon-downgrade-alt',
239
+ });
240
+ });
241
+ });
104
242
  });
@@ -1,7 +1,7 @@
1
1
  import { _EDIT } from '@shell/config/query-params';
2
2
  import { NORMAN, MANAGEMENT } from '@shell/config/types';
3
3
  import { AFTER_SAVE_HOOKS, BEFORE_SAVE_HOOKS } from '@shell/mixins/child-hook';
4
- import { BASE_SCOPES } from '@shell/store/auth';
4
+ import { BASE_SCOPES, SLO_AUTH_PROVIDERS } from '@shell/store/auth';
5
5
  import { addObject, findBy } from '@shell/utils/array';
6
6
  import { exceptionToErrorsArray } from '@shell/utils/error';
7
7
  import difference from 'lodash/difference';
@@ -30,6 +30,10 @@ export default {
30
30
  }
31
31
  },
32
32
 
33
+ created() {
34
+ this.registerAfterHook(this.updateAuthProviders, 'force-update-auth-providers');
35
+ },
36
+
33
37
  async fetch() {
34
38
  await this.mixinFetch();
35
39
  },
@@ -88,6 +92,23 @@ export default {
88
92
  },
89
93
 
90
94
  methods: {
95
+ updateAuthProviders() {
96
+ // we need to forcefully re-fetch the authProviders list so that we can update the logout method
97
+ // this is to satisfy the SLO usecase where after setting an auth provider the logout method
98
+ // wasn't being updated because the resource is not watchable
99
+ this.$store.dispatch('auth/getAuthProviders', { force: true });
100
+ },
101
+
102
+ setSloType(selectedModel) {
103
+ if (!selectedModel.logoutAllEnabled && !selectedModel.logoutAllForced) {
104
+ this.sloType = SLO_OPTION_VALUES.rancher;
105
+ } else if (selectedModel.logoutAllEnabled && selectedModel.logoutAllForced) {
106
+ this.sloType = SLO_OPTION_VALUES.all;
107
+ } else if (selectedModel.logoutAllEnabled && !selectedModel.logoutAllForced) {
108
+ this.sloType = SLO_OPTION_VALUES.both;
109
+ }
110
+ },
111
+
91
112
  async mixinFetch() {
92
113
  this.authConfigName = this.$route.params.id;
93
114
 
@@ -115,20 +136,16 @@ export default {
115
136
  if (this.model.openLdapConfig) {
116
137
  this.showLdap = true;
117
138
  }
118
- if (this.value.configType === 'saml') {
139
+
140
+ // Logic for Single Logout/SLO for auth providers
141
+ if (this.value?.configType && SLO_AUTH_PROVIDERS.includes(this.value?.configType)) {
119
142
  if (!this.model.rancherApiHost || !this.model.rancherApiHost.length) {
120
143
  this.model['rancherApiHost'] = this.serverUrl;
121
144
  }
122
145
 
123
146
  // setting data for SLO
124
147
  if (this.model && Object.keys(this.model).includes('logoutAllSupported')) {
125
- if (!this.model.logoutAllEnabled && !this.model.logoutAllForced) {
126
- this.sloType = SLO_OPTION_VALUES.rancher;
127
- } else if (this.model.logoutAllEnabled && this.model.logoutAllForced) {
128
- this.sloType = SLO_OPTION_VALUES.all;
129
- } else if (this.model.logoutAllEnabled && !this.model.logoutAllForced) {
130
- this.sloType = SLO_OPTION_VALUES.both;
131
- }
148
+ this.setSloType(this.model);
132
149
  }
133
150
  }
134
151
 
@@ -220,7 +237,7 @@ export default {
220
237
  addObject(this.model.allowedPrincipalIds, this.principal.id);
221
238
  }
222
239
  // Session has switched to new 'me', ensure we react
223
- this.$store.commit('auth/loggedInAs', this.principal.id);
240
+ this.$store.dispatch('auth/loggedInAs', this.principal.id);
224
241
  } else {
225
242
  console.warn(`Unable to find principal marked as 'me'`); // eslint-disable-line no-console
226
243
  }
@@ -292,6 +309,12 @@ export default {
292
309
  // must be cancelling edit of an enabled config; reset any changes and return to add users/groups view for that config
293
310
  this.$store.dispatch(`rancher/clone`, { resource: this.originalModel }).then((cloned) => {
294
311
  this.model = cloned;
312
+
313
+ // reset SLO type (radio option)
314
+ if (cloned && Object.keys(cloned).includes('logoutAllSupported')) {
315
+ this.setSloType(cloned);
316
+ }
317
+
295
318
  this.editConfig = false;
296
319
  });
297
320
  }
package/mixins/chart.js CHANGED
@@ -1,6 +1,5 @@
1
1
 
2
2
  import { mapGetters } from 'vuex';
3
-
4
3
  import {
5
4
  REPO_TYPE, REPO, CHART, VERSION, NAMESPACE, NAME, DESCRIPTION as DESCRIPTION_QUERY, DEPRECATED as DEPRECATED_QUERY, HIDDEN, _FLAGGED, _CREATE, _EDIT
6
5
  } from '@shell/config/query-params';
@@ -9,10 +8,9 @@ import { SHOW_PRE_RELEASE, mapPref } from '@shell/store/prefs';
9
8
  import { NAME as EXPLORER } from '@shell/config/product/explorer';
10
9
  import { NAME as MANAGER } from '@shell/config/product/manager';
11
10
  import { OPA_GATE_KEEPER_ID } from '@shell/pages/c/_cluster/gatekeeper/index.vue';
12
-
13
11
  import { formatSi, parseSi } from '@shell/utils/units';
14
12
  import { CAPI, CATALOG } from '@shell/config/types';
15
- import { isPrerelease } from '@shell/utils/version';
13
+ import { isPrerelease, compare } from '@shell/utils/version';
16
14
  import difference from 'lodash/difference';
17
15
  import { LINUX, APP_UPGRADE_STATUS } from '@shell/store/catalog';
18
16
  import { clone } from '@shell/utils/object';
@@ -230,11 +228,27 @@ export default {
230
228
  },
231
229
 
232
230
  action() {
233
- if (this.existing) {
234
- return this.currentVersion === this.targetVersion ? 'update' : 'upgrade';
231
+ if (!this.existing) {
232
+ return {
233
+ name: 'install', tKey: 'install', icon: 'icon-plus'
234
+ };
235
+ }
236
+
237
+ if (this.currentVersion === this.targetVersion) {
238
+ return {
239
+ name: 'editVersion', tKey: 'edit', icon: 'icon-edit'
240
+ };
241
+ }
242
+
243
+ if (compare(this.currentVersion, this.targetVersion) < 0) {
244
+ return {
245
+ name: 'upgradeVersion', tKey: 'upgrade', icon: 'icon-upgrade-alt'
246
+ };
235
247
  }
236
248
 
237
- return 'install';
249
+ return {
250
+ name: 'downgrade', tKey: 'downgrade', icon: 'icon-downgrade-alt'
251
+ };
238
252
  },
239
253
 
240
254
  isChartTargeted() {
@@ -276,7 +290,10 @@ export default {
276
290
  async fetchChart() {
277
291
  this.versionInfoError = null;
278
292
 
279
- await this.$store.dispatch('catalog/load'); // not the problem
293
+ await Promise.all([
294
+ this.$store.dispatch('catalog/load'),
295
+ this.$store.dispatch('cluster/findAll', { type: CATALOG.APP })
296
+ ]);
280
297
 
281
298
  this.fetchStoreChart();
282
299
 
@@ -316,6 +333,15 @@ export default {
316
333
  this.mode = _CREATE;
317
334
  this.existing = null;
318
335
  }
336
+ } else if (this.chart) {
337
+ const matching = this.chart.matchingInstalledApps;
338
+
339
+ if (matching.length === 1) {
340
+ this.existing = matching[0];
341
+ this.mode = _EDIT;
342
+ } else {
343
+ this.mode = _CREATE;
344
+ }
319
345
  } else {
320
346
  // Regular create
321
347
 
@@ -345,10 +371,11 @@ export default {
345
371
 
346
372
  try {
347
373
  this.version = this.$store.getters['catalog/version']({
348
- repoType: this.query.repoType,
349
- repoName: this.query.repoName,
350
- chartName: this.query.chartName,
351
- versionName: this.query.versionName
374
+ repoType: this.query.repoType,
375
+ repoName: this.query.repoName,
376
+ chartName: this.query.chartName,
377
+ versionName: this.query.versionName,
378
+ showDeprecated: this.showDeprecated
352
379
  });
353
380
  } catch (e) {
354
381
  console.error('Unable to fetch Version: ', e); // eslint-disable-line no-console
@@ -503,18 +530,24 @@ export default {
503
530
  version: this.query.versionName,
504
531
  };
505
532
 
533
+ const query = {
534
+ [REPO_TYPE]: provider.repoType,
535
+ [REPO]: provider.repoName,
536
+ [CHART]: provider.name,
537
+ [VERSION]: provider.version,
538
+ };
539
+
540
+ if (this.showDeprecated) {
541
+ query[DEPRECATED_QUERY] = this.query.deprecated;
542
+ }
543
+
506
544
  return {
507
545
  name: install ? 'c-cluster-apps-charts-install' : 'c-cluster-apps-charts-chart',
508
546
  params: {
509
547
  cluster: this.$route.params.cluster,
510
548
  product: this.$store.getters['productId'],
511
549
  },
512
- query: {
513
- [REPO_TYPE]: provider.repoType,
514
- [REPO]: provider.repoName,
515
- [CHART]: provider.name,
516
- [VERSION]: provider.version,
517
- }
550
+ query
518
551
  };
519
552
  },
520
553
 
@@ -530,13 +563,20 @@ export default {
530
563
  },
531
564
 
532
565
  clusterToolsLocation() {
566
+ const query = {};
567
+
568
+ if (this.showDeprecated) {
569
+ query[DEPRECATED_QUERY] = this.query.deprecated;
570
+ }
571
+
533
572
  return {
534
573
  name: `c-cluster-explorer-tools`,
535
574
  params: {
536
575
  product: EXPLORER,
537
576
  cluster: this.$store.getters['clusterId'],
538
577
  resource: CATALOG.APP,
539
- }
578
+ },
579
+ query
540
580
  };
541
581
  },
542
582