@rancher/shell 3.0.2-rc.2 → 3.0.2-rc.4

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 (172) hide show
  1. package/assets/styles/base/_basic.scss +7 -8
  2. package/assets/styles/global/_button.scss +10 -0
  3. package/assets/styles/global/_form.scss +2 -1
  4. package/assets/styles/global/_tooltip.scss +2 -2
  5. package/assets/styles/themes/_dark.scss +15 -3
  6. package/assets/styles/themes/_light.scss +7 -2
  7. package/assets/styles/vendor/vue-select.scss +4 -0
  8. package/assets/translations/en-us.yaml +66 -9
  9. package/assets/translations/zh-hans.yaml +2 -3
  10. package/components/AppModal.vue +50 -0
  11. package/components/BannerGraphic.vue +0 -42
  12. package/components/ButtonMultiAction.vue +1 -1
  13. package/components/Carousel.vue +88 -74
  14. package/components/CommunityLinks.vue +6 -1
  15. package/components/CopyToClipboardText.vue +3 -0
  16. package/components/Dialog.vue +20 -1
  17. package/components/GrowlManager.vue +9 -2
  18. package/components/LocaleSelector.vue +8 -1
  19. package/components/PaginatedResourceTable.vue +4 -7
  20. package/components/ProgressBarMulti.vue +14 -0
  21. package/components/PromptChangePassword.vue +3 -0
  22. package/components/Questions/Reference.vue +57 -28
  23. package/components/ResourceDetail/Masthead.vue +1 -1
  24. package/components/SelectIconGrid.vue +12 -1
  25. package/components/SideNav.vue +12 -38
  26. package/components/SortableTable/index.vue +1 -0
  27. package/components/Tabbed/index.vue +9 -1
  28. package/components/YamlEditor.vue +1 -0
  29. package/components/__tests__/Carousel.test.ts +56 -27
  30. package/components/auth/Principal.vue +5 -3
  31. package/components/fleet/FleetClusters.vue +82 -1
  32. package/components/fleet/FleetRepos.vue +13 -30
  33. package/components/fleet/ForceDirectedTreeChart/index.vue +2 -2
  34. package/components/form/ChangePassword.vue +2 -0
  35. package/components/form/ColorInput.vue +24 -1
  36. package/components/form/FileSelector.vue +2 -0
  37. package/components/form/KeyValue.vue +230 -160
  38. package/components/form/LabeledSelect.vue +2 -2
  39. package/components/form/PlusMinus.vue +14 -2
  40. package/components/form/ResourceLabeledSelect.vue +13 -53
  41. package/components/form/ResourceSelector.vue +1 -0
  42. package/components/form/ResourceTabs/index.vue +79 -36
  43. package/components/form/SSHKnownHosts/KnownHostsEditDialog.vue +192 -0
  44. package/components/form/SSHKnownHosts/__tests__/KnownHostsEditDialog.test.ts +104 -0
  45. package/components/form/SSHKnownHosts/index.vue +101 -0
  46. package/components/form/SecretSelector.vue +2 -2
  47. package/components/form/Select.vue +1 -1
  48. package/components/form/SelectOrCreateAuthSecret.vue +43 -11
  49. package/components/form/__tests__/KeyValue.test.ts +1 -1
  50. package/components/form/__tests__/SSHKnownHosts.test.ts +59 -0
  51. package/components/formatter/FleetClusterSummaryGraph.vue +2 -2
  52. package/components/formatter/FleetSummaryGraph.vue +6 -7
  53. package/components/formatter/WorkloadHealthScale.vue +7 -0
  54. package/components/nav/Group.vue +30 -4
  55. package/components/nav/Header.vue +82 -114
  56. package/components/nav/HeaderPageActionMenu.vue +27 -131
  57. package/components/nav/NamespaceFilter.vue +1 -1
  58. package/components/nav/Type.vue +15 -0
  59. package/composables/focusTrap.ts +68 -0
  60. package/config/home-links.js +21 -13
  61. package/config/labels-annotations.js +2 -0
  62. package/config/page-actions.js +1 -0
  63. package/config/pagination-table-headers.js +15 -1
  64. package/config/product/explorer.js +7 -17
  65. package/config/table-headers.js +6 -0
  66. package/config/version.js +5 -1
  67. package/core/plugin.ts +41 -1
  68. package/core/plugins.js +125 -72
  69. package/core/types-provisioning.ts +91 -2
  70. package/core/types.ts +55 -0
  71. package/detail/__tests__/autoscaling.horizontalpodautoscaler.test.ts +12 -3
  72. package/detail/catalog.cattle.io.app.vue +1 -1
  73. package/detail/fleet.cattle.io.cluster.vue +3 -3
  74. package/detail/namespace.vue +13 -19
  75. package/detail/networking.k8s.io.ingress.vue +13 -53
  76. package/detail/provisioning.cattle.io.cluster.vue +12 -1
  77. package/detail/secret.vue +25 -0
  78. package/detail/workload/index.vue +3 -3
  79. package/dialog/AddCustomBadgeDialog.vue +5 -1
  80. package/edit/auth/ldap/__tests__/config.test.ts +18 -0
  81. package/edit/auth/ldap/config.vue +24 -0
  82. package/edit/auth/saml.vue +8 -6
  83. package/edit/fleet.cattle.io.gitrepo.vue +34 -23
  84. package/edit/logging-flow/index.vue +4 -19
  85. package/edit/networking.k8s.io.ingress/index.vue +18 -65
  86. package/edit/networking.k8s.io.networkpolicy/index.vue +4 -5
  87. package/edit/provisioning.cattle.io.cluster/index.vue +27 -8
  88. package/edit/provisioning.cattle.io.cluster/rke2.vue +31 -115
  89. package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +2 -2
  90. package/edit/provisioning.cattle.io.cluster/tabs/networking/ACE.vue +14 -28
  91. package/edit/provisioning.cattle.io.cluster/tabs/networking/index.vue +25 -12
  92. package/edit/secret/index.vue +1 -1
  93. package/edit/secret/ssh.vue +21 -3
  94. package/edit/service.vue +1 -2
  95. package/list/networking.k8s.io.ingress.vue +1 -1
  96. package/list/node.vue +15 -8
  97. package/list/persistentvolume.vue +12 -4
  98. package/list/provisioning.cattle.io.cluster.vue +1 -0
  99. package/list/service.vue +1 -1
  100. package/list/workload.vue +4 -0
  101. package/mixins/chart.js +4 -1
  102. package/models/catalog.cattle.io.app.js +3 -1
  103. package/models/catalog.cattle.io.clusterrepo.js +56 -7
  104. package/models/fleet.cattle.io.bundle.js +0 -11
  105. package/models/fleet.cattle.io.cluster.js +17 -1
  106. package/models/fleet.cattle.io.gitrepo.js +88 -52
  107. package/models/provisioning.cattle.io.cluster.js +36 -1
  108. package/models/secret.js +5 -0
  109. package/models/service.js +1 -0
  110. package/models/workload.js +19 -1
  111. package/package.json +5 -4
  112. package/pages/account/index.vue +4 -0
  113. package/pages/c/_cluster/apps/charts/index.vue +4 -0
  114. package/pages/c/_cluster/explorer/ConfigBadge.vue +4 -2
  115. package/pages/c/_cluster/explorer/index.vue +13 -6
  116. package/pages/c/_cluster/fleet/GitRepoGraphConfig.js +3 -3
  117. package/pages/c/_cluster/fleet/index.vue +75 -89
  118. package/pages/c/_cluster/settings/links.vue +2 -2
  119. package/pages/c/_cluster/uiplugins/AddExtensionRepos.vue +3 -1
  120. package/pages/c/_cluster/uiplugins/CatalogList/CatalogLoadDialog.vue +3 -0
  121. package/pages/c/_cluster/uiplugins/CatalogList/CatalogUninstallDialog.vue +7 -1
  122. package/pages/c/_cluster/uiplugins/CatalogList/index.vue +3 -1
  123. package/pages/c/_cluster/uiplugins/DeveloperInstallDialog.vue +10 -7
  124. package/pages/c/_cluster/uiplugins/InstallDialog.vue +7 -0
  125. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +181 -106
  126. package/pages/c/_cluster/uiplugins/SetupUIPlugins.vue +2 -0
  127. package/pages/c/_cluster/uiplugins/UninstallDialog.vue +9 -1
  128. package/pages/c/_cluster/uiplugins/index.vue +50 -12
  129. package/pages/diagnostic.vue +17 -15
  130. package/pages/home.vue +32 -6
  131. package/plugins/clean-html.js +50 -0
  132. package/plugins/dashboard-store/resource-class.js +4 -0
  133. package/plugins/plugin.js +54 -49
  134. package/plugins/steve/mutations.js +1 -1
  135. package/plugins/steve/steve-class.js +8 -0
  136. package/plugins/steve/steve-pagination-utils.ts +3 -1
  137. package/rancher-components/Accordion/Accordion.vue +4 -4
  138. package/rancher-components/BadgeState/BadgeState.vue +7 -0
  139. package/rancher-components/Card/Card.vue +12 -0
  140. package/rancher-components/Form/Checkbox/Checkbox.vue +9 -2
  141. package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +18 -1
  142. package/rancher-components/Form/LabeledInput/LabeledInput.vue +19 -1
  143. package/rancher-components/Form/ToggleSwitch/ToggleSwitch.vue +39 -2
  144. package/rancher-components/RcButton/RcButton.vue +90 -0
  145. package/rancher-components/RcButton/index.ts +2 -0
  146. package/rancher-components/RcButton/types.ts +17 -0
  147. package/rancher-components/RcDropdown/RcDropdown.vue +122 -0
  148. package/rancher-components/RcDropdown/RcDropdownItem.vue +127 -0
  149. package/rancher-components/RcDropdown/RcDropdownSeparator.vue +6 -0
  150. package/rancher-components/RcDropdown/RcDropdownTrigger.vue +42 -0
  151. package/rancher-components/RcDropdown/index.ts +4 -0
  152. package/rancher-components/RcDropdown/types.ts +22 -0
  153. package/rancher-components/RcDropdown/useDropdownCollection.ts +46 -0
  154. package/rancher-components/RcDropdown/useDropdownContext.ts +110 -0
  155. package/scripts/test-plugins-build.sh +2 -0
  156. package/scripts/typegen.sh +2 -0
  157. package/store/catalog.js +1 -1
  158. package/tsconfig.json +2 -1
  159. package/types/components/paginatedResourceTable.ts +25 -0
  160. package/types/components/resourceLabeledSelect.ts +48 -0
  161. package/types/resources/fleet.d.ts +17 -0
  162. package/types/shell/index.d.ts +61 -0
  163. package/utils/auth.js +5 -1
  164. package/utils/cluster.js +106 -0
  165. package/utils/fleet.ts +35 -3
  166. package/utils/ingress.ts +64 -0
  167. package/utils/uiplugins.ts +56 -44
  168. package/utils/validators/cron-schedule.js +7 -2
  169. package/utils/validators/formRules/__tests__/index.test.ts +53 -17
  170. package/utils/validators/formRules/index.ts +20 -5
  171. package/vue.config.js +1 -1
  172. package/components/RelatedWorkloadsTable.vue +0 -50
