@rancher/shell 3.0.12-rc.5 → 3.0.12-rc.7

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 (116) hide show
  1. package/apis/intf/resources-api/resource-base.ts +42 -2
  2. package/apis/intf/resources-api/resource-instance.ts +101 -0
  3. package/apis/intf/resources-api/resources-api.ts +177 -23
  4. package/apis/intf/resources.ts +8 -6
  5. package/apis/intf/shell-api/slide-in.ts +46 -17
  6. package/apis/resources/__tests__/resources-api-class.test.ts +309 -28
  7. package/apis/resources/resources-api-class.ts +232 -43
  8. package/apis/shell/__tests__/slide-in.test.ts +83 -2
  9. package/apis/shell/slide-in.ts +20 -0
  10. package/assets/images/providers/traefik.png +0 -0
  11. package/assets/translations/en-us.yaml +10 -4
  12. package/chart/__tests__/rancher-monitoring-dashboards-index.test.ts +269 -0
  13. package/chart/rancher-monitoring-dashboards/index.vue +155 -0
  14. package/cloud-credential/__tests__/generic.test.ts +132 -0
  15. package/cloud-credential/generic.vue +33 -3
  16. package/components/EtcdInfoBanner.vue +2 -12
  17. package/components/ExplorerProjectsNamespaces.vue +2 -2
  18. package/components/GrafanaDashboard.vue +2 -8
  19. package/components/Resource/Detail/Masthead/__tests__/index.test.ts +68 -14
  20. package/components/Resource/Detail/Masthead/index.vue +8 -3
  21. package/components/Resource/Detail/Metadata/IdentifyingInformation/__tests__/identifying-fields.test.ts +9 -0
  22. package/components/Resource/Detail/Metadata/IdentifyingInformation/identifying-fields.ts +5 -1
  23. package/components/SlideInPanelManager.vue +103 -25
  24. package/components/__tests__/SlideInPanelManager.spec.ts +153 -10
  25. package/components/auth/AuthBanner.vue +1 -1
  26. package/components/form/Security.vue +1 -1
  27. package/components/formatter/Translate.vue +22 -1
  28. package/components/nav/Group.vue +5 -10
  29. package/components/nav/TopLevelMenu.vue +1 -30
  30. package/components/nav/__tests__/Group.test.ts +61 -0
  31. package/config/labels-annotations.js +1 -0
  32. package/config/table-headers.js +5 -2
  33. package/core/__tests__/plugin-products-apply.test.ts +787 -0
  34. package/core/__tests__/plugin-products-extend.test.ts +189 -0
  35. package/core/__tests__/plugin-products-helpers.test.ts +81 -78
  36. package/core/__tests__/plugin-products-new.test.ts +389 -0
  37. package/core/__tests__/plugin-products-scenarios.test.ts +980 -0
  38. package/core/__tests__/plugin-products-side-menu.test.ts +1935 -0
  39. package/core/plugin-products-base.ts +201 -93
  40. package/core/plugin-products-extending.ts +1 -1
  41. package/core/plugin-products-external.ts +380 -0
  42. package/core/plugin-products-helpers.ts +28 -24
  43. package/core/plugin-products-internal.ts +207 -0
  44. package/core/plugin-products-top-level.ts +10 -5
  45. package/core/plugin-products-type-guards.ts +17 -6
  46. package/core/plugin-products.ts +5 -7
  47. package/core/plugin-types.ts +1 -389
  48. package/core/plugin.ts +10 -11
  49. package/core/plugins-loader.js +2 -0
  50. package/core/types.ts +6 -202
  51. package/detail/__tests__/provisioning.cattle.io.cluster.test.ts +52 -0
  52. package/detail/pod.vue +1 -1
  53. package/detail/provisioning.cattle.io.cluster.vue +0 -2
  54. package/edit/auth/AuthProviderWarningBanners.vue +12 -0
  55. package/edit/auth/__tests__/AuthProviderWarningBanners.test.ts +10 -1
  56. package/edit/auth/__tests__/azuread.test.ts +1 -0
  57. package/edit/auth/__tests__/github.test.ts +1 -0
  58. package/edit/auth/__tests__/oidc.test.ts +1 -0
  59. package/edit/auth/__tests__/saml.test.ts +1 -0
  60. package/edit/auth/ldap/__tests__/index.test.ts +1 -0
  61. package/edit/provisioning.cattle.io.cluster/index.vue +1 -1
  62. package/edit/provisioning.cattle.io.cluster/rke2.vue +1 -1
  63. package/edit/provisioning.cattle.io.cluster/tabs/networking/index.vue +2 -2
  64. package/list/management.cattle.io.user.vue +24 -5
  65. package/list/utils/management.cattle.io.cluster.utils.ts +1 -1
  66. package/machine-config/__tests__/generic.test.ts +163 -0
  67. package/machine-config/components/EC2Networking.vue +31 -7
  68. package/machine-config/components/__tests__/EC2Networking.test.ts +38 -15
  69. package/machine-config/generic.vue +32 -5
  70. package/mixins/resource-fetch.js +1 -1
  71. package/models/__tests__/namespace.test.ts +133 -8
  72. package/models/__tests__/provisioning.cattle.io.cluster.test.ts +57 -1
  73. package/models/__tests__/rke-machine.cattle.io.vmwarevspheremachinetemplate.test.ts +22 -0
  74. package/models/namespace.js +34 -2
  75. package/models/pod.js +12 -10
  76. package/models/provisioning.cattle.io.cluster.js +20 -13
  77. package/models/rke-machine.cattle.io.vmwarevspheremachinetemplate.js +9 -0
  78. package/package.json +1 -1
  79. package/pages/auth/login.vue +1 -1
  80. package/pages/c/_cluster/apps/charts/__tests__/install.test.ts +110 -0
  81. package/pages/c/_cluster/apps/charts/install.vue +29 -0
  82. package/pages/c/_cluster/auth/config/index.vue +11 -4
  83. package/pages/c/_cluster/explorer/workload-dashboard/__tests__/composable.test.ts +136 -1
  84. package/pages/c/_cluster/explorer/workload-dashboard/composable.ts +69 -1
  85. package/pages/c/_cluster/fleet/index.vue +5 -9
  86. package/pages/c/_cluster/monitoring/index.vue +6 -1
  87. package/pkg/vue.config.js +4 -3
  88. package/plugins/codemirror.js +2 -0
  89. package/plugins/dashboard-store/resource-class.js +13 -9
  90. package/plugins/steve/__tests__/steve-class-resource-api.test.ts +147 -0
  91. package/plugins/steve/steve-class.js +43 -0
  92. package/plugins/steve/subscribe.js +11 -0
  93. package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +115 -0
  94. package/rancher-components/Form/LabeledInput/LabeledInput.vue +83 -3
  95. package/store/__tests__/action-menu.test.ts +622 -0
  96. package/store/__tests__/auth.test.ts +18 -0
  97. package/store/__tests__/notifications.test.ts +466 -1
  98. package/store/__tests__/slideInPanel.test.ts +143 -43
  99. package/store/__tests__/ui-context.test.ts +255 -0
  100. package/store/__tests__/wm.test.ts +503 -0
  101. package/store/auth.js +11 -1
  102. package/store/aws.js +19 -4
  103. package/store/features.js +1 -0
  104. package/store/plugins.js +6 -0
  105. package/store/slideInPanel.ts +15 -3
  106. package/types/rancher/steve.api.ts +2 -2
  107. package/types/shell/index.d.ts +84 -2
  108. package/types/store/dashboard-store.types.ts +2 -2
  109. package/types/store/type-map.ts +262 -1
  110. package/utils/__tests__/fleet-appco.test.ts +23 -0
  111. package/utils/async.ts +2 -0
  112. package/utils/fleet-appco.ts +6 -1
  113. package/utils/grafana.js +2 -17
  114. package/utils/monitoring.js +38 -1
  115. package/utils/sort.js +1 -1
  116. package/core/__tests__/plugin-products.test.ts +0 -4694
