@rancher/shell 0.4.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (243) hide show
  1. package/assets/images/providers/ovhcloudmks.svg +122 -0
  2. package/assets/images/providers/ovhcloudpubliccloud.svg +122 -0
  3. package/assets/styles/global/_layout.scss +99 -0
  4. package/assets/translations/en-us.yaml +30 -5
  5. package/assets/translations/zh-hans.yaml +1 -1
  6. package/babel.config.js +7 -1
  7. package/chart/monitoring/alerting/index.vue +7 -21
  8. package/chart/monitoring/grafana/index.vue +55 -0
  9. package/chart/monitoring/index.vue +51 -17
  10. package/chart/monitoring/prometheus/index.vue +37 -43
  11. package/chart/rancher-backup/index.vue +2 -1
  12. package/cloud-credential/azure.vue +4 -17
  13. package/components/Certificates.vue +164 -0
  14. package/components/CodeMirror.vue +19 -21
  15. package/components/CruResource.vue +1 -0
  16. package/components/EtcdInfoBanner.vue +1 -1
  17. package/components/ExplorerProjectsNamespaces.vue +25 -1
  18. package/components/IconOrSvg.vue +1 -1
  19. package/components/LandingPagePreference.vue +1 -4
  20. package/components/Questions/index.vue +1 -1
  21. package/components/ResourceDetail/Masthead.vue +16 -3
  22. package/components/ResourceTable.vue +14 -2
  23. package/components/ResourceYaml.vue +5 -0
  24. package/components/SideNav.vue +1 -1
  25. package/components/SingleClusterInfo.vue +1 -4
  26. package/components/Tabbed/index.vue +12 -0
  27. package/components/fleet/FleetRepos.vue +62 -27
  28. package/components/fleet/FleetResources.vue +6 -1
  29. package/components/form/ArrayListSelect.vue +10 -0
  30. package/components/form/KeyValue.vue +4 -0
  31. package/components/form/LabeledSelect.vue +4 -0
  32. package/components/formatter/Checked.vue +11 -3
  33. package/components/formatter/FleetClusterSummaryGraph.vue +27 -0
  34. package/components/formatter/FleetSummaryGraph.vue +23 -11
  35. package/components/formatter/LiveDuration.vue +1 -1
  36. package/components/formatter/PercentageBar.vue +1 -1
  37. package/components/formatter/__tests__/Checked.test.ts +19 -0
  38. package/components/nav/Group.vue +2 -2
  39. package/components/nav/Header.vue +0 -1
  40. package/components/nav/TopLevelMenu.vue +36 -6
  41. package/components/nav/Type.vue +1 -3
  42. package/components/nav/WindowManager/ContainerLogs.vue +101 -3
  43. package/components/nav/WindowManager/ContainerShell.vue +6 -1
  44. package/components/nav/WindowManager/__tests__/ContainerLogs.test.ts +186 -0
  45. package/components/nav/WindowManager/index.vue +11 -10
  46. package/components/nav/__tests__/TopLevelMenu.test.ts +33 -0
  47. package/components/nav/__tests__/Type.test.ts +1 -1
  48. package/components/nuxt/nuxt-child.js +14 -78
  49. package/components/nuxt/nuxt.js +1 -1
  50. package/{layouts → components/templates}/blank.vue +1 -1
  51. package/{layouts → components/templates}/default.vue +8 -98
  52. package/{layouts → components/templates}/error.vue +10 -19
  53. package/{layouts → components/templates}/home.vue +4 -1
  54. package/{layouts → components/templates}/plain.vue +4 -1
  55. package/{layouts → components/templates}/standalone.vue +1 -1
  56. package/{layouts → components/templates}/unauthenticated.vue +1 -1
  57. package/composables/useCompactInput.ts +20 -0
  58. package/composables/useLabeledFormElement.ts +138 -0
  59. package/config/harvester-manager-types.js +2 -0
  60. package/config/private-label.js +22 -0
  61. package/config/product/explorer.js +3 -0
  62. package/config/product/fleet.js +6 -1
  63. package/config/product/manager.js +8 -2
  64. package/config/query-params.js +1 -0
  65. package/config/router.js +385 -364
  66. package/config/settings.ts +1 -0
  67. package/config/store.js +1 -1
  68. package/config/system-namespaces.js +3 -0
  69. package/config/table-headers.js +47 -0
  70. package/core/plugin-routes.ts +56 -114
  71. package/core/plugin.ts +16 -10
  72. package/core/plugins-loader.js +7 -9
  73. package/core/plugins.js +0 -3
  74. package/creators/app/files/.gitlab-ci.yml +1 -1
  75. package/detail/fleet.cattle.io.cluster.vue +11 -1
  76. package/detail/provisioning.cattle.io.cluster.vue +4 -3
  77. package/dialog/ScaleMachineDownDialog.vue +34 -17
  78. package/edit/__tests__/service.test.ts +89 -0
  79. package/edit/auth/googleoauth.vue +1 -5
  80. package/edit/catalog.cattle.io.clusterrepo.vue +18 -0
  81. package/edit/cloudcredential.vue +2 -0
  82. package/edit/configmap.vue +2 -1
  83. package/edit/networking.k8s.io.networkpolicy/__tests__/PolicyRuleTarget.spec.ts +1 -1
  84. package/edit/provisioning.cattle.io.cluster/SelectCredential.vue +15 -7
  85. package/edit/provisioning.cattle.io.cluster/__tests__/Advanced.test.ts +112 -0
  86. package/edit/provisioning.cattle.io.cluster/__tests__/Basics.test.ts +473 -0
  87. package/edit/provisioning.cattle.io.cluster/__tests__/{CustomCommand.tests.ts → CustomCommand.test.ts} +4 -0
  88. package/edit/provisioning.cattle.io.cluster/__tests__/DrainOptions.test.ts +1 -1
  89. package/edit/provisioning.cattle.io.cluster/__tests__/index.test.ts +73 -0
  90. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +7 -1
  91. package/edit/provisioning.cattle.io.cluster/__tests__/utils/cluster.ts +386 -0
  92. package/edit/provisioning.cattle.io.cluster/import.vue +2 -2
  93. package/edit/provisioning.cattle.io.cluster/index.vue +92 -36
  94. package/edit/provisioning.cattle.io.cluster/rke2.vue +171 -583
  95. package/edit/provisioning.cattle.io.cluster/tabs/AddOnConfig.vue +137 -0
  96. package/edit/provisioning.cattle.io.cluster/tabs/Advanced.vue +157 -0
  97. package/edit/provisioning.cattle.io.cluster/{Basics.vue → tabs/Basics.vue} +94 -19
  98. package/edit/provisioning.cattle.io.cluster/{MachinePool.vue → tabs/MachinePool.vue} +1 -0
  99. package/edit/provisioning.cattle.io.cluster/tabs/etcd/index.vue +135 -0
  100. package/edit/provisioning.cattle.io.cluster/tabs/networking/index.vue +189 -0
  101. package/edit/provisioning.cattle.io.cluster/tabs/registries/index.vue +144 -0
  102. package/edit/provisioning.cattle.io.cluster/tabs/upgrade/index.vue +76 -0
  103. package/edit/service.vue +12 -0
  104. package/edit/workload/mixins/workload.js +1 -1
  105. package/initialize/App.js +25 -71
  106. package/initialize/client.js +21 -162
  107. package/initialize/index.js +27 -123
  108. package/list/management.cattle.io.feature.vue +1 -7
  109. package/list/node.vue +1 -0
  110. package/machine-config/__tests__/vmwarevsphere.test.ts +100 -21
  111. package/machine-config/vmwarevsphere.vue +73 -51
  112. package/middleware/authenticated.js +10 -17
  113. package/mixins/auth-config.js +2 -7
  114. package/mixins/brand.js +29 -41
  115. package/mixins/labeled-form-element.ts +6 -1
  116. package/models/__tests__/management.cattle.io.node.ts +85 -0
  117. package/models/__tests__/management.cattle.io.nodepool.ts +83 -0
  118. package/models/__tests__/namespace.test.ts +49 -9
  119. package/models/__tests__/workload.test.ts +91 -0
  120. package/models/cluster/node.js +4 -4
  121. package/models/cluster.x-k8s.io.machinedeployment.js +14 -0
  122. package/models/fleet.cattle.io.cluster.js +4 -0
  123. package/models/fleet.cattle.io.gitrepo.js +56 -13
  124. package/models/management.cattle.io.kontainerdriver.js +1 -1
  125. package/models/management.cattle.io.node.js +18 -14
  126. package/models/management.cattle.io.nodepool.js +17 -0
  127. package/models/namespace.js +1 -1
  128. package/models/pod.js +20 -0
  129. package/models/provisioning.cattle.io.cluster.js +20 -3
  130. package/models/secret.js +117 -18
  131. package/models/workload.js +16 -0
  132. package/models/workload.service.js +18 -0
  133. package/package.json +10 -9
  134. package/pages/about.vue +0 -1
  135. package/pages/account/create-key.vue +0 -1
  136. package/pages/account/index.vue +0 -1
  137. package/pages/auth/login.vue +0 -1
  138. package/pages/auth/logout.vue +0 -2
  139. package/pages/auth/setup.vue +0 -4
  140. package/pages/auth/verify.vue +14 -8
  141. package/pages/c/_cluster/apps/charts/install.vue +4 -4
  142. package/pages/c/_cluster/apps/index.vue +0 -2
  143. package/pages/c/_cluster/auth/index.vue +0 -2
  144. package/pages/c/_cluster/ecm/index.vue +0 -2
  145. package/pages/c/_cluster/explorer/index.vue +28 -2
  146. package/pages/c/_cluster/fleet/index.vue +1 -1
  147. package/pages/c/_cluster/index.vue +0 -2
  148. package/pages/c/_cluster/settings/banners.vue +0 -2
  149. package/pages/c/_cluster/settings/brand.vue +0 -2
  150. package/pages/c/_cluster/settings/index.vue +0 -2
  151. package/pages/c/_cluster/settings/links.vue +0 -1
  152. package/pages/c/_cluster/settings/performance.vue +0 -1
  153. package/pages/c/_cluster/uiplugins/CatalogList/CatalogLoadDialog.vue +2 -1
  154. package/pages/c/_cluster/uiplugins/CatalogList/index.vue +10 -46
  155. package/pages/c/_cluster/uiplugins/index.vue +0 -2
  156. package/pages/diagnostic.vue +1 -2
  157. package/pages/fail-whale.vue +0 -1
  158. package/pages/prefs.vue +0 -1
  159. package/pages/support/index.vue +2 -8
  160. package/pkg/auto-import.js +1 -1
  161. package/plugins/axios.js +0 -36
  162. package/plugins/back-button.js +3 -5
  163. package/plugins/codemirror-loader.js +1 -1
  164. package/plugins/codemirror.js +41 -0
  165. package/plugins/dashboard-store/__tests__/{mutations.spec.ts → mutations.test.ts} +1 -1
  166. package/plugins/dashboard-store/__tests__/resource-class.test.ts +49 -0
  167. package/plugins/dashboard-store/__tests__/utils/store-mocks.ts +7 -0
  168. package/plugins/dashboard-store/actions.js +30 -4
  169. package/plugins/dashboard-store/classify.js +1 -18
  170. package/plugins/dashboard-store/getters.js +10 -5
  171. package/plugins/dashboard-store/index.js +0 -12
  172. package/plugins/dashboard-store/mutations.js +0 -4
  173. package/plugins/dashboard-store/resource-class.js +59 -18
  174. package/plugins/steve/__tests__/steve-class.spec.ts +59 -0
  175. package/plugins/steve/__tests__/utils/steve-mocks.ts +31 -0
  176. package/plugins/steve/getters.js +4 -1
  177. package/plugins/steve/norman-class.js +19 -0
  178. package/plugins/steve/steve-class.js +22 -0
  179. package/plugins/steve/subscribe.js +4 -10
  180. package/rancher-components/Accordion/Accordion.test.ts +45 -0
  181. package/rancher-components/Accordion/Accordion.vue +85 -0
  182. package/rancher-components/Accordion/index.ts +1 -0
  183. package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +19 -2
  184. package/rancher-components/Form/LabeledInput/LabeledInput.vue +12 -1
  185. package/rancher-components/Form/Radio/RadioButton.test.ts +7 -3
  186. package/rancher-components/Form/Radio/RadioGroup.test.ts +30 -0
  187. package/rancher-components/Form/Radio/RadioGroup.vue +4 -0
  188. package/rancher-components/StringList/StringList.test.ts +270 -0
  189. package/rancher-components/StringList/StringList.vue +57 -18
  190. package/rancher-components/components/Accordion/Accordion.test.ts +45 -0
  191. package/rancher-components/components/Accordion/Accordion.vue +85 -0
  192. package/rancher-components/components/Accordion/index.ts +1 -0
  193. package/rancher-components/components/Form/LabeledInput/LabeledInput.test.ts +19 -2
  194. package/rancher-components/components/Form/LabeledInput/LabeledInput.vue +4 -1
  195. package/scripts/.gitlab/workflows/build-extension-catalog.gitlab-ci.yml +50 -0
  196. package/scripts/extension/parse-tag-name +2 -2
  197. package/scripts/publish-shell.sh +10 -0
  198. package/scripts/test-plugins-build.sh +85 -9
  199. package/server/har-file.js +183 -0
  200. package/store/catalog.js +1 -1
  201. package/store/features.js +1 -0
  202. package/store/i18n.js +11 -0
  203. package/store/index.js +10 -11
  204. package/store/prefs.js +33 -35
  205. package/store/type-map.js +8 -7
  206. package/tsconfig.json +35 -9
  207. package/tsconfig.paths.json +18 -0
  208. package/types/shell/index.d.ts +345 -214
  209. package/utils/__tests__/create-yaml.test.ts +60 -0
  210. package/utils/axios.js +0 -19
  211. package/utils/azure.js +24 -0
  212. package/utils/create-yaml.js +17 -10
  213. package/utils/monitoring.js +1 -1
  214. package/utils/nuxt.js +18 -39
  215. package/utils/object.js +14 -0
  216. package/utils/router.scrollBehavior.js +12 -14
  217. package/utils/time.js +1 -1
  218. package/utils/url.ts +1 -1
  219. package/vue.config.js +23 -2
  220. package/.DS_Store +0 -0
  221. package/assets/images/providers/aks-black.svg +0 -28
  222. package/assets/images/providers/aks.svg +0 -31
  223. package/edit/provisioning.cattle.io.cluster/__tests__/Basics.tests.ts +0 -234
  224. package/initialize/layouts.ts +0 -26
  225. package/mixins/fetch.server.js +0 -73
  226. package/pages/c/index.vue +0 -9
  227. package/pages/rio/mesh.vue +0 -508
  228. package/plugins/transitions.js +0 -4
  229. package/scripts/.DS_Store +0 -0
  230. package/scripts/verdaccio.log +0 -205
  231. package/tsconfig.default.json +0 -46
  232. package/yarn-error.log +0 -200
  233. /package/components/form/__tests__/{NameNsDescription.ts → NameNsDescription.test.ts} +0 -0
  234. /package/edit/networking.k8s.io.networkpolicy/__tests__/utils/{selectors.ts → selectors.test.ts} +0 -0
  235. /package/edit/provisioning.cattle.io.cluster/{AgentConfiguration.vue → tabs/AgentConfiguration.vue} +0 -0
  236. /package/edit/provisioning.cattle.io.cluster/{MemberRoles.vue → tabs/MemberRoles.vue} +0 -0
  237. /package/edit/provisioning.cattle.io.cluster/{S3Config.vue → tabs/etcd/S3Config.vue} +0 -0
  238. /package/edit/provisioning.cattle.io.cluster/{ACE.vue → tabs/networking/ACE.vue} +0 -0
  239. /package/edit/provisioning.cattle.io.cluster/{RegistryConfigs.vue → tabs/registries/RegistryConfigs.vue} +0 -0
  240. /package/edit/provisioning.cattle.io.cluster/{RegistryMirrors.vue → tabs/registries/RegistryMirrors.vue} +0 -0
  241. /package/edit/provisioning.cattle.io.cluster/{DrainOptions.vue → tabs/upgrade/DrainOptions.vue} +0 -0
  242. /package/plugins/dashboard-store/__tests__/{actions.spec.ts → actions.test.ts} +0 -0
  243. /package/plugins/dashboard-store/__tests__/{getters.spec.ts → getters.test.ts} +0 -0