@@ -0,0 +1,48 @@
1
+ import { LabelSelectPaginationFunctionOptions } from '@shell/components/form/labeled-select-utils/labeled-select.utils';
2
+ import { LabelSelectPaginateFn } from '@shell/types/components/labeledSelect';
3
+
4
+ type PaginateTypeOverridesFn = (opts: LabelSelectPaginationFunctionOptions) => LabelSelectPaginationFunctionOptions;
5
+
6
+ interface SharedSettings {
7
+ /**
8
+ * Provide specific LabelSelect options for this mode (paginated / not paginated)
9
+ */
10
+ labelSelectOptions?: { [key: string]: any },
11
+ /**
12
+ * Map, filter, tweak, etc the resources to show in the LabelSelect
13
+ */
14
+ updateResources?: (resources: any[]) => any[]
15
+ }
16
+
17
+ /**
18
+ * Settings to use when the LabelSelect is paginating
19
+ */
20
+ export interface ResourceLabeledSelectPaginateSettings extends SharedSettings {
21
+ /**
22
+ * Override the convenience function which fetches a page of results
23
+ */
24
+ overrideRequest?: LabelSelectPaginateFn,
25
+ /**
26
+ * Override the default settings used in the convenience function to fetch a page of results
27
+ */
28
+ requestSettings?: PaginateTypeOverridesFn,
29
+ }
30
+
31
+ /**
32
+ * Settings to use when the LabelSelect is fetching all resources (not paginating)
33
+ */
34
+ export type ResourceLabeledSelectSettings = SharedSettings
35
+
36
+ /**
37
+ * Force a specific mode
38
+ */
39
+ export enum RESOURCE_LABEL_SELECT_MODE {
40
+ /**
41
+ * Fetch all resources
42
+ */
43
+ ALL_RESOURCES = 'ALL', // eslint-disable-line no-unused-vars
44
+ /**
45
+ * Determine if all resources are fetched given system settings
46
+ */
47
+ DYNAMIC = 'DYNAMIC', // eslint-disable-line no-unused-vars
48
+ }
@@ -24,10 +24,19 @@ export interface BundleNonReadyBundle {
24
24
  nonReadyStatus: BundleNonReadyResource[],
25
25
  }
