@rancher/shell 3.0.2-rc.5 → 3.0.2

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 (219) hide show
  1. package/assets/images/providers/nutanix.svg +12 -1
  2. package/assets/styles/base/_basic.scss +2 -1
  3. package/assets/styles/base/_helpers.scss +4 -0
  4. package/assets/styles/base/_variables.scss +2 -0
  5. package/assets/styles/global/_labeled-input.scss +5 -13
  6. package/assets/styles/global/_layout.scss +4 -1
  7. package/assets/styles/global/_select.scss +5 -0
  8. package/assets/styles/themes/_dark.scss +1 -3
  9. package/assets/styles/themes/_light.scss +5 -1
  10. package/assets/translations/en-us.yaml +130 -23
  11. package/assets/translations/zh-hans.yaml +0 -3
  12. package/cloud-credential/azure.vue +1 -1
  13. package/components/ActionMenuShell.vue +105 -0
  14. package/components/AppModal.vue +2 -2
  15. package/components/AsyncButton.vue +2 -0
  16. package/components/ButtonGroup.vue +9 -2
  17. package/components/ClusterBadge.vue +1 -0
  18. package/components/ClusterIconMenu.vue +3 -0
  19. package/components/ClusterProviderIcon.vue +14 -1
  20. package/components/CodeMirror.vue +96 -5
  21. package/components/Collapse.vue +16 -3
  22. package/components/CruResource.vue +9 -0
  23. package/components/CruResourceFooter.vue +1 -1
  24. package/components/ExplorerMembers.vue +2 -1
  25. package/components/FixedBanner.vue +19 -12
  26. package/components/Import.vue +14 -1
  27. package/components/LandingPagePreference.vue +4 -2
  28. package/components/PodSecurityAdmission.vue +8 -6
  29. package/components/PromptChangePassword.vue +1 -0
  30. package/components/PromptRemove.vue +23 -21
  31. package/components/ResourceDetail/Masthead.vue +30 -11
  32. package/components/ResourceDetail/__tests__/Masthead.test.ts +61 -0
  33. package/components/ResourceDetail/index.vue +6 -0
  34. package/components/ResourceTable.vue +6 -1
  35. package/components/ResourceYaml.vue +1 -0
  36. package/components/Setting.vue +115 -0
  37. package/components/SortableTable/THead.vue +2 -0
  38. package/components/SortableTable/index.vue +7 -12
  39. package/components/StatusBadge.vue +71 -0
  40. package/components/Tabbed/index.vue +16 -15
  41. package/components/Wizard.vue +108 -104
  42. package/components/YamlEditor.vue +12 -2
  43. package/components/__tests__/Collapse.test.ts +2 -2
  44. package/components/__tests__/FixedBanner.test.ts +3 -3
  45. package/components/auth/Principal.vue +29 -17
  46. package/components/auth/__tests__/Principal.test.ts +40 -0
  47. package/components/auth/login/ldap.vue +7 -0
  48. package/components/fleet/FleetBundles.vue +1 -1
  49. package/components/fleet/FleetRepos.vue +1 -1
  50. package/components/fleet/FleetResources.vue +0 -2
  51. package/components/fleet/FleetSummary.vue +60 -65
  52. package/components/fleet/ForceDirectedTreeChart/index.vue +5 -1
  53. package/components/fleet/__tests__/FleetSummary.test.ts +49 -9
  54. package/components/form/ArrayList.vue +6 -2
  55. package/components/form/ColorInput.vue +1 -0
  56. package/components/form/KeyValue.vue +11 -12
  57. package/components/form/LabeledSelect.vue +15 -3
  58. package/components/form/Labels.vue +8 -1
  59. package/components/form/Members/MembershipEditor.vue +230 -222
  60. package/components/form/Members/__tests__/MembershipEditor.test.ts +62 -0
  61. package/components/form/Password.vue +3 -0
  62. package/components/form/ProjectMemberEditor.vue +6 -3
  63. package/components/form/ResourceTabs/index.vue +15 -13
  64. package/components/form/SSHKnownHosts/KnownHostsEditDialog.vue +5 -4
  65. package/components/form/SchedulingCustomization.vue +85 -0
  66. package/components/form/Select.vue +3 -2
  67. package/components/form/SelectOrCreateAuthSecret.vue +2 -1
  68. package/components/form/UnitInput.vue +3 -4
  69. package/components/form/__tests__/ArrayList.test.ts +9 -6
  70. package/components/form/__tests__/LabeledSelect.test.ts +37 -0
  71. package/components/form/__tests__/SelectOrCreateAuthSecret.test.ts +34 -0
  72. package/components/form/__tests__/UnitInput.test.ts +4 -5
  73. package/components/formatter/LiveDate.vue +3 -1
  74. package/components/formatter/ServiceType.vue +12 -4
  75. package/components/formatter/WorkloadHealthScale.vue +2 -1
  76. package/components/nav/Header.vue +35 -2
  77. package/components/nav/HeaderPageActionMenu.vue +11 -40
  78. package/components/nav/Jump.vue +8 -2
  79. package/components/nav/NamespaceFilter.vue +5 -4
  80. package/components/nav/Pinned.vue +1 -1
  81. package/components/nav/TopLevelMenu.helper.ts +5 -5
  82. package/components/nav/TopLevelMenu.vue +1 -12
  83. package/components/nav/WindowManager/ContainerLogs.vue +96 -58
  84. package/components/nav/WindowManager/ContainerShell.vue +99 -18
  85. package/components/nav/WindowManager/index.vue +74 -6
  86. package/components/nav/__tests__/TopLevelMenu.test.ts +0 -40
  87. package/components/templates/default.vue +2 -47
  88. package/config/features.js +1 -0
  89. package/config/labels-annotations.js +11 -1
  90. package/config/router/navigation-guards/index.js +2 -1
  91. package/config/router/navigation-guards/record-last-route.js +24 -0
  92. package/config/settings.ts +66 -98
  93. package/config/version.js +1 -1
  94. package/core/types-provisioning.ts +7 -0
  95. package/detail/fleet.cattle.io.bundle.vue +7 -0
  96. package/detail/fleet.cattle.io.cluster.vue +0 -3
  97. package/detail/fleet.cattle.io.gitrepo.vue +8 -15
  98. package/detail/provisioning.cattle.io.cluster.vue +8 -2
  99. package/dialog/DeactivateDriverDialog.vue +5 -5
  100. package/dialog/GitRepoForceUpdateDialog.vue +132 -0
  101. package/directives/strip-html-aria-label.js +19 -0
  102. package/edit/__tests__/cis.cattle.io.clusterscan.test.ts +87 -0
  103. package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +217 -37
  104. package/edit/auth/__tests__/oidc.test.ts +60 -12
  105. package/edit/auth/ldap/__tests__/config.test.ts +40 -0
  106. package/edit/auth/ldap/config.vue +67 -89
  107. package/edit/auth/oidc.vue +16 -2
  108. package/edit/catalog.cattle.io.clusterrepo.vue +12 -8
  109. package/edit/cis.cattle.io.clusterscan.vue +13 -1
  110. package/edit/fleet.cattle.io.gitrepo.vue +198 -72
  111. package/edit/logging-flow/Match.vue +0 -21
  112. package/edit/management.cattle.io.project.vue +1 -1
  113. package/edit/monitoring.coreos.com.prometheusrule/AlertingRule.vue +10 -3
  114. package/edit/monitoring.coreos.com.prometheusrule/RecordingRule.vue +5 -1
  115. package/edit/monitoring.coreos.com.prometheusrule/index.vue +5 -2
  116. package/edit/provisioning.cattle.io.cluster/CustomCommand.vue +8 -1
  117. package/edit/provisioning.cattle.io.cluster/SelectCredential.vue +2 -0
  118. package/edit/provisioning.cattle.io.cluster/__tests__/Advanced.test.ts +0 -2
  119. package/edit/provisioning.cattle.io.cluster/__tests__/CustomCommand.test.ts +55 -15
  120. package/edit/provisioning.cattle.io.cluster/index.vue +28 -30
  121. package/edit/provisioning.cattle.io.cluster/rke2.vue +64 -13
  122. package/edit/provisioning.cattle.io.cluster/tabs/AgentConfiguration.vue +37 -2
  123. package/edit/provisioning.cattle.io.cluster/tabs/etcd/index.vue +3 -2
  124. package/edit/resources.cattle.io.backup.vue +150 -15
  125. package/edit/secret/__tests__/ssh.test.ts +79 -0
  126. package/edit/secret/ssh.vue +7 -1
  127. package/edit/service.vue +0 -3
  128. package/edit/workload/Job.vue +8 -8
  129. package/edit/workload/__tests__/Job.test.ts +0 -1
  130. package/edit/workload/index.vue +3 -1
  131. package/initialize/install-directives.js +2 -0
  132. package/initialize/install-plugins.js +6 -1
  133. package/list/catalog.cattle.io.app.vue +21 -4
  134. package/list/fleet.cattle.io.bundle.vue +1 -1
  135. package/list/management.cattle.io.setting.vue +34 -132
  136. package/list/provisioning.cattle.io.cluster.vue +11 -3
  137. package/machine-config/vmwarevsphere.vue +15 -8
  138. package/mixins/__tests__/auth-config.test.ts +74 -0
  139. package/mixins/__tests__/chart.test.ts +5 -4
  140. package/mixins/__tests__/create-edit-view.test.ts +38 -0
  141. package/mixins/auth-config.js +8 -0
  142. package/mixins/chart.js +2 -2
  143. package/mixins/create-edit-view/impl.js +4 -1
  144. package/mixins/vue-select-overrides.js +10 -0
  145. package/models/__tests__/catalog.cattle.io.app.test.ts +148 -0
  146. package/models/__tests__/fleet.cattle.io.gitrepo.test.ts +157 -0
  147. package/models/__tests__/secret.test.ts +56 -13
  148. package/models/catalog.cattle.io.app.js +112 -37
  149. package/models/cluster.js +11 -0
  150. package/models/fleet.cattle.io.bundle.js +40 -2
  151. package/models/fleet.cattle.io.gitrepo.js +169 -109
  152. package/models/management.cattle.io.fleetworkspace.js +4 -0
  153. package/models/management.cattle.io.kontainerdriver.js +7 -0
  154. package/models/nodedriver.js +4 -1
  155. package/models/provisioning.cattle.io.cluster.js +24 -0
  156. package/models/secret.js +1 -1
  157. package/package.json +5 -5
  158. package/pages/auth/login.vue +5 -11
  159. package/pages/auth/verify.vue +11 -1
  160. package/pages/c/_cluster/apps/charts/index.vue +6 -4
  161. package/pages/c/_cluster/apps/charts/install.vue +1 -1
  162. package/pages/c/_cluster/explorer/ConfigBadge.vue +3 -5
  163. package/pages/c/_cluster/explorer/EventsTable.vue +3 -2
  164. package/pages/c/_cluster/explorer/__tests__/index.test.ts +9 -9
  165. package/pages/c/_cluster/explorer/index.vue +33 -35
  166. package/pages/c/_cluster/explorer/tools/index.vue +3 -3
  167. package/pages/c/_cluster/fleet/index.vue +0 -5
  168. package/pages/c/_cluster/legacy/project/index.vue +1 -1
  169. package/pages/c/_cluster/settings/performance.vue +52 -53
  170. package/pages/c/_cluster/uiplugins/index.vue +19 -22
  171. package/pages/home.vue +17 -12
  172. package/pages/prefs.vue +5 -1
  173. package/plugins/shortkey.js +10 -1
  174. package/plugins/steve/steve-pagination-utils.ts +58 -8
  175. package/promptRemove/management.cattle.io.fleetworkspace.vue +98 -0
  176. package/promptRemove/management.cattle.io.globalrole.vue +1 -1
  177. package/promptRemove/management.cattle.io.project.vue +2 -8
  178. package/promptRemove/management.cattle.io.roletemplate.vue +1 -1
  179. package/promptRemove/mixin/roleDeletionCheck.js +1 -7
  180. package/promptRemove/pod.vue +7 -28
  181. package/rancher-components/Card/Card.vue +9 -1
  182. package/rancher-components/Form/Checkbox/Checkbox.vue +42 -6
  183. package/rancher-components/Form/LabeledInput/LabeledInput.vue +30 -3
  184. package/rancher-components/Form/Radio/RadioButton.vue +18 -3
  185. package/rancher-components/Form/Radio/RadioGroup.vue +39 -5
  186. package/rancher-components/LabeledTooltip/LabeledTooltip.vue +13 -1
  187. package/rancher-components/RcButton/RcButton.test.ts +97 -0
  188. package/rancher-components/RcButton/RcButton.vue +14 -9
  189. package/rancher-components/RcDropdown/RcDropdown.vue +3 -1
  190. package/rancher-components/RcDropdown/RcDropdownItem.vue +8 -2
  191. package/rancher-components/RcDropdown/RcDropdownMenu.vue +66 -0
  192. package/rancher-components/RcDropdown/index.ts +1 -0
  193. package/rancher-components/RcDropdown/types.ts +27 -0
  194. package/rancher-components/RcDropdown/useDropdownContext.ts +5 -2
  195. package/scripts/extension/helm/charts/ui-plugin-server/templates/_helpers.tpl +2 -2
  196. package/scripts/typegen.sh +1 -0
  197. package/store/__tests__/auth.test.ts +120 -0
  198. package/store/action-menu.js +13 -3
  199. package/store/auth.js +14 -9
  200. package/store/aws.js +9 -2
  201. package/store/catalog.js +14 -7
  202. package/store/features.js +1 -0
  203. package/store/prefs.js +9 -28
  204. package/store/type-map.utils.ts +4 -0
  205. package/types/resources/settings.d.ts +27 -20
  206. package/types/shell/index.d.ts +18 -12
  207. package/utils/__tests__/array.test.ts +13 -1
  208. package/utils/__tests__/string.test.ts +80 -1
  209. package/utils/array.ts +13 -0
  210. package/utils/auth.js +4 -0
  211. package/utils/banners.js +0 -45
  212. package/utils/cluster.js +1 -1
  213. package/{edit/monitoring.coreos.com.prometheusrule → utils}/duration.js +5 -3
  214. package/utils/object.js +0 -3
  215. package/utils/pagination-utils.ts +15 -2
  216. package/utils/string.js +31 -7
  217. package/utils/validators/formRules/__tests__/index.test.ts +27 -0
  218. package/utils/validators/formRules/index.ts +16 -0
  219. package/edit/provisioning.cattle.io.cluster/import.vue +0 -198