package/mixins/brand.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import { mapGetters } from 'vuex';
2
2
  import { CATALOG, MANAGEMENT } from '@shell/config/types';
3
- import { getVendor } from '@shell/config/private-label';
4
3
  import { SETTING } from '@shell/config/settings';
5
4
  import { findBy } from '@shell/utils/array';
6
5
  import { createCssVars } from '@shell/utils/color';
7
6
  import { _ALL_IF_AUTHED } from '@shell/plugins/dashboard-store/actions';
7
+ import { setTitle } from '@shell/config/private-label';
8
8
 
9
9
  const cspAdaptorApp = ['rancher-csp-adapter', 'rancher-csp-billing-adapter'];
10
10
 
@@ -116,6 +116,7 @@ export default {
116
116
  if (this.linkColor) {
117
117
  this.setCustomColor(this.linkColor, 'link');
118
118
  }
119
+ this.setBodyClass();
119
120
  },
120
121
 
121
122
  cspAdapter(neu) {
@@ -142,7 +143,15 @@ export default {
142
143
  }
143
144
  }
144
145
  }
146
+ },
147
+ brand() {
148
+ this.setBodyClass();
145
149
  }
150
+
151
+ },
152
+ mounted() {
153
+ this.setBodyClass();
154
+ setTitle();
146
155
  },