26
26
 
27
+ export interface Condition {
28
+ status: string,
29
+ type: string,
30
+ }
31
+
27
32
  export interface BundleDeploymentStatus {
28
33
  resources?: BundleDeploymentResource[],
29
34
  modifiedStatus?: BundleModifiedResource[],
30
35
  nonReadyStatus?: BundleNonReadyResource[],
36
+ appliedDeploymentId?: string,
37
+ ready: boolean
38
+ nonModified: boolean
39
+ conditions: Condition[],
31
40
  }
32
41
 
33
42
  export interface BundleStatusSummary {
@@ -38,3 +47,11 @@ export interface BundleStatus {
38
47
  resourceKey?: BundleResourceKey[],
39
48
  summary?: BundleStatusSummary,
40
49
  }
50
+
51
+ export interface BundleDeployment {
52
+ spec: {
53
+ deploymentId: string,
54
+ stagedDeploymentId: string,
55
+ }
56
+ status?: BundleDeploymentStatus
57
+ }
@@ -125,6 +125,8 @@ export namespace FLEET {
125
125
  export let CLUSTER_DISPLAY_NAME: string;
126
126
  export let CLUSTER_NAME: string;
127
127
  export let BUNDLE_ID: string;
128
+ export let BUNDLE_NAME: string;
129
+ export let BUNDLE_NAMESPACE: string;
128
130
  let MANAGED_1: string;
129
131
  export { MANAGED_1 as MANAGED };
130
132
  let CLUSTER_NAMESPACE_1: string;
@@ -997,6 +999,9 @@ export namespace LAST_SEEN_TIME {
997
999
  export { sort_50 as sort };
998
1000
  export let tooltip: string;
999
1001
  }
1002
+ export namespace EVENT_LAST_SEEN_TIME {
1003
+ let defaultSort: boolean;
1004
+ }
1000
1005
  export namespace LAST_HEARTBEAT_TIME {
1001
1006
  let name_58: string;
1002
1007
  export { name_58 as name };
@@ -2203,6 +2208,21 @@ export const DEPRECATED: "Deprecated";
2203
2208
  export const EXPERIMENTAL: "Experimental";
2204
2209
  }
2205
2210
 
2211
+ // @shell/config/version
2212
+
2213
+ declare module '@shell/config/version' {
2214
+ export function isRancherPrime(): boolean;
2215
+ export function getVersionData(): {
2216
+ Version: string;
2217
+ RancherPrime: string;
2218
+ GitCommit: string;
2219
+ };
2220
+ export function setVersionData(v: any): void;
2221
+ export function getKubeVersionData(): {};
2222
+ export function setKubeVersionData(v: any): void;
2223
+ export const CURRENT_RANCHER_VERSION: "2.10";
2224
+ }
2225
+
2206
2226
  // @shell/mixins/create-edit-view/impl
2207
2227
 
2208
2228
  declare module '@shell/mixins/create-edit-view/impl' {
@@ -2402,8 +2422,11 @@ export default class ClusterRepo {
2402
2422
  spec: {
2403
2423
  url: string;
2404
2424
  };
2425
+ get _isClusterRepoDisabled(): boolean;
2405
2426
  get _availableActions(): any;
2406
2427
  refresh(): Promise<void>;
2428
+ disableClusterRepo(): Promise<void>;
2429
+ enableClusterRepo(): Promise<void>;
2407
2430
  get isGit(): boolean;
2408
2431
  get isOciType(): any;
2409
2432
  get isRancherSource(): boolean;
@@ -2429,6 +2452,8 @@ export default class ClusterRepo {
2429
2452
  };
2430
2453
  })[];
2431
2454
  get stateObj(): any;
2455
+ get stateDisplay(): any;
2456
+ get stateBackground(): any;
2432
2457
  waitForOperation(operationId: any, timeout: any, interval?: number): any;
2433
2458
  }
2434
2459
  }