@@ -1,13 +1,16 @@
1
1
  <script>
2
- import { mapGetters } from 'vuex';
3
2
  import { MANAGEMENT } from '@shell/config/types';
4
- import { ALLOWED_SETTINGS } from '@shell/config/settings';
3
+ import { ALLOWED_SETTINGS, PROVISIONING_SETTINGS } from '@shell/config/settings';
5
4
  import { Banner } from '@components/Banner';
6
5
  import Loading from '@shell/components/Loading';
7
6
  import { VIEW_IN_API } from '@shell/store/prefs';
7
+ import Setting from '@shell/components/Setting';
8
+ import { mapGetters } from 'vuex';
8
9
 
9
10
  export default {
10
- components: { Banner, Loading },
11
+ components: {
12
+ Banner, Loading, Setting
13
+ },
11
14
 
12
15
  async fetch() {
13
16
  const viewInApi = this.$store.getters['prefs/get'](VIEW_IN_API);
@@ -21,6 +24,7 @@ export default {
21
24
  }, {});
22
25
 
23
26
  const settings = [];
27
+ const provisioningSettings = [];
24
28
 
25
29
  // Combine the allowed settings with the data from the API
26
30
  for ( const id in ALLOWED_SETTINGS ) {
@@ -52,43 +56,22 @@ export default {
52
56
  // There are only 2 actions that can be enabled - Edit Setting or View in API
53
57
  // If neither is available for this setting then we hide the action menu button
54
58
  s.hasActions = (!s.readOnly || viewInApi) && setting.availableActions?.length;
55
- settings.push(s);
59
+
60
+ if (PROVISIONING_SETTINGS.includes(s.id) ) {
61
+ provisioningSettings.push(s);
62
+ } else {
63
+ settings.push(s);
64
+ }
56
65
  }
57
66
 
58
67
  this.settings = settings;
68
+ this.provisioningSettings = provisioningSettings;
59
69
  },
60
70
 
61
71
  data() {
62
- return { settings: null };
63
- },
64
-
65
- computed: {
66
- ...mapGetters({ t: 'i18n/t' }),
67
- ...mapGetters({
68
- // Use either these Vuex getters
69
- // OR the props to set the action menu state,
70
- // but don't use both.
71
- targetElem: 'action-menu/elem',
72
- shouldShow: 'action-menu/showing',
73
- }),
72
+ return { settings: null, provisioningSettings: null };
74
73
  },
75
-
76
- methods: {
77
- toggleActionMenu(e, setting) {
78
- const actionElement = e.srcElement;
79
-
80
- if (!this.targetElem && !this.shouldShow) {
81
- this.$store.commit(`action-menu/show`, {
82
- resources: setting.data,
83
- elem: actionElement
84
- });
85
- } else if (this.targetElem === actionElement && this.shouldShow) {
86
- // this condition is needed so that we can "toggle" the action menu with
87
- // the keyboard for accessibility (row action menu)
88
- this.$store.commit('action-menu/hide');
89
- }
90
- },
91
- }
74
+ computed: { ...mapGetters({ t: 'i18n/t' }) }
92
75
  };