147
156
  methods: {
148
157
  setCustomColor(color, name = 'primary') {
@@ -159,47 +168,26 @@ export default {
159
168
  for (const prop in vars) {
160
169
  document.body.style.removeProperty(prop);
161
170
  }
162
- }
163
- },
164
- head() {
165
- let cssClass = `overflow-hidden dashboard-body`;
166
- const out = {
167
- bodyAttrs: { class: `theme-${ this.theme } ${ cssClass }` },
168
- title: getVendor(),
169
- };
170
-
171
- if (getVendor() === 'Harvester') {
172
- const ico = require(`~shell/assets/images/pl/harvester.png`);
173
-
174
- out.title = 'Harvester';
175
- out.link = [{
176
- hid: 'icon',
177
- rel: 'icon',
178
- type: 'image/x-icon',
179
- href: ico
180
- }];
181
- }
182
-
183
- let brandMeta;
184
-
185
- if ( this.brand ) {
186
- try {
187
- brandMeta = require(`~shell/assets/brand/${ this.brand }/metadata.json`);
188
- } catch {
189
- return out;
171
+ },
172
+ setBodyClass() {
173
+ const body = document.getElementsByTagName('body')[0];
174
+ const cssClass = `overflow-hidden dashboard-body`;
175
+ let bodyClass = `theme-${ this.theme } ${ cssClass }`;
176
+
177
+ if ( this.brand ) {
178
+ try {
179
+ const brandMeta = require(`~shell/assets/brand/${ this.brand }/metadata.json`);
180
+
181
+ if (brandMeta?.hasStylesheet === 'true') {
182
+ bodyClass = `${ cssClass } ${ this.brand } theme-${ this.theme }`;
183
+ } else {
184
+ bodyClass = `theme-${ this.theme } overflow-hidden dashboard-body`;
185
+ this.$store.dispatch('prefs/setBrandStyle', this.theme === 'dark');
186
+ }
187
+ } catch {}
190
188
  }
189
+ body.className = bodyClass;
191
190
  }
192
-
193
- if (brandMeta?.hasStylesheet === 'true') {
194
- cssClass = `${ cssClass } ${ this.brand } theme-${ this.theme }`;
195
- } else {
196
- cssClass = `theme-${ this.theme } overflow-hidden dashboard-body`;
197
- this.$store.dispatch('prefs/setBrandStyle', this.theme === 'dark');
198
- }
199
-
200
- out.bodyAttrs.class = cssClass;
201
-
202
- return out;
203
- },
191
+ }
204
192
 
205
193
  };