@@ -2816,6 +2841,7 @@ export default class Resource {
2816
2841
  get $dispatch(): any;
2817
2842
  get $state(): any;
2818
2843
  get $rootState(): any;
2844
+ get $plugin(): any;
2819
2845
  get customValidationRules(): any[];
2820
2846
  get _key(): any;
2821
2847
  get schema(): any;
@@ -3117,6 +3143,10 @@ export default class SteveModel extends HybridModel {
3117
3143
  set description(value: any);
3118
3144
  get description(): any;
3119
3145
  _description: any;
3146
+ /**
3147
+ * Get all model extensions for this model
3148
+ */
3149
+ get modelExtensions(): any;
3120
3150
  cleanForSave(data: any, forNew: any): any;
3121
3151
  }
3122
3152
  import HybridModel from './hybrid-class';
@@ -3153,6 +3183,31 @@ export namespace actions {
3153
3183
  }
3154
3184
  }
3155
3185
 
3186
+ // @shell/store/plugins
3187
+
3188
+ declare module '@shell/store/plugins' {
3189
+ export function simplify(key: any): any;
3190
+ export function configureCredential(name: any, opt: any): void;
3191
+ export function mapDriver(name: any, to: any): void;
3192
+ export const rke1Supports: string[];
3193
+ export const likelyFields: any[];
3194
+ export const iffyFields: any[];
3195
+ export const fullFields: any[];
3196
+ export const prefixFields: any[];
3197
+ export const suffixFields: any[];
3198
+ export function state(): {};
3199
+ export namespace getters {
3200
+ function credentialOptions(): (name: any) => any;
3201
+ function credentialDriverFor(): (name: any) => any;
3202
+ function credentialFieldForDriver(): (name: any) => any;
3203
+ function clusterDrivers(): any[];
3204
+ function schemaForDriver(state: any, getters: any, rootState: any, rootGetters: any): (name: any) => any;
3205
+ function fieldNamesForDriver(state: any, getters: any): (name: any) => Promise<string[]>;
3206
+ function fieldsForDriver(state: any, getters: any): (name: any) => Promise<{}>;
3207
+ function cloudProviderForDriver(): (name: any) => any;
3208
+ }
3209
+ }
3210
+
3156
3211
  // @shell/store/prefs
3157
3212
 