@@ -28,6 +28,7 @@ export const IMPORTED_CLUSTER_VERSION_MANAGEMENT: "rancher.io/imported-cluster-v
28
28
  export const UI_PROJECT_SECRET: "management.cattle.io/project-scoped-secret";
29
29
  export const UI_PROJECT_SECRET_COPY: "management.cattle.io/project-scoped-secret-copy";
30
30
  export const SERVICE_LINKS: "ui.rancher/service-links";
31
+ export const NODE_DRIVER_FIELD_HINTS: "io.cattle.nodedriver/ui-field-hints";
31
32
  export namespace KUBERNETES {
32
33
  let SERVICE_ACCOUNT_UID: string;
33
34
  let SERVICE_ACCOUNT_NAME: string;
@@ -199,6 +200,7 @@ export const IMPORTED_CLUSTER_VERSION_MANAGEMENT: "rancher.io/imported-cluster-v
199
200
  export const UI_PROJECT_SECRET: "management.cattle.io/project-scoped-secret";
200
201
  export const UI_PROJECT_SECRET_COPY: "management.cattle.io/project-scoped-secret-copy";
201
202
  export const SERVICE_LINKS: "ui.rancher/service-links";
203
+ export const NODE_DRIVER_FIELD_HINTS: "io.cattle.nodedriver/ui-field-hints";
202
204
  export namespace KUBERNETES {
203
205
  let SERVICE_ACCOUNT_UID: string;
204
206
  let SERVICE_ACCOUNT_NAME: string;
@@ -2369,6 +2371,7 @@ export namespace FEATURE_DESCRIPTION {
2369
2371
  export { formatter_65 as formatter };
2370
2372
  export namespace formatterOpts_16 {
2371
2373
  let prefix: string;
2374
+ let fallbackPath: string;
2372
2375
  }
2373
2376
  export { formatterOpts_16 as formatterOpts };
2374
2377
  }
@@ -4166,6 +4169,7 @@ export namespace FEATURE_DESCRIPTION {
4166
4169
  export { formatter_65 as formatter };
4167
4170
  export namespace formatterOpts_16 {
4168
4171
  let prefix: string;
4172
+ let fallbackPath: string;
4169
4173
  }
4170
4174
  export { formatterOpts_16 as formatterOpts };
4171
4175
  }
@@ -5979,13 +5983,25 @@ export default class Namespace {
5979
5983
  get confirmRemove(): boolean;
5980
5984
  get listLocation(): {
5981
5985
  name: string;
5986
+ params: {
5987
+ cluster: any;
5988
+ product: any;
5989
+ };
5982
5990
  };
5983
5991
  get _detailLocation(): any;
5984
5992
  get parentLocationOverride(): {
5985
5993
  name: string;
5994
+ params: {
5995
+ cluster: any;
5996
+ product: any;
5997
+ };
5986
5998
  };
5987
5999
  get doneOverride(): {
5988
6000
  name: string;
6001
+ params: {
6002
+ cluster: any;
6003
+ product: any;
6004
+ };
5989
6005
  };
5990
6006
  set resourceQuota(value: any);
5991
6007
  get resourceQuota(): any;
@@ -6043,13 +6059,25 @@ export default class Namespace {
6043
6059
  get confirmRemove(): boolean;
6044
6060
  get listLocation(): {
6045
6061
  name: string;
6062
+ params: {
6063
+ cluster: any;
6064
+ product: any;
6065
+ };
6046
6066
  };
6047
6067
  get _detailLocation(): any;
6048
6068
  get parentLocationOverride(): {
6049
6069
  name: string;
6070
+ params: {
6071
+ cluster: any;
6072
+ product: any;
6073
+ };
6050
6074
  };
6051
6075
  get doneOverride(): {
6052
6076
  name: string;
6077
+ params: {
6078
+ cluster: any;
6079
+ product: any;
6080
+ };
6053
6081
  };
6054
6082
  set resourceQuota(value: any);
6055
6083
  get resourceQuota(): any;
@@ -7870,6 +7898,18 @@ export default class SteveModel extends HybridModel {
7870
7898
  * @param {*} res
7871
7899
  */
7872
7900
  processSaveResponse(res: any, opt?: {}): void;
7901
+ /**
7902
+ * RESOURCES API - ResourceInstance update method to send a PATCH request
7903
+ */
7904
+ update(data: any): Promise<this>;
7905
+ /**
7906
+ * RESOURCES API - ResourceInstance update method to send a PUT request
7907
+ */
7908
+ replace(): Promise<this>;
7909
+ /**
7910
+ * RESOURCES API - ResourceInstance delete method to send a DELETE request
7911
+ */
7912
+ delete(): Promise<void>;
7873
7913
  }
7874
7914
  import HybridModel from './hybrid-class';
7875
7915
  }
@@ -7899,6 +7939,18 @@ export default class SteveModel extends HybridModel {
7899
7939
  * @param {*} res
7900
7940
  */
7901
7941
  processSaveResponse(res: any, opt?: {}): void;
7942
+ /**
7943
+ * RESOURCES API - ResourceInstance update method to send a PATCH request
7944
+ */
7945
+ update(data: any): Promise<this>;
7946
+ /**
7947
+ * RESOURCES API - ResourceInstance update method to send a PUT request
7948
+ */
7949
+ replace(): Promise<this>;
7950
+ /**
7951
+ * RESOURCES API - ResourceInstance delete method to send a DELETE request
7952
+ */
7953
+ delete(): Promise<void>;
7902
7954
  }
7903
7955
  import HybridModel from './hybrid-class';
7904
7956
  }
@@ -7928,6 +7980,7 @@ export const AUTOSCALER: any;
7928
7980
  export const CLUSTER_SHELL: any;
7929
7981
  export const NODE_SHELL: any;
7930
7982
  export const POD_SHELL: any;
7983
+ export const HIDE_LOCAL_AUTH_PROVIDER: any;
7931
7984
  export namespace getters {
7932
7985
  function get(state: any, getters: any, rootState: any, rootGetters: any): (name: any) => any;
7933
7986
  }
@@ -7964,6 +8017,7 @@ export const AUTOSCALER: any;
7964
8017
  export const CLUSTER_SHELL: any;
7965
8018
  export const NODE_SHELL: any;
7966
8019
  export const POD_SHELL: any;
8020
+ export const HIDE_LOCAL_AUTH_PROVIDER: any;
7967
8021
  export namespace getters {
7968
8022
  function get(state: any, getters: any, rootState: any, rootGetters: any): (name: any) => any;
7969
8023
  }
@@ -7988,6 +8042,12 @@ export const fullFields: any[];
7988
8042
  export const prefixFields: any[];
7989
8043
  export const suffixFields: any[];
7990
8044
  export function state(): {};
8045
+ export namespace actions {
8046
+ function mapDriver(ctx: any, { name, to }: {
8047
+ name: any;
8048
+ to: any;
8049
+ }): void;
8050
+ }
7991
8051
  export namespace getters {
7992
8052
  function credentialOptions(): (name: any) => any;
7993
8053
  function credentialDriverFor(): (name: any) => any;
@@ -8013,6 +8073,12 @@ export const fullFields: any[];
8013
8073
  export const prefixFields: any[];
8014
8074
  export const suffixFields: any[];
8015
8075
  export function state(): {};
8076
+ export namespace actions {
8077
+ function mapDriver(ctx: any, { name, to }: {
8078
+ name: any;
8079
+ to: any;
8080
+ }): void;
8081
+ }
8016
8082
  export namespace getters {
8017
8083
  function credentialOptions(): (name: any) => any;
8018
8084
  function credentialDriverFor(): (name: any) => any;
@@ -9526,6 +9592,14 @@ export function monitoringStatus(): {
9526
9592
  };
9527
9593
  };
9528
9594
  export function haveV2Monitoring(getters: any): boolean;
9595
+ /**
9596
+ * Look up the installed Rancher monitoring app, preferring the new
9597
+ * `rancher-monitoring-dashboards` chart and falling back to the legacy
9598
+ * `rancher-monitoring`. Returns null if the user lacks permission, neither
9599
+ * chart is installed, or any lookup fails.
9600
+ */
9601
+ export function fetchMonitoringApp(store: any, storeName: any): Promise<any>;
9602
+ export function fetchMonitoringVersion(store: any, storeName: any): Promise<any>;
9529
9603
  export function canViewGrafanaLink(store: any): Promise<boolean>;
9530
9604
  export function canViewAlertManagerLink(store: any): Promise<boolean>;
9531
9605
  export function canViewPrometheusLink(store: any): Promise<boolean>;
@@ -9541,6 +9615,14 @@ export function monitoringStatus(): {
9541
9615
  };
9542
9616
  };
9543
9617
  export function haveV2Monitoring(getters: any): boolean;
9618
+ /**
9619
+ * Look up the installed Rancher monitoring app, preferring the new
9620
+ * `rancher-monitoring-dashboards` chart and falling back to the legacy
9621
+ * `rancher-monitoring`. Returns null if the user lacks permission, neither
9622
+ * chart is installed, or any lookup fails.
9623
+ */
9624
+ export function fetchMonitoringApp(store: any, storeName: any): Promise<any>;
9625
+ export function fetchMonitoringVersion(store: any, storeName: any): Promise<any>;
9544
9626
  export function canViewGrafanaLink(store: any): Promise<boolean>;
9545
9627
  export function canViewAlertManagerLink(store: any): Promise<boolean>;
9546
9628
  export function canViewPrometheusLink(store: any): Promise<boolean>;
@@ -10511,7 +10593,7 @@ export function parseField(str: any): {
10511
10593
  field: any;
10512
10594
  reverse: boolean;
10513
10595
  };
10514
- export function sortBy(ary: any, keys: any, desc: any): any;
10596
+ export function sortBy(ary: any, keys: any, desc?: boolean): any;
10515
10597
  export function sortableNumericSuffix(str: any): any;
10516
10598
  export function isNumeric(num: any): boolean;
10517
10599
  }
@@ -10576,7 +10658,7 @@ export function parseField(str: any): {
10576
10658
  field: any;
10577
10659
  reverse: boolean;
10578
10660
  };
10579
- export function sortBy(ary: any, keys: any, desc: any): any;
10661
+ export function sortBy(ary: any, keys: any, desc?: boolean): any;
10580
10662
  export function sortableNumericSuffix(str: any): any;
10581
10663
  export function isNumeric(num: any): boolean;
10582
10664
  }