@@ -82,6 +82,11 @@ export default Vue.extend({
82
82
  type: Array,
83
83
  // we only want functions in the rules array
84
84
  validator: (rules: any) => rules.every((rule: any) => ['function'].includes(typeof rule))
85
+ },
86
+
87
+ requireDirty: {
88
+ default: true,
89
+ type: Boolean
85
90
  }
86
91
  },
87
92
 
@@ -141,7 +146,7 @@ export default Vue.extend({
141
146
  ruleMessages.push(message);
142
147
  }
143
148
  }
144
- if (ruleMessages.length > 0 && (this.blurred || this.focused)) {
149
+ if (ruleMessages.length > 0 && (this.blurred || this.focused || !this.requireDirty)) {
145
150
  return ruleMessages.join(', ');
146
151
  } else {
147
152
  return undefined;
@@ -93,4 +93,89 @@ describe('class MgmtNode', () => {
93
93
  resetMocks();
94
94
  });
95
95
  });
96
+
97
+ describe('canScaleDown', () => {
98
+ const mgmtClusterId = 'test';
99
+ const nodeId = 'test/id';
100
+ const specs = {
101
+ worker: {
102
+ worker: true, etcd: false, controlPlane: false
103
+ },
104
+ etcd: {
105
+ worker: false, etcd: true, controlPlane: false
106
+ },
107
+ controlPlane: {
108
+ worker: false, etcd: false, controlPlane: true
109
+ },
110
+ etcdAndControlPlane: {
111
+ worker: false, etcd: true, controlPlane: true
112
+ },
113
+ all: {
114
+ worker: true, etcd: true, controlPlane: true
115
+ }
116
+ };
117
+
118
+ const workerNode = {
119
+ id: '01',
120
+ isWorker: true,
121
+ isControlPlane: false,
122
+ isEtcd: false
123
+ };
124
+ const etcdNode = {
125
+ id: '02',
126
+ isWorker: false,
127
+ isControlPlane: false,
128
+ isEtcd: true
129
+ };
130
+ const controlPlaneNode = {
131
+ id: '03',
132
+ isWorker: false,
133
+ isControlPlane: true,
134
+ isEtcd: false
135
+ };
136
+ const etcdAndControlPlaneNode = {
137
+ id: '03',
138
+ isWorker: false,
139
+ isControlPlane: true,
140
+ isEtcd: true
141
+ };
142
+ const allNode = {
143
+ id: '04',
144
+ isWorker: true,
145
+ isControlPlane: true,
146
+ isEtcd: true
147
+ };
148
+
149
+ const baseCtx = {
150
+ rootGetters: {
151
+ 'rancher/byId': () => ({ actions: { scaledown: 'scaledown' } }),
152
+ 'i18n/t': t
153
+ }
154
+ };
155
+
156
+ it.each([
157
+ [{ spec: specs.worker, nodes: [workerNode] }, true],
158
+ [{ spec: specs.etcd, nodes: [etcdNode, etcdNode, controlPlaneNode] }, true],
159
+ [{ spec: specs.etcdAndControlPlane, nodes: [etcdAndControlPlaneNode, etcdAndControlPlaneNode] }, true],
160
+ [{ spec: specs.etcdAndControlPlane, nodes: [etcdAndControlPlaneNode] }, false],
161
+ [{ spec: specs.all, nodes: [allNode] }, false],
162
+ [{ spec: specs.all, nodes: [allNode, allNode] }, true],
163
+ ])('should return canScaleDown properly', (data, expected) => {
164
+ const { spec, nodes } = data;
165
+ const mgmtNode = new MgmtNode({
166
+ spec,
167
+ id: nodeId
168
+ }, {
169
+ ...baseCtx,
170
+ getters: {
171
+ all: () => [{
172
+ mgmtClusterId,
173
+ nodes
174
+ }]
175
+ }
176
+ });
177
+
178
+ expect(mgmtNode.canScaleDown).toStrictEqual(expected);
179
+ });
180
+ });
96
181
  });