3158
3213
  declare module '@shell/store/prefs' {
@@ -3440,6 +3495,8 @@ export function filterHiddenLocalCluster(mgmtClusters: any, store: any): any;
3440
3495
  */
3441
3496
  export function abbreviateClusterName(input: string): string;
3442
3497
  export function labelForAddon(store: any, name: any, configuration?: boolean): any;
3498
+ export function filterOutDeprecatedPatchVersions(allVersions: any, currentVersion: any): any;
3499
+ export function getAllOptionsAfterCurrentVersion(store: any, versions: any, currentVersion: any, defaultVersion: any): any;
3443
3500
  }
3444
3501
 
3445
3502
  // @shell/utils/color
@@ -4502,6 +4559,10 @@ export function containerImages(spec: any, getters: any, errors: any): void;
4502
4559
 
4503
4560
  declare module '@shell/utils/validators/cron-schedule' {
4504
4561
  export function cronSchedule(schedule: string, getters: any, errors: any): void;
4562
+ export namespace cronScheduleRule {
4563
+ function validation(text: any): string;
4564
+ let message: string;
4565
+ }
4505
4566
  }
4506
4567
 
4507
4568
  // @shell/utils/validators/flow-output
package/utils/auth.js CHANGED
@@ -116,7 +116,11 @@ export const checkSchemasForFindAllHash = (types, store) => {
116
116
  const validSchema = value.schemaValidator ? value.schemaValidator(schema) : !!schema;
117
117
 
118
118
  if (validSchema) {
119
- hash[key] = store.dispatch(`${ value.inStoreType }/findAll`, { type: value.type, opt: value.opt } );
119
+ const res = store.dispatch(`${ value.inStoreType }/findAll`, { type: value.type, opt: value.opt } );
120
+
121
+ if (!value.skipWait) {
122
+ hash[key] = res;
123
+ }
120
124
  }
121
125
  }
122
126
 
package/utils/cluster.js CHANGED
@@ -4,6 +4,8 @@ import { CAPI } from '@shell/config/labels-annotations';
4
4
  import { MANAGEMENT, VIRTUAL_HARVESTER_PROVIDER } from '@shell/config/types';
5
5
  import { SETTING } from '@shell/config/settings';
6
6
  import { PaginationFilterField, PaginationParamFilter } from '@shell/types/store/pagination.types';
7
+ import { compare, sortable } from '@shell/utils/version';
8
+ import { sortBy } from '@shell/utils/sort';
7
9
 