@@ -180,7 +180,7 @@ export type ActionFindPageTransientResponse<T = any> = {
180
180
  *
181
181
  * If the request was not transient this will just be the array of resources
182
182
  */
183
- export type ActionFindPageResponse<T = any> = ActionFindPageTransientResponse | T[];
183
+ export type ActionFindPageResponse<T = Record<string, any>> = ActionFindPageTransientResponse<T> | T[];
184
184
 
185
185
  /**
186
186
  * Args used for findMatching action
@@ -221,7 +221,7 @@ export interface ActionFindMatchingArgs extends ActionCoreFindOptions {
221
221
  * @internal
222
222
  * Response to the findMatching action
223
223
  */
224
- export type ActionFindMatchingResponse<T = any> = ActionFindPageResponse<T>
224
+ export type ActionFindMatchingResponse<T = any> = T[]
225
225
 
226
226
  /**
227
227
  * @internal
@@ -1,3 +1,264 @@
1
+ import { PluginRouteRecordRaw } from '@shell/core/types';
2
+
3
+ /**
4
+ * The product as seen by the type-map
5
+ */
6
+ export interface TypeMapProduct {
7
+ /**
8
+ * The category this product belongs under. i.e. 'config'
9
+ */
10
+ category?: string;
11
+
12
+ /**
13
+ * Hide the Copy KubeConfig button in the header
14
+ */
15
+ hideCopyConfig?: boolean;
16
+
17
+ /**
18
+ * Hide the Download KubeConfig button in the header
19
+ */
20
+ hideKubeConfig?: boolean;
21
+
22
+ /**
23
+ * Hide the Kubectl Shell button in the header
24
+ */
25
+ hideKubeShell?: boolean;
26
+
27
+ /**
28
+ * Hide the Namespace location
29
+ */
30
+ hideNamespaceLocation?: boolean;
31
+
32
+ /**
33
+ * Hide the system resources
34
+ */
35
+ hideSystemResources?: boolean;
36
+
37
+ /**
38
+ * The icon that should be displayed beside this item in the navigation.
39
+ */
40
+ icon?: string;
41
+
42
+ /**
43
+ * Alternative to the icon property. Uses require
44
+ */
45
+ svg?: Function;
46
+
47
+ /**
48
+ * Only load the product if the feature is present
49
+ */
50
+ ifFeature?: string | RegExp;
51
+
52
+ /**
53
+ * Only load the product if the type is present
54
+ */
55
+ ifHave?: string;
56
+
57
+ /**
58
+ * Only load the product if the group is present
59
+ */
60
+ ifHaveGroup?: string | RegExp;
61
+
62
+ /**
63
+ * Only load the product if the type is present
64
+ */
65
+ ifHaveType?: string | RegExp;
66
+
67
+ /**
68
+ * Hide the product if the type is present (opposite of ifHaveType)
69
+ */
70
+ ifNotHaveType?: string | RegExp;
71
+
72
+ /**
73
+ * The vuex store that this product should use by default i.e. 'management'
74
+ */
75
+ inStore?: string;
76
+
77
+ /**
78
+ * Show the cluster switcher in the navigation
79
+ */
80
+ showClusterSwitcher?: boolean;
81
+
82
+ /**
83
+ * Indicates whether UI Extensions can add pages to this product
84
+ */
85
+ extendable?: boolean;
86
+
87
+ /**
88
+ * Show the namespace filter in the header
89
+ */
90
+ showNamespaceFilter?: boolean;
91
+
92
+ /**
93
+ * A number used to determine where in navigation this item will be placed. The highest number will be at the top of the list.
94
+ */
95
+ weight?: number;
96
+
97
+ /**
98
+ * The route that the product will lead to if click on in navigation.
99
+ */
100
+ to?: PluginRouteRecordRaw;
101
+
102
+ /**
103
+ * Product name
104
+ *
105
+ * Defaults to the DSL fn product arg
106
+ */
107
+ name?: string;
108
+
109
+ /**
110
+ *
111
+ */
112
+ label?: string;
113
+
114
+ labelKey?: string;
115
+
116
+ iconHeader?: string;
117
+
118
+ // Do not use - internal use only
119
+ version?: number;
120
+
121
+ renameGroups?: {
122
+ /** String or regex to match against group internal IDs */
123
+ groupSelector: RegExp | string;
124
+ /** Display name to use for matching groups */
125
+ newName: string;
126
+ }[];
127
+
128
+ moveToGroup?: {
129
+ /** Page identifier — the resource `type` string or the custom page `name` */
130
+ entryId: string;
131
+ /** Target group name as defined in your group config (`name` property) */
132
+ groupName: string;
133
+ /** Ordering weight for the mapping (default: 5). Higher weight takes precedence when multiple rules match */
134
+ weight?: number;
135
+ }[];
136
+
137
+ ignoreGroups?: {
138
+ /** String or regex to match against group names */
139
+ groupSelector: string | RegExp;
140
+ /** Optional conditional function that accepts the root Dashboard Vuex store getters and returns true if the group should be ignored */
141
+ condition?: (getters: any) => boolean;
142
+ }[];
143
+
144
+ /**
145
+ * Whether the product can be removed by users (default: false — products are built-in/not removable unless explicitly set to true)
146
+ */
147
+ removable?: boolean;
148
+ /**
149
+ * controls whether a workspace switcher dropdown appears in the header (instead of the namespace filter) if set to true
150
+ */
151
+ showWorkspaceSwitcher?: boolean;
152
+
153
+ /**
154
+ * Leaving these here for completeness but I don't think these should be advertised as useable to plugin creators.
155
+ */
156
+ // ifHaveVerb: string | RegExp;
157
+ // supportRoute: string;
158
+ // typeStoreMap: string;
159
+ }
160
+
161
+ /**
162
+ * Used by type-map configureType
163
+ */
164
+ export interface TypeMapConfigureType {
165
+ /** Override for the name displayed */
166
+ displayName?: string;
167
+ /** Override for the create button string on a list view */
168
+ listCreateButtonLabelKey?: string;
169
+ /** If false, disable create even if schema says it's allowed */
170
+ isCreatable?: boolean;
171
+ /** If false, disable for edit */
172
+ isEditable?: boolean;
173
+ /** If false, disable for remove/delete */
174
+ isRemovable?: boolean;
175
+ /** If false, hide state in columns and masthead */
176
+ showState?: boolean;
177
+ /** If false, hide age in columns and masthead */
178
+ showAge?: boolean;
179
+ /** If false, hide masthead config button in view mode */
180
+ showConfigView?: boolean;
181
+ /** If false, hide masthead in list view */
182
+ showListMasthead?: boolean;
183
+ /** If false, cannot edit or show yaml */
184
+ canYaml?: boolean;
185
+ /** Show the Masthead in the edit resource component */
186
+ resourceEditMasthead?: boolean;
187
+ /** Entry route definition for this resource page */
188
+ customRoute?: PluginRouteRecordRaw | Object;
189
+ /** Hide this type from the nav/search bar on downstream clusters (will only show in "local" cluster) */
190
+ localOnly?: boolean;
191
+ // resource: undefined; // Use this resource in ResourceDetails instead
192
+ // resourceDetail: undefined; // Use this resource specifically for ResourceDetail's detail component
193
+ // resourceEdit: undefined; // Use this resource specifically for ResourceDetail's edit component
194
+ /**
195
+ * Use this to depaginate requests for this type
196
+ */
197
+ depaginate?: undefined;
198
+ // notFilterNamespace: undefined; // Define namespaces that do not need to be filtered
199
+ // used in configureType options, to be typed later if needed
200
+
201
+ /**
202
+ * Whether this custom page has list groups (definition for grouping items in the list view)
203
+ */
204
+ listGroups?: {
205
+ /** Icon for the group (relates to icons in rancher-icons */
206
+ icon?: string;
207
+ /** Value for the group (used for grouping items in the list view) */
208
+ value?: string;
209
+ /** Field for the group (used for grouping items in the list view) */
210
+ field?: string;
211
+ /** Column to hide when this group is active */
212
+ hideColumn?: string;
213
+ /** Tooltip key for the group */
214
+ tooltipKey?: string;
215
+ }[];
216
+
217
+ /**
218
+ * Whether the provided list groups will override the default grouping options (e.g. group by namespace, group by cluster, etc.) or be added to them
219
+ */
220
+ listGroupsWillOverride?: boolean;
221
+
222
+ /**
223
+ * Use this to configure subtypes that should be shown in the list view for this type (e.g. show "pods" and "deployments" in the list view for "workloads")
224
+ */
225
+ subTypes?: string[];
226
+ }
227
+
228
+ /**
229
+ * Used by type-map virtualType
230
+ */
231
+ export interface TypeMapVirtualType {
232
+ /** Display only if condition is met (relates to IF_HAVE in shell/store/type-map) */
233
+ ifHave?: boolean;
234
+ /** Display only if feature is present (relates to shell/store/features) */
235
+ ifFeature?: string;
236
+ /** Display only if resource type exists */
237
+ ifHaveType?: string;
238
+ /** Used in conjunction with "ifHaveType", display only if resource type allows this verb (GET, POST, PUT, DELETE) */
239
+ ifHaveVerb?: string;
240
+ /** Display label for the custom page */
241
+ label?: string;
242
+ /** Translation key for the label */
243
+ labelKey?: string;
244
+ name: string;
245
+ // /** Name of the page (unique identifier) */
246
+ // name?: string;
247
+ /** Entry route definition for this custom page */
248
+ route?: PluginRouteRecordRaw | Object;
249
+ /** Icon for the custom page (relates to icons in https://github.com/rancher/icons) */
250
+ icon?: 'compass';
251
+ /** Whether this custom page is namespaced or not */
252
+ namespaced?: boolean;
253
+ /** Ordering weight for the custom page */
254
+ weight?: number;
255
+ /** Whether this custom page is exact match */
256
+ exact?: boolean;
257
+ /** Whether this custom page will act as an overview page */
258
+ overview?: boolean;
259
+ /** Whether this custom page has an exact path match */
260
+ 'exact-path'?: boolean;
261
+ }
1
262
 
2
263
  /**
3
264
  * interface for type-map's' definition for table headers/columns
@@ -11,7 +272,7 @@ export interface TableColumn {
11
272
  formatterOpts?: any,
12
273
  width?: number,
13
274
  tooltip?: string,
14
- search?: string | boolean,
275
+ search?: string | string[] | boolean,
15
276
  }
16
277
 
17
278
  export const COLUMN_BREAKPOINTS = {
@@ -100,6 +100,29 @@ describe('fleet-appco utils', () => {
100
100
  expect(onStateChange).toHaveBeenCalledWith(expect.objectContaining({ repoName: 'my-repo', error: false }));
101
101
  });
102
102
 
103
+ it('should wait for the repo using a 10 minute timeout and 3s interval (#14627)', async() => {
104
+ // A cold OCI pull can take minutes; the wait must not give up early.
105
+ const waitForTestFn = jest.fn((fn: () => boolean) => {
106
+ fn();
107
+
108
+ return Promise.resolve();
109
+ });
110
+ const repo = buildRepo({
111
+ status: { conditions: [{ type: 'OCIDownloaded', status: 'True' }] },
112
+ waitForTestFn,
113
+ });
114
+ const store = buildStore(repo);
115
+
116
+ await fetchAppCoCharts(store as any, 'my-repo');
117
+
118
+ expect(waitForTestFn).toHaveBeenCalledWith(
119
+ expect.any(Function),
120
+ expect.stringContaining('my-repo'),
121
+ 600000,
122
+ 3000,
123
+ );
124
+ });
125
+
103
126
  it('should return no entries and an error state when the repo reports an error', async() => {
104
127
  const repo = buildRepo({ metadata: { state: { error: true, message: 'boom' } } });
105
128
  const store = buildStore(repo);
package/utils/async.ts CHANGED
@@ -5,6 +5,8 @@ export const waitFor = (testFn: Function, msg = '', timeoutMs = 3000000, interva
5
5
  if (testFn()) {
6
6
  gatedLog('Wait for', msg || 'unknown', 'done immediately');
7
7
  resolve(this);
8
+
9
+ return;
8
10
  }
9
11
  const timeout = setTimeout(() => {
10
12
  gatedLog('Wait for', msg, 'timed out');
@@ -198,7 +198,12 @@ export async function ensureAppCoResources(
198
198
  return true;
199
199
  }
200
200
 
201
- const REPO_WAIT_TIMEOUT_MS = 90000;
201
+ // Cold/first OCI pulls of the AppCo catalog can take several minutes to download,
202
+ // so we allow a generous window before giving up. Genuine repo/OCI errors still
203
+ // short-circuit early via the `hasError` path below, so this only bounds how long
204
+ // we tolerate a repo that is still downloading. Added as 10 minutes to consider
205
+ // future increases on the size of the AppCo catalog.
206
+ const REPO_WAIT_TIMEOUT_MS = 600000; // 10 minutes
202
207
  const REPO_WAIT_INTERVAL_MS = 3000;
203
208
 
204
209
  function getRepoState(repo: any, repoName: string): { state: RepoState; isReady: boolean; hasError: boolean } {
package/utils/grafana.js CHANGED
@@ -1,6 +1,5 @@
1
- import { haveV2Monitoring } from '@shell/utils/monitoring';
1
+ import { haveV2Monitoring, fetchMonitoringVersion } from '@shell/utils/monitoring';
2
2
  import { parse as parseUrl, addParam } from '@shell/utils/url';
3
- import { CATALOG } from '@shell/config/types';
4
3
 
5
4
  // these two versions of monitoring included a bug fix attempt that required the local cluster to use a different url
6
5
  // the solution going forward doesn't require this, see https://github.com/rancher/dashboard/issues/8885
@@ -60,21 +59,7 @@ export async function dashboardExists(monitoringVersion, store, clusterId, embed
60
59
  }
61
60
 
62
61
  export async function allDashboardsExist(store, clusterId, embeddedUrls, storeName = 'cluster', projectId = null) {
63
- let res;
64
-
65
- let monitoringVersion = '';
66
-
67
- if (!projectId && store.getters[`${ storeName }/canList`](CATALOG.APP)) {
68
- try {
69
- res = await store.dispatch(`${ storeName }/find`, {
70
- type: CATALOG.APP,
71
- id: 'cattle-monitoring-system/rancher-monitoring'
72
- });
73
- } catch (err) {
74
- }
75
-
76
- monitoringVersion = res?.currentVersion;
77
- }
62
+ const monitoringVersion = projectId ? '' : await fetchMonitoringVersion(store, storeName);
78
63
 
79
64
  const existPromises = embeddedUrls.map((url) => dashboardExists(monitoringVersion, store, clusterId, url, storeName, projectId));
80
65
 
@@ -1,6 +1,6 @@
1
1
  // Helpers for determining if V2 or v1 Monitoring are installed
2
2
 
3
- import { SCHEMA, MONITORING, ENDPOINTS } from '@shell/config/types';
3
+ import { SCHEMA, MONITORING, ENDPOINTS, CATALOG } from '@shell/config/types';
4
4
  import { normalizeType } from '@shell/plugins/dashboard-store/normalize';
5
5
  import { findBy } from '@shell/utils/array';
6
6
  import { isEmpty } from '@shell/utils/object';
@@ -30,6 +30,43 @@ export function haveV2Monitoring(getters) {
30
30
 
31
31
  export const CATTLE_MONITORING_NAMESPACE = 'cattle-monitoring-system';
32
32
 
33
+ /**
34
+ * Look up the installed Rancher monitoring app, preferring the new
35
+ * `rancher-monitoring-dashboards` chart and falling back to the legacy
36
+ * `rancher-monitoring`. Returns null if the user lacks permission, neither
37
+ * chart is installed, or any lookup fails.
38
+ */
39
+ export async function fetchMonitoringApp(store, storeName) {
40
+ if (!store.getters[`${ storeName }/canList`](CATALOG.APP)) {
41
+ return null;
42
+ }
43
+
44
+ const ids = [
45
+ `${ CATTLE_MONITORING_NAMESPACE }/rancher-monitoring-dashboards`,
46
+ `${ CATTLE_MONITORING_NAMESPACE }/rancher-monitoring`,
47
+ ];
48
+
49
+ for (const id of ids) {
50
+ try {
51
+ const res = await store.dispatch(`${ storeName }/find`, { type: CATALOG.APP, id });
52
+
53
+ if (res) {
54
+ return res;
55
+ }
56
+ } catch (err) {
57
+ // Try the next id; both failing means nothing is installed.
58
+ }
59
+ }
60
+
61
+ return null;
62
+ }
63
+
64
+ export async function fetchMonitoringVersion(store, storeName) {
65
+ const app = await fetchMonitoringApp(store, storeName);
66
+
67
+ return app?.currentVersion || '';
68
+ }
69
+
33
70
  async function hasEndpointSubsets(store, id) {
34
71
  if (store.getters['cluster/schemaFor'](ENDPOINTS)) {
35
72
  const endpoints = await store.dispatch('cluster/findAll', { type: ENDPOINTS }) || [];
package/utils/sort.js CHANGED
@@ -184,7 +184,7 @@ export function parseField(str) {
184
184
  }
185
185
  }
186
186
 
187
- export function sortBy(ary, keys, desc) {
187
+ export function sortBy(ary, keys, desc = false) {
188
188
  if ( !Array.isArray(keys) ) {
189
189
  keys = [keys];
190
190
  }