@@ -0,0 +1,83 @@
1
+ import MgmtNodePool from '@shell/models/management.cattle.io.nodepool';
2
+
3
+ describe('class MgmtNodePool', () => {
4
+ describe('canScaleDownPool', () => {
5
+ const mgmtClusterId = 'test';
6
+ const nodeId = 'test/id';
7
+ const specs = {
8
+ worker: {
9
+ worker: true, etcd: false, controlPlane: false
10
+ },
11
+ etcd: {
12
+ worker: false, etcd: true, controlPlane: false
13
+ },
14
+ controlPlane: {
15
+ worker: false, etcd: false, controlPlane: true
16
+ },
17
+ etcdAndControlPlane: {
18
+ worker: false, etcd: true, controlPlane: true
19
+ },
20
+ all: {
21
+ worker: true, etcd: true, controlPlane: true
22
+ }
23
+ };
24
+
25
+ const workerNode = {
26
+ id: '01',
27
+ isWorker: true,
28
+ isControlPlane: false,
29
+ isEtcd: false
30
+ };
31
+ const etcdNode = {
32
+ id: '02',
33
+ isWorker: false,
34
+ isControlPlane: false,
35
+ isEtcd: true
36
+ };
37
+ const controlPlaneNode = {
38
+ id: '03',
39
+ isWorker: false,
40
+ isControlPlane: true,
41
+ isEtcd: false
42
+ };
43
+ const etcdAndControlPlaneNode = {
44
+ id: '03',
45
+ isWorker: false,
46
+ isControlPlane: true,
47
+ isEtcd: true
48
+ };
49
+ const allNode = {
50
+ id: '04',
51
+ isWorker: true,
52
+ isControlPlane: true,
53
+ isEtcd: true
54
+ };
55
+
56
+ const baseCtx = { rootGetters: { 'rancher/byId': () => ({ actions: { scaledown: 'scaledown' } }) } };
57
+
58
+ it.each([
59
+ [{ spec: specs.worker, nodes: [workerNode] }, true],
60
+ [{ spec: specs.etcd, nodes: [etcdNode, etcdNode, controlPlaneNode] }, true],
61
+ [{ spec: specs.etcdAndControlPlane, nodes: [etcdAndControlPlaneNode, etcdAndControlPlaneNode] }, true],
62
+ [{ spec: specs.etcdAndControlPlane, nodes: [etcdAndControlPlaneNode] }, false],
63
+ [{ spec: specs.all, nodes: [allNode] }, false],
64
+ [{ spec: specs.all, nodes: [allNode, allNode] }, true],
65
+ ])('should return canScaleDownPool properly', (data, expected) => {
66
+ const { spec, nodes } = data;
67
+ const mgmtNode = new MgmtNodePool({
68
+ spec,
69
+ id: nodeId
70
+ }, {
71
+ ...baseCtx,
72
+ getters: {
73
+ all: () => [{
74
+ mgmtClusterId,
75
+ nodes
76
+ }]
77
+ }
78
+ });
79
+
80
+ expect(mgmtNode.canScaleDownPool()).toStrictEqual(expected);
81
+ });
82
+ });
83
+ });
@@ -1,11 +1,48 @@
1
1
  import Namespace from '@shell/models/namespace';