8
10
  /**
9
11
  * Combination of paginationFilterHiddenLocalCluster and paginationFilterOnlyKubernetesClusters
@@ -190,3 +192,107 @@ export function labelForAddon(store, name, configuration = true) {
190
192
 
191
193
  return store.getters['i18n/withFallback'](key, null, fallback);
192
194
  }
195
+
196
+ function getMostRecentPatchVersions(sortedVersions) {
197
+ // Get the most recent patch version for each Kubernetes minor version.
198
+ const versionMap = {};
199
+
200
+ sortedVersions.forEach((version) => {
201
+ const majorMinor = `${ semver.major(version.value) }.${ semver.minor(version.value) }`;
202
+
203
+ if (!versionMap[majorMinor]) {
204
+ // Because we start with a sorted list of versions, we know the
205
+ // highest patch version is first in the list, so we only keep the
206
+ // first of each minor version in the list.
207
+ versionMap[majorMinor] = version.value;
208
+ }
209
+ });
210
+
211
+ return versionMap;
212
+ }
213
+
214
+ export function filterOutDeprecatedPatchVersions(allVersions, currentVersion) {
215
+ // Get the most recent patch version for each Kubernetes minor version.
216
+ const mostRecentPatchVersions = getMostRecentPatchVersions(allVersions);
217
+
218
+ const filteredVersions = allVersions.filter((version) => {
219
+ // Always show pre-releases
220
+ if (semver.prerelease(version.value)) {
221
+ return true;
222
+ }
223
+
224
+ const majorMinor = `${ semver.major(version.value) }.${ semver.minor(version.value) }`;
225
+
226
+ // Always show current version, else show if we haven't shown anything for this major.minor version yet
227
+ if (version.value === currentVersion || mostRecentPatchVersions[majorMinor] === version.value) {
228
+ return true;
229
+ }
230
+
231
+ return false;
232
+ });
233
+
234
+ return filteredVersions;
235
+ }
236
+
237
+ export function getAllOptionsAfterCurrentVersion(store, versions, currentVersion, defaultVersion) {
238
+ const out = (versions || []).filter((obj) => !!obj.serverArgs).map((obj) => {
239
+ let disabled = false;
240
+ let experimental = false;
241
+ let isCurrentVersion = false;
242
+ let label = obj.id;
243
+
244
+ if (currentVersion) {
245
+ disabled = compare(obj.id, currentVersion) < 0;
246
+ isCurrentVersion = compare(obj.id, currentVersion) === 0;
247
+ }
248
+
249
+ if (defaultVersion) {
250
+ experimental = compare(defaultVersion, obj.id) < 0;
251
+ }
252
+
253
+ if (isCurrentVersion) {
254
+ label = `${ label } ${ store.getters['i18n/t']('cluster.kubernetesVersion.current') }`;
255
+ }
256
+
257
+ if (experimental) {
258
+ label = `${ label } ${ store.getters['i18n/t']('cluster.kubernetesVersion.experimental') }`;
259
+ }
260
+
261
+ return {
262
+ label,
263
+ value: obj.id,
264
+ sort: sortable(obj.id),
265
+ serverArgs: obj.serverArgs,
266
+ agentArgs: obj.agentArgs,
267
+ charts: obj.charts,
268
+ disabled,
269
+ };
270
+ });
271
+
272
+ if (currentVersion && !out.find((obj) => obj.value === currentVersion)) {
273
+ out.push({
274
+ label: `${ currentVersion } ${ store.getters['i18n/t']('cluster.kubernetesVersion.current') }`,
275
+ value: currentVersion,
276
+ sort: sortable(currentVersion),
277
+ });
278
+ }
279
+
280
+ const sorted = sortBy(out, 'sort:desc');
281
+
282
+ const mostRecentPatchVersions = getMostRecentPatchVersions(sorted);
283
+
284
+ const sortedWithDeprecatedLabel = sorted.map((optionData) => {
285
+ const majorMinor = `${ semver.major(optionData.value) }.${ semver.minor(optionData.value) }`;
286
+
287
+ if (mostRecentPatchVersions[majorMinor] === optionData.value) {
288
+ return optionData;
289
+ }
290
+
291
+ return {
292
+ ...optionData,
293
+ label: `${ optionData.label } ${ store.getters['i18n/t']('cluster.kubernetesVersion.deprecated') }`
294
+ };
295
+ });
296
+
297
+ return sortedWithDeprecatedLabel;
298
+ }
package/utils/fleet.ts CHANGED
@@ -1,11 +1,13 @@
1
1
  import {
2
2
  BundleDeploymentResource,
3
3
  BundleResourceKey,
4
+ BundleDeployment,
4
5
  BundleDeploymentStatus,
5
6
  BundleStatus,
7
+ Condition,
6
8
  } from '@shell/types/resources/fleet';
7
9
  import { STATES_ENUM } from '@shell/plugins/dashboard-store/resource-class';
8
- import { FLEET as FLEET_ANNOTATIONS } from '@shell/config/labels-annotations';
10
+ import { FLEET as FLEET_LABELS } from '@shell/config/labels-annotations';
9
11
 
10
12
  interface Resource extends BundleDeploymentResource {
11
13
  state: string,
@@ -28,6 +30,14 @@ function resourceKey(r: BundleResourceKey): string {
28
30
  return `${ r.kind }/${ r.namespace }/${ r.name }`;
29
31
  }
30
32
 
33
+ function conditionIsTrue(conditions: Condition[] | undefined, type: string): boolean {
34
+ if (!conditions) {
35
+ return false;
36
+ }
37
+
38
+ return !!conditions.find((c) => c.type === type && c.status.toLowerCase() === 'true');
39
+ }
40
+
31
41
  class Fleet {
32
42
  resourceId(r: BundleResourceKey): string {
33
43
  return r.namespace ? `${ r.namespace }/${ r.name }` : r.name;
@@ -147,11 +157,33 @@ class Fleet {
147
157
  }
148
158
 
149
159
  clusterIdFromBundleDeploymentLabels(labels?: Labels): string {
150
- const clusterNamespace = labels?.[FLEET_ANNOTATIONS.CLUSTER_NAMESPACE];
151
- const clusterName = labels?.[FLEET_ANNOTATIONS.CLUSTER];
160
+ const clusterNamespace = labels?.[FLEET_LABELS.CLUSTER_NAMESPACE];
161
+ const clusterName = labels?.[FLEET_LABELS.CLUSTER];
152
162
 
153
163
  return `${ clusterNamespace }/${ clusterName }`;
154
164
  }
165
+
166
+ bundleIdFromBundleDeploymentLabels(labels?: Labels): string {
167
+ const bundleNamespace = labels?.[FLEET_LABELS.BUNDLE_NAMESPACE];
168
+ const bundleName = labels?.[FLEET_LABELS.BUNDLE_NAME];
169
+
170
+ return `${ bundleNamespace }/${ bundleName }`;
171
+ }
172
+
173
+ bundleDeploymentState(bd: BundleDeployment): string {
174
+ // Ported from https://github.com/rancher/fleet/blob/534dbfdd6f74caf97bccd4cf977e42c5009b2432/internal/cmd/controller/summary/summary.go#L89
175
+ if (bd.status?.appliedDeploymentId !== bd.spec.deploymentId) {
176
+ return conditionIsTrue(bd.status?.conditions, 'Deployed') ? STATES_ENUM.WAIT_APPLIED : STATES_ENUM.ERR_APPLIED;
177
+ } else if (!bd.status?.ready) {
178
+ return STATES_ENUM.NOT_READY;
179
+ } else if (bd.spec.deploymentId !== bd.spec.stagedDeploymentId) {
180
+ return STATES_ENUM.OUT_OF_SYNC;
181
+ } else if (!bd.status?.nonModified) {
182
+ return STATES_ENUM.MODIFIED;
183
+ } else {
184
+ return STATES_ENUM.READY;
185
+ }
186
+ }
155
187
  }
156
188
 
157
189
  const instance = new Fleet();
@@ -0,0 +1,64 @@
1
+ import { SECRET, SERVICE } from '@shell/config/types';
2
+ import { SECRET_TYPES as TYPES } from '@shell/config/secret';
3
+ import { VuexStore } from '@shell/types/store/vuex';
4
+
5
+ /**
6
+ * Helper class for common functionality shared between the detail and edit ingress pages
7
+ *
8
+ * This could be an untyped mixin.. but this setups up us better for the future
9
+ */
10
+ class IngressDetailEditHelper {
11
+ private $store: VuexStore;
12
+ private namespace: string;
13
+
14
+ constructor({
15
+ $store,
16
+ namespace,
17
+ }: {
18
+ $store: VuexStore
19
+ namespace: string,
20
+ }) {
21
+ this.$store = $store;
22
+ this.namespace = namespace;
23
+ }
24
+
25
+ /**
26
+ * Fetch services that will either be used to show
27
+ * - Create - the possible rule's target service
28
+ * - Edit - the selected and possible rule's target service
29
+ * - Detail - the selected rule's target service
30
+ */
31
+ async fetchServices(args?: { namespace: string}): Promise<any[]> {
32
+ return this.$store.dispatch('cluster/findAll', { type: SERVICE, opt: { namespaced: args?.namespace || this.namespace } });
33
+ }
34
+
35
+ /**
36
+ * Fetch secrets that will either be used to show
37
+ * - Create - the possible secrets to use as a certificates
38
+ * - Edit - the selected and possible secrets to use as a certificates
39
+ * - Detail - the selected secrets to use as certificates
40
+ */
41
+ async fetchSecrets(args?: { namespace: string}): Promise<any[]> {
42
+ return this.$store.dispatch('cluster/findAll', { type: SECRET, opt: { namespaced: args?.namespace || this.namespace } });
43
+ }
44
+
45
+ findAndMapCerts(secrets: any[]) {
46
+ return secrets
47
+ .filter((secret) => secret._type === TYPES.TLS)
48
+ .map((secret) => {
49
+ const { id } = secret;
50
+
51
+ return id.slice(id.indexOf('/') + 1);
52
+ });
53
+ }
54
+
55
+ findAndMapServiceTargets(services: any[]) {
56
+ return services.map((service) => ({
57
+ label: service.metadata.name,
58
+ value: service.metadata.name,
59
+ ports: service.spec.ports?.map((p: any) => p.port)
60
+ }));
61
+ }
62
+ }
63
+
64
+ export default IngressDetailEditHelper;
@@ -1,3 +1,4 @@
1
+ import { matchesSomeRegex } from '@shell/utils/string';
1
2
  import { CATALOG as CATALOG_ANNOTATIONS } from '@shell/config/labels-annotations';