93
76
  </script>
94
77
 
@@ -98,77 +81,31 @@ export default {
98
81
  <Banner
99
82
  color="warning"
100
83
  class="settings-banner"
84
+ data-testid="global-settings-banner"
101
85
  >
102
86
  <div>
103
87
  {{ t('advancedSettings.subtext') }}
104
88
  </div>
105
89
  </Banner>
106
90
  <div
107
- v-for="(setting, i) in settings"
108
- :key="i"
109
- class="advanced-setting mb-20"
110
- :data-testid="`advanced-setting__option-${setting.id}`"
91
+ v-for="(setting) in settings"
92
+ :key="setting.id"
111
93
  >
112
- <div class="header">
113
- <div class="title">
114
- <h1>
115
- {{ setting.id }}
116
- <span
117
- v-if="setting.fromEnv"
118
- class="modified"
119
- >{{ t('advancedSettings.setEnv') }}</span>
120
- <span
121
- v-else-if="setting.customized"
122
- class="modified"
123
- >{{ t('advancedSettings.modified') }}</span>
124
- </h1>
125
- <h2>{{ t(`advancedSettings.descriptions.${setting.id}`) }}</h2>
126
- </div>
127
- <div
128
- v-if="setting.hasActions"
129
- class="action"
130
- >
131
- <button
132
- aria-haspopup="true"
133
- aria-expanded="false"
134
- type="button"
135
- class="btn btn-sm role-multi-action actions"
136
- role="button"
137
- :aria-label="t('advancedSettings.edit.moreActions', { setting: setting.id })"
138
- @click="toggleActionMenu($event, setting)"
139
- >
140
- <i
141
- class="icon icon-actions"
142
- :alt="t('advancedSettings.edit.moreActions', { setting: setting.id })"
143
- />
144
- </button>
145
- </div>
146
- </div>
147
- <div value>
148
- <div v-if="setting.canHide">
149
- <button
150
- class="btn btn-sm role-primary"
151
- role="button"
152
- :aria-label="t('advancedSettings.hideShow')"
153
- @click="setting.hide = !setting.hide"
154
- >
155
- {{ setting.hide ? t('advancedSettings.show') : t('advancedSettings.hide') }} {{ setting.id }}
156
- </button>
157
- </div>
158
- <div
159
- v-show="!setting.canHide || (setting.canHide && !setting.hide)"
160
- class="settings-value"
161
- >
162
- <pre v-if="setting.kind === 'json'">{{ setting.json }}</pre>
163
- <pre v-else-if="setting.kind === 'multiline'">{{ setting.data.value || setting.data.default }}</pre>
164
- <pre v-else-if="setting.kind === 'enum'">{{ t(setting.enum) }}</pre>
165
- <pre v-else-if="setting.data.value || setting.data.default">{{ setting.data.value || setting.data.default }}</pre>
166
- <pre
167
- v-else
168
- class="text-muted"
169
- >&lt;{{ t('advancedSettings.none') }}&gt;</pre>
170
- </div>
171
- </div>
94
+ <Setting
95
+ :value="setting"
96
+ />
97
+ </div>
98
+
99
+ <h2>
100
+ {{ t('advancedSettings.provisioning.header') }}
101
+ </h2>
102
+ <div
103
+ v-for="(setting) in provisioningSettings"
104
+ :key="setting.id"
105
+ >
106
+ <Setting
107
+ :value="setting"
108
+ />
172
109
  </div>
173
110
  </div>
174
111
  </template>
@@ -177,39 +114,4 @@ export default {
177
114
  .settings-banner {
178
115
  margin-top: 0;
179
116
  }
180
- .advanced-setting {
181
- border: 1px solid var(--border);
182
- padding: 20px;
183
- border-radius: var(--border-radius);
184
-
185
- h1 {
186
- font-size: 14px;
187
- }
188
- h2 {
189
- font-size: 12px;
190
- margin-bottom: 0;
191
- opacity: 0.8;
192
- }
193
- }
194
-
195
- .settings-value pre {
196
- margin: 0;
197
- }
198
-
199
- .header {
200
- display: flex;
201
- margin-bottom: 20px;
202
- }
203
-
204
- .title {
205
- flex: 1;
206
- }
207
-
208
- .modified {
209
- margin-left: 10px;
210
- border: 1px solid var(--primary);
211
- border-radius: 5px;
212
- padding: 2px 10px;
213
- font-size: 12px;
214
- }
215
117
  </style>
@@ -77,13 +77,15 @@ export default {
77
77
  const res = await allHash(hash);
78
78
 
79
79
  this.mgmtClusters = res.mgmtClusters;
80
+ this.showRke1DeprecationWarning = this.rows.some((r) => r.isRke1);
80
81
  },
81
82
 
82
83
  data() {
83
84
  return {
84
- resource: CAPI.RANCHER_CLUSTER,
85
- schema: this.$store.getters['management/schemaFor'](CAPI.RANCHER_CLUSTER),
86
- mgmtClusters: [],
85
+ resource: CAPI.RANCHER_CLUSTER,
86
+ schema: this.$store.getters['management/schemaFor'](CAPI.RANCHER_CLUSTER),
87
+ mgmtClusters: [],
88
+ showRke1DeprecationWarning: false
87
89
  };
88
90
  },
89
91
 
@@ -180,6 +182,12 @@ export default {
180
182
 
181
183
  <template>
182
184
  <div>
185
+ <Banner
186
+ v-if="showRke1DeprecationWarning"
187
+ color="warning"
188
+ label-key="cluster.banner.rke1DeprecationMessage"
189
+ />
190
+
183
191
  <Banner
184
192
  v-if="hiddenHarvesterCount"
185
193
  color="info"
@@ -149,7 +149,6 @@ export default {
149
149
  const datacenterAlreadySet = !!this.value.datacenter;
150
150
 
151
151
  await this.loadDataCenters();
152
-
153
152
  if (datacenterAlreadySet) {
154
153
  this.loadAllDatacenterResources();
155
154
  }
@@ -382,12 +381,14 @@ export default {
382
381
 
383
382
  set(this.value, 'boot2dockerUrl', boot2dockerUrl);
384
383
  },
385
- vappMode(value) {
384
+ vappMode(value, oldValue) {
386
385
  if (value === VAPP_MODE.AUTO) {
387
386
  const defaultVappOptions = getDefaultVappOptions(this.networkNames || []);
388
387
 
389
388
  return this.updateVappOptions(defaultVappOptions);
390
- } else {
389
+ } else if (value === VAPP_MODE.DISABLED) {
390
+ this.updateVappOptions(INITIAL_VAPP_OPTIONS);
391
+ } else if (value === VAPP_MODE.MANUAL && oldValue === VAPP_MODE.AUTO) {
391
392
  this.updateVappOptions(INITIAL_VAPP_OPTIONS);
392
393
  }
393
394
  },
@@ -417,12 +418,18 @@ export default {
417
418
 
418
419
  const url = `/meta/vsphere/${ resource }?${ queryParams }`;
419
420
 
420
- const result = await this.$store.dispatch('management/request', {
421
- url,
422
- redirectUnauthorized: false,
423
- }, { root: true });
421
+ try {
422
+ const result = await this.$store.dispatch('management/request', {
423
+ url,
424
+ redirectUnauthorized: false,
425
+ }, { root: true });
424
426
 
425
- return result.data;
427
+ return result.data;
428
+ } catch (ex) {
429
+ console.warn(`There was a problem requesting the resource: "${ resource }"" for datacenter: "${ dataCenter }"" and library: "${ library }"`, ex); // eslint-disable-line no-console
430
+
431
+ return [];
432
+ }
426
433
  },
427
434
 
428
435
  async loadDataCenters() {
@@ -0,0 +1,74 @@
1
+ import { mount } from '@vue/test-utils';
2
+ import authConfigMixin from '@shell/mixins/auth-config';
3
+
4
+ describe('mixin: authConfigMixin', () => {
5
+ describe('method: save', () => {
6
+ const componentMock = (model: any) => ({
7
+ data: () => ({
8
+ value: { configType: 'oidc' },
9
+ model,
10
+ }),
11
+ computed: { principal: () => ({ me: {} }) },
12
+ global: {
13
+ mocks: {
14
+ $store: {
15
+ dispatch: () => model,
16
+ commit: () => ({ 'auth/loggedInAs': jest.fn() }),
17
+ },
18
+ $route: {
19
+ params: { id: '123' },
20
+ query: { mode: 'edit' },
21
+ },
22
+ }
23
+ }
24
+ });
25
+ const FakeComponent = {
26
+ render() {},
27
+ mixins: [authConfigMixin],
28
+ methods: { applyHooks: jest.fn() },
29
+ };
30
+
31
+ it('should return error', async() => {
32
+ const instance = mount(FakeComponent, componentMock({
33
+ doAction: jest.fn(),
34
+ save: 'make it fail'
35
+ })).vm as any;
36
+ const fakeCallback = jest.fn();
37
+
38
+ await instance.save(fakeCallback);
39
+
40
+ expect(fakeCallback).toHaveBeenCalledWith(false);
41
+ });
42
+
43
+ it('should not return error', async() => {
44
+ const model = {
45
+ authConfigName: 'whatever',
46
+ doAction: jest.fn(),
47
+ save: async() => {}
48
+ };
49
+ const instance = mount(FakeComponent, componentMock(model)).vm as any;
50
+ const fakeCallback = jest.fn();
51
+
52
+ await instance.save(fakeCallback);
53
+
54
+ expect(fakeCallback).toHaveBeenCalledWith(true);
55
+ });
56
+
57
+ it.each([
58
+ 'oidc'
59
+ ])('should keep custom scope on save', async(type) => {
60
+ const scope = 'openid profile email groups whatever';
61
+ const model = {
62
+ scope,
63
+ authConfigName: 'whatever',
64
+ doAction: jest.fn(),
65
+ save: async() => {}
66
+ };
67
+ const instance = mount(FakeComponent, componentMock(model)).vm as any;
68
+
69
+ await instance.save(jest.fn());
70
+
71
+ expect(instance.model.scope).toStrictEqual(scope);
72
+ });
73
+ });
74
+ });
@@ -1,6 +1,7 @@
1
1
  import ChartMixin from '@shell/mixins/chart';
2
2
  import { OPA_GATE_KEEPER_ID } from '@shell/pages/c/_cluster/gatekeeper/index.vue';
3
3
  import { mount } from '@vue/test-utils';
4
+ import { APP_UPGRADE_STATUS } from '@shell/store/catalog';
4
5
 
5
6
  describe('chartMixin', () => {
6
7
  const testCases = {
@@ -10,10 +11,10 @@ describe('chartMixin', () => {
10
11
  ['any_other_id', 0]
11
12
  ],
12
13
  managedApps: [
13
- [false, false, 0],
14
- [true, null, 0],
15
- [true, true, 0],
16
- [true, false, 1],
14
+ [false, APP_UPGRADE_STATUS.NOT_APPLICABLE, 0],
15
+ [true, APP_UPGRADE_STATUS.NO_UPGRADE, 0],
16
+ [true, 'some-version', 0],
17
+ [true, APP_UPGRADE_STATUS.NOT_APPLICABLE, 1],
17
18
  ],
18
19
  };
19
20
 
@@ -15,4 +15,42 @@ describe('createEditView should', () => {
15
15
 
16
16
  expect(instance.mode).toContain(_EDIT);
17
17
  });
18
+
19
+ it.each([
20
+ ['_status', { _status: 409 }],
21
+ ['status', { status: 409 }],
22
+ ])('catch conflict error by %p field and retry save()', async(_, error) => {
23
+ const Component = {
24
+ render() {},
25
+ mixins: [CreateEditView],
26
+ };
27
+
28
+ const wrapper = mount(Component, {
29
+ props: { value: { id: '123', type: '' } },
30
+ global: {
31
+ mocks: {
32
+ $store: {
33
+ getters: {
34
+ currentStore: () => 'current_store',
35
+ 'type-map/isSpoofed': jest.fn().mockImplementation(() => false),
36
+ }
37
+ },
38
+ },
39
+ },
40
+ });
41
+
42
+ const instance = (wrapper as any).vm;
43
+
44
+ instance.actuallySave = async() => {
45
+ throw error;
46
+ };
47
+ instance.conflict = async() => '';
48
+ instance.done = async() => '';
49
+
50
+ const spyConflict = jest.spyOn(wrapper.vm, 'conflict');
51
+
52
+ await instance.save(() => '', 'url');
53
+
54
+ expect(spyConflict).toHaveBeenCalledTimes(1);
55
+ });
18
56
  });
@@ -137,6 +137,14 @@ export default {
137
137
  }
138
138
  },
139
139
 
140
+ /**
141
+ * On save several operations are executed to return a URL or open pop-up:
142
+ * - Retrieve data from the UI
143
+ * - "Test" the configuration through action and override the model
144
+ * - Retrieve scopes from redirect URL
145
+ * - Set default scopes and merge them with the ones from the redirect URL and from the "test" action
146
+ * @param {*} btnCb
147
+ */
140
148
  async save(btnCb) {
141
149
  await this.applyHooks(BEFORE_SAVE_HOOKS);
142
150
 
package/mixins/chart.js CHANGED
@@ -14,7 +14,7 @@ import { formatSi, parseSi } from '@shell/utils/units';
14
14
  import { CAPI, CATALOG } from '@shell/config/types';
15
15
  import { isPrerelease } from '@shell/utils/version';
16
16
  import difference from 'lodash/difference';
17
- import { LINUX } from '@shell/store/catalog';
17
+ import { LINUX, APP_UPGRADE_STATUS } from '@shell/store/catalog';
18
18
  import { clone } from '@shell/utils/object';
19
19
  import { merge } from 'lodash';
20
20
 
@@ -181,7 +181,7 @@ export default {
181
181
  warnings.unshift(this.t('gatekeeperIndex.deprecated', {}, true));
182
182
  }
183
183
 
184
- if (this.existing && this.existing.upgradeAvailable === false) {
184
+ if (this.existing && this.existing.upgradeAvailable === APP_UPGRADE_STATUS.NOT_APPLICABLE) {
185
185
  const manager = this.existing?.spec?.chart?.metadata?.annotations?.[CATALOG_ANNOTATIONS.MANAGED] || 'Rancher';
186
186
 
187
187
  warnings.unshift(this.t('catalog.install.warning.managed', {
@@ -159,8 +159,11 @@ export default {
159
159
 
160
160
  this.done();
161
161
  } catch (err) {
162
+ // This exception handles errors from the `request` action when it receives a failed http request. The `err` object could be from the action's error handler (raw http response object containing `status`) or thrown later on given the response of the action (a massaged object containing `_status`). TBD why one 409 triggers the error handler and another does not.
163
+ const IS_ERR_409 = err.status === 409 || err._status === 409;
164
+
162
165
  // Conflict, the resource being edited has changed since starting editing
163
- if ( err.status === 409 && depth === 0 && this.isEdit) {
166
+ if (IS_ERR_409 && depth === 0 && this.isEdit) {
164
167
  const errors = await this.conflict();
165
168
 
166
169
  if ( errors === false ) {
@@ -20,6 +20,9 @@ export default {
20
20
 
21
21
  // escape
22
22
  (out[27] = (e) => {
23
+ e.preventDefault();
24
+ e.stopPropagation();
25
+
23
26
  vm.open = false;
24
27
  vm.search = '';
25
28
 
@@ -36,6 +39,13 @@ export default {
36
39
  return;
37
40
  }
38
41
 
42
+ // if the index of the option is -1
43
+ // it means are pressing enter on an invalid option
44
+ // we should exit
45
+ if (vm.typeAheadPointer === -1) {
46
+ return;
47
+ }
48
+
39
49
  let option = vm.filteredOptions[vm.typeAheadPointer];
40
50
 
41
51
  vm.$emit('option:selecting', option);
@@ -0,0 +1,148 @@
1
+ import CatalogApp from '@shell/models/catalog.cattle.io.app';
2
+ import { APP_UPGRADE_STATUS } from '@shell/store/catalog';
3
+ import { CATALOG as CATALOG_ANNOTATIONS } from '@shell/config/labels-annotations';
4
+
5
+ const latestVersion = '1.16.2';
6
+ const secondLatestVersion = '1.16.1';
7
+ const chartName = 'cert-manager';
8
+
9
+ const appCo = {
10
+ repoName: 'appCo',
11
+ home: 'https://apps.rancher.io/applications/cert-manager'
12
+ };
13
+
14
+ const certManagerOfficial = {
15
+ repoName: 'certManagerOfficial',
16
+ home: 'https://cert-manager.io',
17
+ oldHome: 'https://github.com/jetstack/cert-manager' // older versions of cert-manager used to have this home url(e.g. 1.7.1)
18
+ };
19
+
20
+ // cert-manager chart from application collection OCI repo
21
+ const appCoMatchingChart1 = {
22
+ name: chartName,
23
+ repoName: appCo.repoName,
24
+ versions: [{
25
+ version: latestVersion,
26
+ home: appCo.home,
27
+ repoName: appCo.repoName,
28
+ annotations: {}
29
+ },
30
+ {
31
+ version: secondLatestVersion,
32
+ home: appCo.home,
33
+ repoName: appCo.repoName,
34
+ annotations: {}
35
+ }]
36
+ };
37
+
38
+ const appCoMatchingChart2 = {
39
+ name: chartName,
40
+ repoName: appCo.repoName,
41
+ versions: [{
42
+ version: latestVersion,
43
+ home: appCo.home,
44
+ repoName: appCo.repoName,
45
+ annotations: {}
46
+ },
47
+ {
48
+ version: secondLatestVersion,
49
+ home: appCo.home,
50
+ repoName: appCo.repoName,
51
+ annotations: {}
52
+ }]
53
+ };
54
+
55
+ // cert-manager chart from its official helm repo 'https://cert-manager.io' added to Rancher UI repositories
56
+ const certManagerOfficialMatchingChart1 = {
57
+ name: chartName,
58
+ repoName: certManagerOfficial.repoName,
59
+ versions: [{
60
+ version: latestVersion,
61
+ home: certManagerOfficial.home,
62
+ repoName: certManagerOfficial.repoName,
63
+ annotations: {},
64
+ },
65
+ {
66
+ version: secondLatestVersion,
67
+ home: certManagerOfficial.oldHome,
68
+ repoName: certManagerOfficial.repoName,
69
+ annotations: {},
70
+ }]
71
+ };
72
+
73
+ const certManagerOfficialMatchingChart2 = {
74
+ name: chartName,
75
+ repoName: certManagerOfficial.repoName,
76
+ versions: [{
77
+ version: latestVersion,
78
+ home: certManagerOfficial.home,
79
+ repoName: certManagerOfficial.repoName,
80
+ annotations: {},
81
+ },
82
+ {
83
+ version: secondLatestVersion,
84
+ home: certManagerOfficial.oldHome,
85
+ repoName: certManagerOfficial.repoName,
86
+ annotations: {},
87
+ }]
88
+ };
89
+
90
+ const installedCertManagerAppCoFromRancherUI = {
91
+ metadata: {
92
+ annotations: { [CATALOG_ANNOTATIONS.SOURCE_REPO_NAME]: appCo.repoName },
93
+ name: chartName,
94
+ home: appCo.home,
95
+ version: secondLatestVersion
96
+ }
97
+ };
98
+
99
+ const installedCertManagerOfficialFromCli = {
100
+ metadata: {
101
+ name: chartName,
102
+ home: certManagerOfficial.oldHome,
103
+ version: secondLatestVersion
104
+ }
105
+ };
106
+
107
+ const installedCertManagerOfficialFromRancherUI = {
108
+ metadata: {
109
+ annotations: { [CATALOG_ANNOTATIONS.SOURCE_REPO_NAME]: certManagerOfficial.repoName },
110
+ name: chartName,
111
+ home: certManagerOfficial.oldHome,
112
+ version: secondLatestVersion
113
+ }
114
+ };
115
+
116
+ describe('class CatalogApp', () => {
117
+ describe('upgradeAvailable', () => {
118
+ const testCases = [
119
+ // when you follow Rancher Installation docs to install cert-manager through CLI
120
+ [installedCertManagerOfficialFromCli, [], APP_UPGRADE_STATUS.NO_UPGRADE],
121
+ [installedCertManagerOfficialFromCli, [appCoMatchingChart1], APP_UPGRADE_STATUS.NO_UPGRADE],
122
+ [installedCertManagerOfficialFromCli, [appCoMatchingChart1, appCoMatchingChart2], APP_UPGRADE_STATUS.NO_UPGRADE],
123
+ [installedCertManagerOfficialFromCli, [appCoMatchingChart1, appCoMatchingChart2, certManagerOfficialMatchingChart1], APP_UPGRADE_STATUS.SINGLE_UPGRADE],
124
+ // when you add application collection OCI repo through UI
125
+ [installedCertManagerAppCoFromRancherUI, [], APP_UPGRADE_STATUS.NO_UPGRADE],
126
+ [installedCertManagerAppCoFromRancherUI, [appCoMatchingChart1], APP_UPGRADE_STATUS.SINGLE_UPGRADE],
127
+ [installedCertManagerAppCoFromRancherUI, [appCoMatchingChart1, certManagerOfficialMatchingChart1], APP_UPGRADE_STATUS.SINGLE_UPGRADE],
128
+ [installedCertManagerAppCoFromRancherUI, [appCoMatchingChart1, appCoMatchingChart2], APP_UPGRADE_STATUS.MULTIPLE_UPGRADES],
129
+ // when you add cert-manager official helm repo through UI
130
+ [installedCertManagerOfficialFromRancherUI, [], APP_UPGRADE_STATUS.NO_UPGRADE],
131
+ [installedCertManagerOfficialFromRancherUI, [certManagerOfficialMatchingChart1], APP_UPGRADE_STATUS.SINGLE_UPGRADE],
132
+ [installedCertManagerOfficialFromRancherUI, [certManagerOfficialMatchingChart1, appCoMatchingChart1], APP_UPGRADE_STATUS.SINGLE_UPGRADE],
133
+ [installedCertManagerOfficialFromRancherUI, [certManagerOfficialMatchingChart1, certManagerOfficialMatchingChart2], APP_UPGRADE_STATUS.MULTIPLE_UPGRADES]
134
+ ];
135
+
136
+ it.each(testCases)('should return the correct upgrade status', (installedChart: Object, matchingCharts: any, expected: any) => {
137
+ const catalogApp = new CatalogApp({ spec: { chart: installedChart } }, {
138
+ rootGetters: {
139
+ 'catalog/chart': () => matchingCharts,
140
+ currentCluster: { workerOSs: ['linux'] },
141
+ 'prefs/get': () => false
142
+ }
143
+ });
144
+
145
+ expect(catalogApp.upgradeAvailable).toBe(expected);
146
+ });
147
+ });
148
+ });