2
+ import { SYSTEM_NAMESPACE } from '@shell/config/labels-annotations';
3
+ import SYSTEM_NAMESPACES from '@shell/config/system-namespaces';
2
4
 
3
5
  describe('class Namespace', () => {
4
6
  describe('checking if isSystem', () => {
5
7
  it.each([
6
- ['c-whatever-system', true],
7
- ['whatever', false]
8
- ])('should return true if end with "-system', (name, expectation) => {
8
+ ['whatever1', SYSTEM_NAMESPACE, true],
9
+ ['whatever2', 'any-annotation', false],
10
+ ['whatever3', '', false]
11
+ ])('should return true if it has the correct annotation', (name, annotation, expectation) => {
12
+ const namespace = new Namespace({});
13
+
14
+ namespace.metadata = { ...namespace.metadata, name };
15
+
16
+ if (annotation) {
17
+ namespace.metadata.annotations = { [annotation]: 'true' };
18
+ }
19
+
20
+ expect(namespace.isSystem).toBe(expectation);
21
+ });
22
+
23
+ const assertionsArr = [];
24
+
25
+ SYSTEM_NAMESPACES.forEach((ns) => {
26
+ assertionsArr.push([ns, true]);
27
+ });
28
+
29
+ assertionsArr.push(['c-whatever-system', false]);
30
+ assertionsArr.push(['cattle-whatever', false]);
31
+ assertionsArr.push(['', false]);
32
+
33
+ it.each(assertionsArr)('should return true if it belongs to the curated list of namespaces', (name, expectation) => {
34
+ const namespace = new Namespace({});
35
+
36
+ namespace.metadata = { ...namespace.metadata, name };
37
+
38
+ expect(namespace.isSystem).toBe(expectation);
39
+ });
40
+
41
+ it.each([
42
+ ['cattle-c-whatever-system', true],
43
+ ['cattle-whatever', false],
44
+ ['c-whatever-system', false]
45
+ ])('should return true if starts with "cattle-" end with "-system', (name, expectation) => {
9
46
  const namespace = new Namespace({});
10
47
 
11
48
  namespace.metadata = { ...namespace.metadata, name };
@@ -18,18 +55,21 @@ describe('class Namespace', () => {
18
55
 
19
56
  describe('checking if isObscure', () => {
20
57
  it.each([
21
- ['c-whatever-system', true],
22
- ['whatever', false],
23
- ['', false]
24
- ])('should return a value if has or not a name in the metadata', (name, expectation) => {
58
+ ['c-whatever-system', 'management.cattle.io/system-namespace', true],
59
+ ['p-whatever', SYSTEM_NAMESPACE, true],
60
+ ['p-whatever', '', false],
61
+ ['', '', false]
62
+ ])('should return a value if is system AND has the correct prefix', (name, annotation, expectation) => {
25
63
  const namespace = new Namespace({});
26
64
 
27
65
  namespace.metadata = { ...namespace.metadata, name };
28
66
 
67
+ if (annotation) {
68
+ namespace.metadata.annotations = { [annotation]: 'true' };
69
+ }
70
+
29
71
  expect(namespace.isObscure).toBe(expectation);
30
72
  });
31
-
32
- it.todo('should return a value if is or not system');
33
73
  });
34
74
 
35
75
  it.each([
@@ -0,0 +1,91 @@
1
+ import Workload from '@shell/models/workload.js';
2
+ import { steveClassJunkObject } from '@shell/plugins/steve/__tests__/utils/steve-mocks';
3
+
4
+ describe('class: Workload', () => {
5
+ describe('given custom workload keys', () => {
6
+ const customContainerImage = 'image';
7
+ const customContainer = {
8
+ image: customContainerImage,
9
+ __active: 'whatever',
10
+ active: 'whatever',
11
+ _init: 'whatever',
12
+ error: 'whatever',
13
+ };
14
+ const customWorkload = {
15
+ ...steveClassJunkObject,
16
+ type: '123abv',
17
+ __rehydrate: 'whatever',
18
+ __clone: 'whatever',
19
+ spec: {
20
+ template: {
21
+ spec: {
22
+ containers: [customContainer],
23
+ initContainers: [customContainer],
24
+ }
25
+ }
26
+ }
27
+ };
28
+
29
+ customWorkload.metadata.name = 'abc';
30
+
31
+ it('should keep internal keys', () => {
32
+ const workload = new Workload(customWorkload, {
33
+ getters: { schemaFor: () => ({ linkFor: jest.fn() }) },
34
+ dispatch: jest.fn(),
35
+ rootGetters: { 'i18n/t': jest.fn() },
36
+ });
37
+
38
+ expect({ ...workload }).toStrictEqual(customWorkload);
39
+ });
40
+
41
+ describe('method: save', () => {
42
+ it('should remove all the internal keys', async() => {
43
+ const dispatch = jest.fn();
44
+ const workload = new Workload(customWorkload, {
45
+ getters: { schemaFor: () => ({ linkFor: jest.fn() }) },
46
+ dispatch,
47
+ rootGetters: {
48
+ 'i18n/t': jest.fn(),
49
+ 'i18n/exists': () => true,
50
+ },
51
+ });
52
+ const expectation = {
53
+ metadata: {
54
+ name: 'abc',
55
+ fields: 'whatever',
56
+ resourceVersion: 'whatever',
57
+ clusterName: 'whatever',
58
+ deletionGracePeriodSeconds: 'whatever',
59
+ generateName: 'whatever',
60
+ },
61
+ spec: {
62
+ template: {
63
+ spec: {
64
+ containers: [{ image: customContainerImage }],
65
+ initContainers: [{ image: customContainerImage }]
66
+ }
67
+ }
68
+ }
69
+ };
70
+
71
+ await workload.save();
72
+
73
+ const opt = {
74
+ data: expectation,
75
+ headers: {
76
+ accept: 'application/json',
77
+ 'content-type': 'application/json',
78
+ },
79
+ method: 'post',
80
+ url: undefined,
81
+ };
82
+
83
+ // Data sent should have been cleaned
84
+ expect(dispatch).toHaveBeenCalledWith('request', { opt, type: customWorkload.type });
85
+
86
+ // Original workload model should remain unchanged
87
+ expect({ ...workload }).toStrictEqual(customWorkload);
88
+ });
89
+ });
90
+ });
91
+ });
@@ -187,7 +187,7 @@ export default class ClusterNode extends SteveModel {
187
187
  }
188
188
 
189
189
  get cpuCapacity() {
190
- return parseSi(this.status.allocatable.cpu);
190
+ return parseSi(this.status.allocatable?.cpu);
191
191
  }
192
192
 
193
193
  get cpuUsagePercentage() {
@@ -203,7 +203,7 @@ export default class ClusterNode extends SteveModel {
203
203
  }
204
204
 
205
205
  get ramCapacity() {
206
- return parseSi(this.status.capacity.memory);
206
+ return parseSi(this.status.capacity?.memory);
207
207
  }
208
208
 
209
209
  get ramUsagePercentage() {
@@ -219,7 +219,7 @@ export default class ClusterNode extends SteveModel {
219
219
  }
220
220
 
221
221
  get podUsage() {
222
- return calculatePercentage(this.status.allocatable.pods, this.status.capacity.pods);
222
+ return calculatePercentage(this.status.allocatable?.pods, this.status.capacity?.pods);
223
223
  }
224
224
 
225
225
  get podConsumedUsage() {
@@ -227,7 +227,7 @@ export default class ClusterNode extends SteveModel {
227
227
  }
228
228
 
229
229
  get podCapacity() {
230
- return Number.parseInt(this.status.capacity.pods);
230
+ return Number.parseInt(this.status.capacity?.pods);
231
231
  }
232
232
 
233
233
  get podConsumed() {
@@ -7,6 +7,7 @@ import { handleConflict } from '@shell/plugins/dashboard-store/normalize';
7
7
  import { MACHINE_ROLES } from '@shell/config/labels-annotations';
8
8
  import { notOnlyOfRole } from '@shell/models/cluster.x-k8s.io.machine';
9
9
  import { KIND } from '../config/elemental-types';
10
+ import { KIND as HARVESTER_KIND } from '../config/harvester-manager-types';
10
11
 
11
12
  export default class CapiMachineDeployment extends SteveModel {
12
13
  get cluster() {
@@ -69,6 +70,19 @@ export default class CapiMachineDeployment extends SteveModel {
69
70
  return this.template?.providerSize || this.t('node.list.poolDescription.noSize');
70
71
  }
71
72
 
73
+ get providerSummary() {
74
+ if (this.template) {
75
+ switch (this.infrastructureRefKind) {
76
+ case HARVESTER_KIND.MACHINE_TEMPLATE:
77
+ return null;
78
+ default:
79
+ return `${ this.providerDisplay } \u2013 ${ this.providerLocation } / ${ this.providerSize } (${ this.providerName })`;
80
+ }
81
+ }
82
+
83
+ return null;
84
+ }
85
+
72
86
  get desired() {
73
87
  return this.spec?.replicas || 0;
74
88
  }
@@ -107,6 +107,10 @@ export default class FleetCluster extends SteveModel {
107
107
  return this.metadata?.labels?.[FLEET_LABELS.CLUSTER_DISPLAY_NAME] || this.metadata?.name || this.id;
108
108
  }
109
109
 
110
+ get name() {
111
+ return this.metadata?.name || this.metadata?.labels?.[FLEET_LABELS.CLUSTER_NAME];
112
+ }
113
+
110
114
  get state() {
111
115
  if (this.spec?.paused === true) {
112
116
  return 'paused';
@@ -7,7 +7,9 @@ import { FLEET as FLEET_ANNOTATIONS } from '@shell/config/labels-annotations';
7
7
  import { addObject, addObjects, findBy, insertAt } from '@shell/utils/array';
8
8
  import { set } from '@shell/utils/object';
9
9
  import SteveModel from '@shell/plugins/steve/steve-class';
10
- import { STATES_ENUM, colorForState, stateDisplay, stateSort } from '@shell/plugins/dashboard-store/resource-class';
10
+ import {
11
+ STATES_ENUM, colorForState, mapStateToEnum, primaryDisplayStatusFromCount, stateDisplay, stateSort
12
+ } from '@shell/plugins/dashboard-store/resource-class';
11
13
  import { NAME } from '@shell/config/product/explorer';
12
14
 
13
15
  function quacksLikeAHash(str) {
@@ -203,17 +205,6 @@ export default class GitRepo extends SteveModel {
203
205
  return hash;
204
206
  }
205
207
 
206
- get clusterInfo() {
207
- const ready = this.status?.readyClusters || 0;
208
- const total = this.status?.desiredReadyClusters || 0;
209
-
210
- return {
211
- ready,
212
- unready: total - ready,
213
- total,
214
- };
215
- }
216
-
217
208
  get targetInfo() {
218
209
  let mode = null;
219
210
  let cluster = null;
@@ -384,8 +375,9 @@ export default class GitRepo extends SteveModel {
384
375
  namespace: r.namespace,
385
376
  name: r.name,
386
377
  clusterId: c.id,
378
+ clusterLabel: c.metadata.labels[FLEET_ANNOTATIONS.CLUSTER_NAME],
387
379
  clusterName: c.nameDisplay,
388
- state,
380
+ state: mapStateToEnum(state),
389
381
  stateBackground: color,
390
382
  stateDisplay: display,
391
383
  stateSort: stateSort(color, display),
@@ -401,6 +393,57 @@ export default class GitRepo extends SteveModel {
401
393
  return out;
402
394
  }
403
395
 
396
+ get clusterInfo() {
397
+ const ready = this.status?.readyClusters || 0;
398
+ const total = this.status?.desiredReadyClusters || 0;
399
+
400
+ return {
401
+ ready,
402
+ unready: total - ready,
403
+ total,
404
+ };
405
+ }
406
+
407
+ get clusterResourceStatus() {
408
+ const clusterStatuses = this.resourcesStatuses.reduce((prev, curr) => {
409
+ const { clusterId, clusterLabel } = curr;
410
+
411
+ const state = curr.state;
412
+
413
+ if (!prev[clusterId]) {
414
+ prev[clusterId] = {
415
+ clusterLabel,
416
+ resourceCounts: { [state]: 0, desiredReady: 0 }
417
+
418
+ };
419
+ }
420
+
421
+ if (!prev[clusterId].resourceCounts[state]) {
422
+ prev[clusterId].resourceCounts[state] = 0;
423
+ }
424
+
425
+ prev[clusterId].resourceCounts[state] += 1;
426
+ prev[clusterId].resourceCounts.desiredReady += 1;
427
+
428
+ return prev;
429
+ }, {});
430
+
431
+ const values = Object.keys(clusterStatuses).map((key) => {
432
+ const { clusterLabel, resourceCounts } = clusterStatuses[key];
433
+
434
+ return {
435
+ clusterId: key,
436
+ clusterLabel, // FLEET LABEL
437
+ status: {
438
+ displayStatus: primaryDisplayStatusFromCount(resourceCounts),
439
+ resourceCounts: { ...resourceCounts }
440
+ }
441
+ };
442
+ });
443
+
444
+ return values;
445
+ }
446
+
404
447
  get clustersList() {
405
448
  return this.$getters['all'](FLEET.CLUSTER);
406
449
  }
@@ -1,6 +1,6 @@
1
1
  import HybridModel from '@shell/plugins/steve/hybrid-class';
2
2
 
3
- const HIDDEN = ['rke', 'rancherkubernetesengine'];
3
+ const HIDDEN = ['rke', 'rancherkubernetesengine', 'azureaks'];
4
4
  const V2 = ['amazoneks', 'googlegke', 'azureaks'];
5
5
  const IMPORTABLE = ['amazoneks', 'googlegke', 'azureaks'];
6
6