2
3
  import { CATALOG } from '@shell/config/types';
3
4
  import { UI_PLUGIN_BASE_URL, isSupportedChartVersion } from '@shell/config/uiplugins';
@@ -158,37 +159,56 @@ export async function installHelmChart(repo: any, chart: any, values: any = {},
158
159
  }
159
160
 
160
161
  /**
161
- * Get the Helm repository object
162
162
  *
163
163
  * @param store Vue store
164
- * @param url The url of the Helm repository
165
- * @param branch The branch of the Helm repository
164
+ * @param url Repository Url
166
165
  * @returns HelmRepository
167
166
  */
168
- export async function getHelmRepository(store: any, url: string, branch?: string): Promise<HelmRepository> {
167
+ export async function getHelmRepositoryExact(store: any, url: string): Promise<HelmRepository> {
168
+ return await getHelmRepository(store, (repository: any) => {
169
+ const target = repository.spec?.gitRepo || repository.spec?.url;
170
+
171
+ return target === url;
172
+ });
173
+ }
174
+
175
+ /**
176
+ *
177
+ * @param store Vue store
178
+ * @param urlRegexes Regex to match a community repository
179
+ * @returns HelmRepository
180
+ */
181
+ export async function getHelmRepositoryMatch(store: any, urlRegexes: string[]): Promise<HelmRepository> {
182
+ return await getHelmRepository(store, (repository: any) => {
183
+ const target = repository.spec?.gitBranch ? repository.spec?.gitRepo : repository.spec?.url;
184
+
185
+ return matchesSomeRegex(target, urlRegexes);
186
+ });
187
+ }
188
+
189
+ /**
190
+ *
191
+ * @param store Vue store
192
+ * @param matchFn Match function for repository's urls
193
+ * @returns HelmRepository
194
+ */
195
+ async function getHelmRepository(store: any, matchFn: (repository: any) => boolean): Promise<HelmRepository> {
169
196
  if (store.getters['management/schemaFor'](CATALOG.CLUSTER_REPO)) {
170
197
  const repos = await store.dispatch('management/findAll', { type: CATALOG.CLUSTER_REPO, opt: { force: true, watch: false } });
171
198
 
172
- return repos.find((r: any) => {
173
- const target = branch ? r.spec?.gitRepo : r.spec?.url ;
174
-
175
- return target === url;
176
- });
199
+ return repos.find(matchFn);
177
200
  } else {
178
201
  throw new Error('No permissions');
179
202
  }
180
203
  }
181
204
 
182
205
  /**
183
- * Refresh the Helm repository
184
- * Ensures that we find the latest extension versions
185
206
  *
186
207
  * @param store Vue store
187
- * @param gitRepo Extension Repository url
188
- * @param gitBranch Extension Repository branch
208
+ * @param url Repository Url
189
209
  */
190
- export async function refreshHelmRepository(store: any, gitRepo: string, gitBranch: string): Promise<void> {
191
- const repository = await getHelmRepository(store, gitRepo, gitBranch);
210
+ export async function refreshHelmRepository(store: any, url: string): Promise<void> {
211
+ const repository = await getHelmRepositoryExact(store, url);
192
212
 
193
213
  const now = (new Date()).toISOString().replace(/\.\d+Z$/, 'Z');
194
214
 
@@ -202,40 +222,32 @@ export async function refreshHelmRepository(store: any, gitRepo: string, gitBran
202
222
  }
203
223
 
204
224
  /**
205
- * Ensure the required Helm Repository exits, if it does not, add it with the specified name
206
- *
207
- * Wait until the newly added repository has been downloaded
208
225
  *
209
226
  * @param store Vue store
210
- * @param url The url of the Helm repository
211
- * @param name The name of the cluster repository
212
- * @param branch The branch of the Helm repository
213
- * @returns HelmRepository object
227
+ * @param name Repository name
228
+ * @param url Repository Url
229
+ * @param branch Repository Branch
230
+ * @returns HelmRepository
214
231
  */
215
- export async function ensureHelmRepository(store: any, url: string, name: string, branch?: string): Promise<HelmRepository> {
216
- let helmRepo = await getHelmRepository(store, url, branch);
217
-
218
- // Add the Helm repository, if it is not there
219
- if (!helmRepo) {
220
- const data = {
221
- type: CATALOG.CLUSTER_REPO,
222
- metadata: { name },
223
- spec: {} as any
224
- };
225
-
226
- if (branch) {
227
- data.spec.gitBranch = branch;
228
- data.spec.gitRepo = url;
229
- } else {
230
- data.spec.url = url;
231
- }
232
-
233
- // Create a model for the new repository and save it
234
- const repo = await store.dispatch('management/create', data);
232
+ export async function createHelmRepository(store: any, name: string, url: string, branch?: string): Promise<HelmRepository> {
233
+ const data = {
234
+ type: CATALOG.CLUSTER_REPO,
235
+ metadata: { name },
236
+ spec: {} as any
237
+ };
235
238
 
236
- helmRepo = await repo.save();
239
+ if (branch) {
240
+ data.spec.gitBranch = branch;
241
+ data.spec.gitRepo = url;
242
+ } else {
243
+ data.spec.url = url;
237
244
  }
238
245
 
246
+ // Create a model for the new repository and save it
247
+ const repo = await store.dispatch('management/create', data);
248
+
249
+ const helmRepo = await repo.save();
250
+
239
251
  // Poll the repository until it says it has been downloaded
240
252
  let fetched = false;
241
253
  let tries = 0;
@@ -278,7 +290,7 @@ export async function ensureHelmRepository(store: any, url: string, name: string
278
290
  * Get the given Helm Chart from the specified Helm Repository
279
291
  *
280
292
  * @param store Vue store
281
- * @param repository Helm Repository
293
+ * @param repository Repository Url
282
294
  * @param chartName Helm Chart name
283
295
  * @returns Helm Chart
284
296
  */
@@ -2,8 +2,13 @@ import cronstrue from 'cronstrue';
2
2
 
3
3
  export function cronSchedule(schedule = '', getters, errors) {
4
4
  try {
5
- cronstrue.toString(schedule, { verbose: true });
5
+ cronScheduleRule.validation(schedule);
6
6
  } catch (e) {
7
- errors.push(getters['i18n/t']('validation.invalidCron'));
7
+ errors.push(getters['i18n/t'](cronScheduleRule.message));
8
8
  }
9
9
  }
10
+
11
+ export const cronScheduleRule = {
12
+ validation: (text) => cronstrue.toString(text, { verbose: true }),
13
+ message: 'validation.invalidCron'
14
+ };