@rancher/shell 0.3.29 → 0.5.0

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 (301) 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 +31 -6
  5. package/assets/translations/zh-hans.yaml +2 -2
  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/AsyncButton.vue +17 -5
  14. package/components/Certificates.vue +164 -0
  15. package/components/CodeMirror.vue +19 -21
  16. package/components/CopyCode.vue +6 -2
  17. package/components/CopyToClipboard.vue +2 -1
  18. package/components/CopyToClipboardText.vue +14 -9
  19. package/components/CruResource.vue +1 -0
  20. package/components/DraggableZone.vue +2 -2
  21. package/components/EtcdInfoBanner.vue +5 -5
  22. package/components/ExplorerProjectsNamespaces.vue +25 -1
  23. package/components/IconOrSvg.vue +1 -1
  24. package/components/LandingPagePreference.vue +1 -4
  25. package/components/Markdown.vue +16 -12
  26. package/components/PodSecurityAdmission.vue +2 -2
  27. package/components/Questions/index.vue +1 -1
  28. package/components/ResourceDetail/Masthead.vue +25 -9
  29. package/components/ResourceTable.vue +14 -2
  30. package/components/ResourceYaml.vue +5 -0
  31. package/components/SideNav.vue +1 -1
  32. package/components/SingleClusterInfo.vue +1 -4
  33. package/components/StatusTable.vue +5 -1
  34. package/components/Tabbed/index.vue +12 -0
  35. package/components/__tests__/CopyCode.test.ts +5 -4
  36. package/components/fleet/FleetBundles.vue +5 -11
  37. package/components/fleet/FleetRepos.vue +62 -27
  38. package/components/fleet/FleetResources.vue +6 -1
  39. package/components/fleet/FleetSummary.vue +3 -3
  40. package/components/fleet/__tests__/FleetSummary.test.ts +316 -0
  41. package/components/form/ArrayListSelect.vue +10 -0
  42. package/components/form/Error.vue +3 -3
  43. package/components/form/Footer.vue +2 -2
  44. package/components/form/GitPicker.vue +83 -38
  45. package/components/form/KeyValue.vue +4 -0
  46. package/components/form/LabeledSelect.vue +4 -0
  47. package/components/form/Password.vue +3 -1
  48. package/components/formatter/Checked.vue +11 -3
  49. package/components/formatter/FleetClusterSummaryGraph.vue +27 -0
  50. package/components/formatter/FleetSummaryGraph.vue +23 -11
  51. package/components/formatter/LiveDuration.vue +1 -1
  52. package/components/formatter/PercentageBar.vue +1 -1
  53. package/components/formatter/__tests__/Checked.test.ts +19 -0
  54. package/components/nav/Group.vue +2 -2
  55. package/components/nav/Header.vue +1 -2
  56. package/components/nav/TopLevelMenu.vue +36 -6
  57. package/components/nav/Type.vue +1 -3
  58. package/components/nav/WindowManager/ContainerLogs.vue +101 -3
  59. package/components/nav/WindowManager/ContainerShell.vue +6 -1
  60. package/components/nav/WindowManager/__tests__/ContainerLogs.test.ts +186 -0
  61. package/components/nav/WindowManager/index.vue +11 -10
  62. package/components/nav/__tests__/TopLevelMenu.test.ts +33 -0
  63. package/components/nav/__tests__/Type.test.ts +1 -1
  64. package/components/nuxt/nuxt-child.js +14 -78
  65. package/components/nuxt/nuxt.js +1 -1
  66. package/{layouts → components/templates}/blank.vue +1 -1
  67. package/{layouts → components/templates}/default.vue +8 -98
  68. package/{layouts → components/templates}/error.vue +10 -19
  69. package/{layouts → components/templates}/home.vue +4 -1
  70. package/{layouts → components/templates}/plain.vue +4 -1
  71. package/{layouts → components/templates}/standalone.vue +1 -1
  72. package/{layouts → components/templates}/unauthenticated.vue +1 -1
  73. package/composables/useCompactInput.test.ts +36 -0
  74. package/composables/useCompactInput.ts +20 -0
  75. package/composables/useLabeledFormElement.test.ts +135 -0
  76. package/composables/useLabeledFormElement.ts +138 -0
  77. package/config/harvester-manager-types.js +2 -0
  78. package/config/home-links.js +1 -1
  79. package/config/private-label.js +22 -0
  80. package/config/product/explorer.js +3 -0
  81. package/config/product/fleet.js +6 -1
  82. package/config/product/manager.js +8 -2
  83. package/config/query-params.js +1 -0
  84. package/config/router.js +385 -364
  85. package/config/settings.ts +1 -0
  86. package/config/store.js +1 -1
  87. package/config/system-namespaces.js +3 -0
  88. package/config/table-headers.js +47 -0
  89. package/core/plugin-helpers.js +3 -5
  90. package/core/plugin-routes.ts +56 -114
  91. package/core/plugin.ts +16 -10
  92. package/core/plugins-loader.js +7 -9
  93. package/core/plugins.js +0 -3
  94. package/creators/app/files/.gitlab-ci.yml +14 -0
  95. package/creators/app/init +19 -0
  96. package/detail/fleet.cattle.io.cluster.vue +11 -1
  97. package/detail/provisioning.cattle.io.cluster.vue +4 -3
  98. package/dialog/ScaleMachineDownDialog.vue +34 -17
  99. package/edit/__tests__/service.test.ts +89 -0
  100. package/edit/auth/googleoauth.vue +1 -5
  101. package/edit/cloudcredential.vue +2 -0
  102. package/edit/configmap.vue +2 -1
  103. package/edit/management.cattle.io.podsecurityadmissionconfigurationtemplate.vue +2 -2
  104. package/edit/monitoring.coreos.com.prometheusrule/AlertingRule.vue +12 -3
  105. package/edit/monitoring.coreos.com.prometheusrule/GroupRules.vue +2 -1
  106. package/edit/networking.k8s.io.networkpolicy/__tests__/PolicyRuleTarget.spec.ts +1 -1
  107. package/edit/provisioning.cattle.io.cluster/SelectCredential.vue +15 -7
  108. package/edit/provisioning.cattle.io.cluster/__tests__/Advanced.test.ts +112 -0
  109. package/edit/provisioning.cattle.io.cluster/__tests__/Basics.test.ts +473 -0
  110. package/edit/provisioning.cattle.io.cluster/__tests__/{CustomCommand.tests.ts → CustomCommand.test.ts} +6 -0
  111. package/edit/provisioning.cattle.io.cluster/__tests__/DrainOptions.test.ts +1 -1
  112. package/edit/provisioning.cattle.io.cluster/__tests__/index.test.ts +73 -0
  113. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +7 -1
  114. package/edit/provisioning.cattle.io.cluster/__tests__/utils/cluster.ts +386 -0
  115. package/edit/provisioning.cattle.io.cluster/import.vue +2 -2
  116. package/edit/provisioning.cattle.io.cluster/index.vue +92 -36
  117. package/edit/provisioning.cattle.io.cluster/rke2.vue +171 -583
  118. package/edit/provisioning.cattle.io.cluster/tabs/AddOnConfig.vue +137 -0
  119. package/edit/provisioning.cattle.io.cluster/tabs/Advanced.vue +157 -0
  120. package/edit/provisioning.cattle.io.cluster/{Basics.vue → tabs/Basics.vue} +94 -19
  121. package/edit/provisioning.cattle.io.cluster/{MachinePool.vue → tabs/MachinePool.vue} +1 -0
  122. package/edit/provisioning.cattle.io.cluster/tabs/etcd/index.vue +135 -0
  123. package/edit/provisioning.cattle.io.cluster/tabs/networking/index.vue +189 -0
  124. package/edit/provisioning.cattle.io.cluster/tabs/registries/index.vue +144 -0
  125. package/edit/provisioning.cattle.io.cluster/tabs/upgrade/index.vue +76 -0
  126. package/edit/service.vue +12 -0
  127. package/edit/workload/Upgrading.vue +3 -2
  128. package/edit/workload/mixins/workload.js +1 -1
  129. package/edit/workload/storage/persistentVolumeClaim/persistentvolumeclaim.vue +2 -1
  130. package/initialize/App.js +25 -71
  131. package/initialize/client.js +21 -162
  132. package/initialize/index.js +47 -124
  133. package/list/management.cattle.io.feature.vue +1 -7
  134. package/list/node.vue +1 -0
  135. package/machine-config/__tests__/vmwarevsphere.test.ts +100 -21
  136. package/machine-config/vmwarevsphere.vue +73 -51
  137. package/middleware/authenticated.js +10 -17
  138. package/mixins/auth-config.js +2 -7
  139. package/mixins/brand.js +29 -41
  140. package/mixins/create-edit-view/index.js +2 -2
  141. package/mixins/labeled-form-element.ts +6 -1
  142. package/models/__tests__/management.cattle.io.cluster.test.ts +4 -0
  143. package/models/__tests__/management.cattle.io.node.ts +85 -0
  144. package/models/__tests__/management.cattle.io.nodepool.ts +83 -0
  145. package/models/__tests__/namespace.test.ts +49 -9
  146. package/models/__tests__/workload.test.ts +91 -0
  147. package/models/cluster/node.js +4 -4
  148. package/models/cluster.x-k8s.io.machinedeployment.js +14 -0
  149. package/models/fleet.cattle.io.cluster.js +4 -0
  150. package/models/fleet.cattle.io.gitrepo.js +56 -13
  151. package/models/management.cattle.io.cluster.js +7 -3
  152. package/models/management.cattle.io.kontainerdriver.js +1 -1
  153. package/models/management.cattle.io.node.js +18 -14
  154. package/models/management.cattle.io.nodepool.js +17 -0
  155. package/models/namespace.js +1 -1
  156. package/models/pod.js +20 -0
  157. package/models/provisioning.cattle.io.cluster.js +39 -4
  158. package/models/secret.js +117 -18
  159. package/models/workload.js +16 -0
  160. package/models/workload.service.js +18 -0
  161. package/package.json +11 -10
  162. package/pages/about.vue +0 -1
  163. package/pages/account/create-key.vue +0 -1
  164. package/pages/account/index.vue +0 -1
  165. package/pages/auth/login.vue +0 -1
  166. package/pages/auth/logout.vue +0 -2
  167. package/pages/auth/setup.vue +0 -4
  168. package/pages/auth/verify.vue +14 -8
  169. package/pages/c/_cluster/apps/charts/index.vue +64 -43
  170. package/pages/c/_cluster/apps/charts/install.vue +4 -4
  171. package/pages/c/_cluster/apps/index.vue +0 -2
  172. package/pages/c/_cluster/auth/index.vue +0 -2
  173. package/pages/c/_cluster/ecm/index.vue +0 -2
  174. package/pages/c/_cluster/explorer/index.vue +28 -2
  175. package/pages/c/_cluster/fleet/index.vue +1 -1
  176. package/pages/c/_cluster/index.vue +0 -2
  177. package/pages/c/_cluster/settings/banners.vue +0 -2
  178. package/pages/c/_cluster/settings/brand.vue +0 -2
  179. package/pages/c/_cluster/settings/index.vue +0 -2
  180. package/pages/c/_cluster/settings/links.vue +0 -1
  181. package/pages/c/_cluster/settings/performance.vue +0 -1
  182. package/pages/c/_cluster/uiplugins/CatalogList/CatalogLoadDialog.vue +2 -1
  183. package/pages/c/_cluster/uiplugins/CatalogList/index.vue +10 -46
  184. package/pages/c/_cluster/uiplugins/index.vue +0 -2
  185. package/pages/diagnostic.vue +1 -2
  186. package/pages/fail-whale.vue +0 -1
  187. package/pages/prefs.vue +0 -1
  188. package/pages/support/index.vue +2 -8
  189. package/pkg/auto-import.js +1 -1
  190. package/plugins/axios.js +0 -36
  191. package/plugins/back-button.js +3 -5
  192. package/plugins/clean-html-directive.js +1 -19
  193. package/plugins/clean-html.js +53 -0
  194. package/plugins/clean-tooltip-directive.js +1 -1
  195. package/plugins/codemirror-loader.js +1 -1
  196. package/plugins/codemirror.js +41 -0
  197. package/plugins/dashboard-store/__tests__/{mutations.spec.ts → mutations.test.ts} +1 -1
  198. package/plugins/dashboard-store/__tests__/resource-class.test.ts +49 -0
  199. package/plugins/dashboard-store/__tests__/utils/store-mocks.ts +7 -0
  200. package/plugins/dashboard-store/actions.js +30 -4
  201. package/plugins/dashboard-store/classify.js +1 -18
  202. package/plugins/dashboard-store/getters.js +10 -5
  203. package/plugins/dashboard-store/index.js +0 -12
  204. package/plugins/dashboard-store/mutations.js +0 -4
  205. package/plugins/dashboard-store/resource-class.js +59 -18
  206. package/plugins/index.js +11 -0
  207. package/plugins/steve/__tests__/steve-class.spec.ts +59 -0
  208. package/plugins/steve/__tests__/utils/steve-mocks.ts +31 -0
  209. package/plugins/steve/getters.js +4 -1
  210. package/plugins/steve/norman-class.js +19 -0
  211. package/plugins/steve/steve-class.js +22 -0
  212. package/plugins/steve/subscribe.js +4 -10
  213. package/rancher-components/Accordion/Accordion.test.ts +45 -0
  214. package/rancher-components/Accordion/Accordion.vue +86 -0
  215. package/rancher-components/Accordion/index.ts +1 -0
  216. package/rancher-components/BadgeState/BadgeState.vue +3 -3
  217. package/rancher-components/Banner/Banner.vue +2 -2
  218. package/rancher-components/Card/Card.vue +3 -3
  219. package/rancher-components/Form/Checkbox/Checkbox.vue +3 -3
  220. package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +18 -1
  221. package/rancher-components/Form/LabeledInput/LabeledInput.vue +65 -24
  222. package/rancher-components/Form/Radio/RadioButton.test.ts +7 -3
  223. package/rancher-components/Form/Radio/RadioButton.vue +13 -7
  224. package/rancher-components/Form/Radio/RadioGroup.test.ts +30 -0
  225. package/rancher-components/Form/Radio/RadioGroup.vue +8 -3
  226. package/rancher-components/Form/TextArea/TextAreaAutoGrow.vue +6 -4
  227. package/rancher-components/Form/ToggleSwitch/ToggleSwitch.vue +7 -4
  228. package/rancher-components/LabeledTooltip/LabeledTooltip.vue +9 -4
  229. package/rancher-components/StringList/StringList.test.ts +270 -0
  230. package/rancher-components/StringList/StringList.vue +65 -26
  231. package/rancher-components/components/Accordion/Accordion.test.ts +45 -0
  232. package/rancher-components/components/Accordion/Accordion.vue +86 -0
  233. package/rancher-components/components/Accordion/index.ts +1 -0
  234. package/rancher-components/components/BadgeState/BadgeState.vue +3 -3
  235. package/rancher-components/components/Banner/Banner.vue +2 -2
  236. package/rancher-components/components/Card/Card.vue +3 -3
  237. package/rancher-components/components/Form/Checkbox/Checkbox.vue +3 -3
  238. package/rancher-components/components/Form/LabeledInput/LabeledInput.test.ts +18 -1
  239. package/rancher-components/components/Form/LabeledInput/LabeledInput.vue +57 -24
  240. package/rancher-components/components/Form/Radio/RadioButton.vue +13 -7
  241. package/rancher-components/components/Form/Radio/RadioGroup.vue +4 -3
  242. package/rancher-components/components/Form/TextArea/TextAreaAutoGrow.vue +6 -4
  243. package/rancher-components/components/Form/ToggleSwitch/ToggleSwitch.vue +7 -4
  244. package/rancher-components/components/LabeledTooltip/LabeledTooltip.vue +9 -4
  245. package/rancher-components/components/StringList/StringList.vue +8 -8
  246. package/scripts/.gitlab/workflows/build-extension-catalog.gitlab-ci.yml +50 -0
  247. package/scripts/extension/bundle +19 -7
  248. package/scripts/extension/helm/scripts/package +11 -3
  249. package/scripts/extension/parse-tag-name +2 -2
  250. package/scripts/extension/publish +20 -9
  251. package/scripts/publish-shell.sh +10 -0
  252. package/scripts/test-plugins-build.sh +85 -9
  253. package/server/har-file.js +183 -0
  254. package/store/catalog.js +1 -1
  255. package/store/features.js +1 -0
  256. package/store/i18n.js +11 -0
  257. package/store/index.js +13 -15
  258. package/store/prefs.js +33 -35
  259. package/store/type-map.js +8 -7
  260. package/tsconfig.json +35 -9
  261. package/tsconfig.paths.json +21 -0
  262. package/types/shell/index.d.ts +433 -234
  263. package/types/vue-shim.d.ts +42 -0
  264. package/utils/__tests__/create-yaml.test.ts +60 -0
  265. package/utils/axios.js +0 -19
  266. package/utils/azure.js +24 -0
  267. package/utils/clipboard.js +5 -0
  268. package/utils/create-yaml.js +17 -10
  269. package/utils/git.ts +1 -1
  270. package/utils/monitoring.js +1 -1
  271. package/utils/nuxt.js +18 -39
  272. package/utils/object.js +14 -0
  273. package/utils/router.scrollBehavior.js +12 -14
  274. package/utils/time.js +1 -1
  275. package/utils/url.ts +1 -1
  276. package/vue.config.js +23 -2
  277. package/.DS_Store +0 -0
  278. package/assets/images/providers/aks-black.svg +0 -28
  279. package/assets/images/providers/aks.svg +0 -31
  280. package/edit/provisioning.cattle.io.cluster/__tests__/Basics.tests.ts +0 -234
  281. package/initialize/layouts.ts +0 -26
  282. package/mixins/fetch.server.js +0 -73
  283. package/pages/c/index.vue +0 -9
  284. package/pages/rio/mesh.vue +0 -508
  285. package/plugins/transitions.js +0 -4
  286. package/plugins/vue-clipboard2.js +0 -4
  287. package/tsconfig.default.json +0 -46
  288. package/yarn-error.log +0 -200
  289. /package/components/form/__tests__/{NameNsDescription.ts → NameNsDescription.test.ts} +0 -0
  290. /package/edit/networking.k8s.io.networkpolicy/__tests__/utils/{selectors.ts → selectors.test.ts} +0 -0
  291. /package/edit/provisioning.cattle.io.cluster/{AgentConfiguration.vue → tabs/AgentConfiguration.vue} +0 -0
  292. /package/edit/provisioning.cattle.io.cluster/{MemberRoles.vue → tabs/MemberRoles.vue} +0 -0
  293. /package/edit/provisioning.cattle.io.cluster/{S3Config.vue → tabs/etcd/S3Config.vue} +0 -0
  294. /package/edit/provisioning.cattle.io.cluster/{ACE.vue → tabs/networking/ACE.vue} +0 -0
  295. /package/edit/provisioning.cattle.io.cluster/{RegistryConfigs.vue → tabs/registries/RegistryConfigs.vue} +0 -0
  296. /package/edit/provisioning.cattle.io.cluster/{RegistryMirrors.vue → tabs/registries/RegistryMirrors.vue} +0 -0
  297. /package/edit/provisioning.cattle.io.cluster/{DrainOptions.vue → tabs/upgrade/DrainOptions.vue} +0 -0
  298. /package/plugins/dashboard-store/__tests__/{actions.spec.ts → actions.test.ts} +0 -0
  299. /package/plugins/dashboard-store/__tests__/{getters.spec.ts → getters.test.ts} +0 -0
  300. /package/rancher-components/BadgeState/{BadgeState.spec.ts → BadgeState.test.ts} +0 -0
  301. /package/rancher-components/components/BadgeState/{BadgeState.spec.ts → BadgeState.test.ts} +0 -0
@@ -343,6 +343,7 @@ export default {
343
343
 
344
344
  for ( const row of input ) {
345
345
  let value = row[this.valueName] || '';
346
+
346
347
  const decodedValue = base64Decode(row[this.valueName]);
347
348
  const asciiValue = asciiLike(decodedValue);
348
349
 
@@ -644,6 +645,7 @@ export default {
644
645
  :clearable="false"
645
646
  :taggable="keyTaggable"
646
647
  :options="calculateOptions(row[keyName])"
648
+ :data-testid="`select-kv-item-key-${i}`"
647
649
  @input="queueUpdate"
648
650
  />
649
651
  <input
@@ -652,6 +654,7 @@ export default {
652
654
  v-model="row[keyName]"
653
655
  :disabled="isView || disabled || !keyEditable || isProtected(row.key)"
654
656
  :placeholder="keyPlaceholder"
657
+ :data-testid="`input-kv-item-key-${i}`"
655
658
  @input="queueUpdate"
656
659
  @paste="onPaste(i, $event)"
657
660
  >
@@ -661,6 +664,7 @@ export default {
661
664
  <!-- Value -->
662
665
  <div
663
666
  :key="i+'value'"
667
+ :data-testid="`kv-item-value-${i}`"
664
668
  class="kv-item value"
665
669
  >
666
670
  <slot
@@ -88,6 +88,10 @@ export default {
88
88
  default: null,
89
89
  type: [String, Object, Number, Array, Boolean]
90
90
  },
91
+ options: {
92
+ type: Array,
93
+ required: true
94
+ },
91
95
  closeOnSelect: {
92
96
  type: Boolean,
93
97
  default: true
@@ -2,6 +2,7 @@
2
2
  import { mapGetters } from 'vuex';
3
3
  import { LabeledInput } from '@components/Form/LabeledInput';
4
4
  import { CHARSET, randomStr } from '@shell/utils/string';
5
+ import { copyTextToClipboard } from '@shell/utils/clipboard';
5
6
 
6
7
  export default {
7
8
  components: { LabeledInput },
@@ -75,6 +76,7 @@ export default {
75
76
  }
76
77
  },
77
78
  methods: {
79
+ copyTextToClipboard,
78
80
  generatePassword() {
79
81
  this.password = randomStr(16, CHARSET.ALPHA_NUM);
80
82
  },
@@ -109,7 +111,7 @@ export default {
109
111
  >
110
112
  <a
111
113
  href="#"
112
- @click.prevent.stop="$copyText(password)"
114
+ @click.prevent.stop="copyTextToClipboard(password)"
113
115
  >{{ t('action.copy') }}</a>
114
116
  </div>
115
117
  <div
@@ -2,16 +2,24 @@
2
2
 
3
3
  export default {
4
4
  props: {
5
+ // When sortabletable calculates these values it converts null and undefined to ''
6
+ // passing '' to a prop typed as Boolean coerces it to true
5
7
  value: {
6
- type: Boolean,
8
+ type: [String, Boolean],
7
9
  default: true
8
10
  },
9
- }
11
+ },
12
+
13
+ computed: {
14
+ checked() {
15
+ return this.value === true || this.value === 'true';
16
+ }
17
+ },
10
18
  };
11
19
  </script>
12
20
 
13
21
  <template>
14
- <span v-if="value">
22
+ <span v-if="checked">
15
23
  <i class="icon icon-checkmark" />
16
24
  </span>
17
25
  <span
@@ -0,0 +1,27 @@
1
+ <script>
2
+ import FleetSummaryGraph from '@shell/components/formatter/FleetSummaryGraph';
3
+
4
+ export default {
5
+ name: 'FleetClusterSummaryGraph',
6
+ components: { FleetSummaryGraph },
7
+
8
+ props: {
9
+ row: {
10
+ type: Object,
11
+ required: true
12
+ },
13
+
14
+ clusterLabel: {
15
+ type: String,
16
+ required: true
17
+ }
18
+ },
19
+ };
20
+ </script>
21
+
22
+ <template>
23
+ <FleetSummaryGraph
24
+ :row="row"
25
+ :clusterLabel="clusterLabel"
26
+ />
27
+ </template>
@@ -12,10 +12,22 @@ export default {
12
12
  type: Object,
13
13
  required: true
14
14
  },
15
+
16
+ clusterLabel: {
17
+ type: String,
18
+ required: false,
19
+ default: null,
20
+ }
15
21
  },
16
22
 
17
23
  computed: {
18
24
  summary() {
25
+ if (this.clusterLabel) {
26
+ return this.row.clusterResourceStatus.find((x) => {
27
+ return x.clusterLabel === this.clusterLabel;
28
+ })?.status.resourceCounts || {};
29
+ }
30
+
19
31
  return this.row.status?.resourceCounts || {};
20
32
  },
21
33
 
@@ -73,7 +85,7 @@ export default {
73
85
  >
74
86
  <td
75
87
  class="text-left pr-20"
76
- :class="{[obj.textColor]: true}"
88
+ :class="{ [obj.textColor]: true }"
77
89
  >
78
90
  {{ obj.label }}
79
91
  </td>
@@ -94,17 +106,17 @@ export default {
94
106
  </template>
95
107
 
96
108
  <style lang="scss">
97
- .col-scale {
98
- position: relative;
109
+ .col-scale {
110
+ position: relative;
99
111
 
100
- .trigger {
101
- width: 100%;
102
- }
112
+ .trigger {
113
+ width: 100%;
103
114
  }
115
+ }
104
116
 
105
- .scale {
106
- margin: 0;
107
- padding: 0;
108
- line-height: initial;
109
- }
117
+ .scale {
118
+ margin: 0;
119
+ padding: 0;
120
+ line-height: initial;
121
+ }
110
122
  </style>
@@ -57,7 +57,7 @@ export default {
57
57
  const now = day();
58
58
 
59
59
  from = from || now;
60
- const seconds = Math.abs(value.diff(now, 'seconds'));
60
+ const seconds = Math.abs(value.diff(from, 'seconds'));
61
61
 
62
62
  return elapsedTime(seconds);
63
63
  },
@@ -30,7 +30,7 @@ export default {
30
30
  </script>
31
31
 
32
32
  <template>
33
- <p v-if="!value || value === '0'">
33
+ <p v-if="!value || value === '0' || isNaN(value)">
34
34
  {{ t('generic.na') }}
35
35
  </p>
36
36
  <PercentageBarComponent
@@ -0,0 +1,19 @@
1
+ import { mount } from '@vue/test-utils';
2
+ import Checked from '@shell/components/formatter/Checked.vue';
3
+
4
+ describe('component: Checked', () => {
5
+ it.each([true, 'true'])('should display a checkmark when the value is true or "true"', (value: any) => {
6
+ const wrapper = mount(Checked, { propsData: { value } });
7
+ const checkmark = wrapper.find('.icon-checkmark');
8
+
9
+ expect(checkmark.exists()).toBe(true);
10
+ });
11
+
12
+ it.each([false, '', 'abc'])('should not display a checkmark when the value is anything other than true or "true"', (value: any) => {
13
+ const wrapper = mount(Checked, { propsData: { value } });
14
+
15
+ const checkmark = wrapper.find('.icon-checkmark');
16
+
17
+ expect(checkmark.exists()).toBe(false);
18
+ });
19
+ });
@@ -390,8 +390,8 @@ export default {
390
390
  }
391
391
  }
392
392
 
393
- .body ::v-deep > .child.nuxt-link-active,
394
- .header ::v-deep > .child.nuxt-link-exact-active {
393
+ .body ::v-deep > .child.router-link-active,
394
+ .header ::v-deep > .child.router-link-exact-active {
395
395
  padding: 0;
396
396
 
397
397
  A, A I {
@@ -311,7 +311,7 @@ export default {
311
311
  product: this.currentProduct.name,
312
312
  cluster: this.currentCluster,
313
313
  };
314
- const enabled = action.enabled ? action.enabled.apply(this, [opts]) : true;
314
+ const enabled = action.enabled ? action.enabled.apply(this, [this.ctx]) : true;
315
315
 
316
316
  if (fn && enabled) {
317
317
  fn.apply(this, [opts, [], { $route: this.$route }]);
@@ -441,7 +441,6 @@ export default {
441
441
 
442
442
  <div class="rd-header-right">
443
443
  <component :is="navHeaderRight" />
444
-
445
444
  <div
446
445
  v-if="showFilter"
447
446
  class="top"
@@ -13,6 +13,7 @@ import { getVersionInfo } from '@shell/utils/version';
13
13
  import { LEGACY } from '@shell/store/features';
14
14
  import { SETTING } from '@shell/config/settings';
15
15
  import { filterOnlyKubernetesClusters, filterHiddenLocalCluster } from '@shell/utils/cluster';
16
+ import { getProductFromRoute } from '@shell/middleware/authenticated';
16
17
  import { isRancherPrime } from '@shell/config/version';
17
18
  import Pinned from '@shell/components/nav/Pinned';
18
19
 
@@ -72,12 +73,17 @@ export default {
72
73
  if (bannerSettings) {
73
74
  const parsed = JSON.parse(bannerSettings.value);
74
75
  const {
75
- showFooter, showHeader, bannerFooter, bannerHeader
76
+ showFooter, showHeader, bannerFooter, bannerHeader, banner
76
77
  } = parsed;
77
78
 
79
+ // add defaults to accomodate older JSON structures for banner definitions without breaking the UI
80
+ // https://github.com/rancher/dashboard/issues/10140
81
+ const bannerHeaderFontSize = bannerHeader?.fontSize || banner?.fontSize || '14px';
82
+ const bannerFooterFontSize = bannerFooter?.fontSize || banner?.fontSize || '14px';
83
+
78
84
  return {
79
- headerFont: showHeader === 'true' ? this.pxToEm(bannerHeader.fontSize) : '0px',
80
- footerFont: showFooter === 'true' ? this.pxToEm(bannerFooter.fontSize) : '0px'
85
+ headerFont: showHeader === 'true' ? this.pxToEm(bannerHeaderFontSize) : '0px',
86
+ footerFont: showFooter === 'true' ? this.pxToEm(bannerFooterFontSize) : '0px'
81
87
  };
82
88
  }
83
89
 
@@ -242,7 +248,15 @@ export default {
242
248
 
243
249
  hasSupport() {
244
250
  return isRancherPrime() || this.$store.getters['management/byId'](MANAGEMENT.SETTING, SETTING.SUPPORTED )?.value === 'true';
245
- }
251
+ },
252
+
253
+ isCurrRouteClusterExplorer() {
254
+ return this.$route?.name?.startsWith('c-cluster');
255
+ },
256
+
257
+ productFromRoute() {
258
+ return getProductFromRoute(this.$route);
259
+ },
246
260
  },
247
261
 
248
262
  watch: {
@@ -273,6 +287,16 @@ export default {
273
287
  return `${ lineHeightInEm }em`;
274
288
  },
275
289
 
290
+ checkActiveRoute(obj, isClusterRoute) {
291
+ // for Cluster links in main nav: check if route is a cluster explorer one + check if route cluster matches cluster obj id + check if curr product matches route product
292
+ if (isClusterRoute) {
293
+ return this.isCurrRouteClusterExplorer && this.$route?.params?.cluster === obj?.id && this.productFromRoute === this.currentProduct?.name;
294
+ }
295
+
296
+ // for remaining main nav items, check if curr product matches route product is enough
297
+ return this.productFromRoute === obj?.value;
298
+ },
299
+
276
300
  handler(e) {
277
301
  if (e.keyCode === KEY.ESCAPE ) {
278
302
  this.hide();
@@ -435,6 +459,7 @@ export default {
435
459
  <nuxt-link
436
460
  class="option"
437
461
  :to="a.to"
462
+ :class="{'active-menu-link': checkActiveRoute(a) }"
438
463
  >
439
464
  <IconOrSvg
440
465
  :icon="a.icon"
@@ -466,6 +491,7 @@ export default {
466
491
  v-if="c.ready"
467
492
  :data-testid="`menu-cluster-${ c.id }`"
468
493
  class="cluster selector option"
494
+ :class="{'active-menu-link': checkActiveRoute(c, true) }"
469
495
  :to="{ name: 'c-cluster-explorer', params: { cluster: c.id } }"
470
496
  >
471
497
  <ClusterIconMenu
@@ -515,6 +541,7 @@ export default {
515
541
  v-if="c.ready"
516
542
  :data-testid="`menu-cluster-${ c.id }`"
517
543
  class="cluster selector option"
544
+ :class="{'active-menu-link': checkActiveRoute(c, true) }"
518
545
  :to="{ name: 'c-cluster-explorer', params: { cluster: c.id } }"
519
546
  >
520
547
  <ClusterIconMenu
@@ -592,6 +619,7 @@ export default {
592
619
  >
593
620
  <nuxt-link
594
621
  class="option"
622
+ :class="{'active-menu-link': checkActiveRoute(a) }"
595
623
  :to="a.to"
596
624
  >
597
625
  <IconOrSvg
@@ -619,6 +647,7 @@ export default {
619
647
  >
620
648
  <nuxt-link
621
649
  class="option"
650
+ :class="{'active-menu-link': checkActiveRoute(a) }"
622
651
  :to="a.to"
623
652
  >
624
653
  <IconOrSvg
@@ -648,6 +677,7 @@ export default {
648
677
  >
649
678
  <nuxt-link
650
679
  class="option"
680
+ :class="{'active-menu-link': checkActiveRoute(a) }"
651
681
  :to="a.to"
652
682
  >
653
683
  <IconOrSvg
@@ -867,7 +897,7 @@ export default {
867
897
  margin-right: 16px;
868
898
  }
869
899
 
870
- &.nuxt-link-active {
900
+ &.router-link-active, &.active-menu-link {
871
901
  background: var(--primary-hover-bg);
872
902
  color: var(--primary-hover-text);
873
903
 
@@ -991,7 +1021,7 @@ export default {
991
1021
  font-size: 14px;
992
1022
  }
993
1023
 
994
- .nuxt-link-active {
1024
+ .router-link-active {
995
1025
  &:hover {
996
1026
  text-decoration: none;
997
1027
  }
@@ -1,7 +1,6 @@
1
1
  <script>
2
2
  import Favorite from '@shell/components/nav/Favorite';
3
3
  import { FAVORITE, USED } from '@shell/store/type-map';
4
- import { linkActiveClass } from '@shell/config/router';
5
4
 
6
5
  const showFavoritesFor = [FAVORITE, USED];
7
6
 
@@ -31,7 +30,6 @@ export default {
31
30
  near: false,
32
31
  over: false,
33
32
  menuPath: this.type.route ? this.$router.resolve(this.type.route)?.route?.path : undefined,
34
- linkActiveClass
35
33
  };
36
34
  },
37
35
 
@@ -134,7 +132,7 @@ export default {
134
132
  :to="type.route"
135
133
  tag="li"
136
134
  class="child nav-type"
137
- :class="{'root': isRoot, [`depth-${depth}`]: true, [linkActiveClass]: isCurrent}"
135
+ :class="{'root': isRoot, [`depth-${depth}`]: true, 'router-link-active': isCurrent}"
138
136
  :exact="type.exact"
139
137
  >
140
138
  <a
@@ -2,7 +2,7 @@
2
2
  import { saveAs } from 'file-saver';
3
3
  import AnsiUp from 'ansi_up';
4
4
  import { addParams } from '@shell/utils/url';
5
- import { base64Decode } from '@shell/utils/crypto';
5
+ import { base64DecodeToBuffer } from '@shell/utils/crypto';
6
6
  import { LOGS_RANGE, LOGS_TIME, LOGS_WRAP } from '@shell/store/prefs';
7
7
  import LabeledSelect from '@shell/components/form/LabeledSelect';
8
8
  import { Checkbox } from '@components/Form/Checkbox';
@@ -25,6 +25,61 @@ import Window from './Window';
25
25
 
26
26
  let lastId = 1;
27
27
  const ansiup = new AnsiUp();
28
+ // Convert arrayBuffer(Uint8Array) to string
29
+ // ref: https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder
30
+ // ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/of
31
+ const ab2str = (input, outputEncoding = 'utf8') => {
32
+ const decoder = new TextDecoder(outputEncoding);
33
+
34
+ return decoder.decode(input);
35
+ };
36
+
37
+ // The utf-8 encoded messages pushed by websocket may truncate multi-byte utf-8 characters,
38
+ // which causes the front-end to be unable to parse the truncated multi-byte utf-8 characters in the previous and next messages when decoding.
39
+ // Therefore, we need to determine whether the last 4 bytes of the current pushed message contain incomplete utf-8 encoded characters.
40
+ // ref: https://en.wikipedia.org/wiki/UTF-8#Encoding
41
+ const isLogTruncated = (uint8ArrayBuffer) => {
42
+ const len = uint8ArrayBuffer.length;
43
+ const count = Math.min(4, len);
44
+ let isTruncated = false;
45
+
46
+ // Parses the last ${count} bytes of the array to determine if there are any truncated utf-8 characters.
47
+ for ( let i = 0; i < count; i++ ) {
48
+ const a = uint8ArrayBuffer[len - (1 + i)];
49
+
50
+ // 1 byte utf-8 character in binary form: 0xxxxxxxxx
51
+ if ((a & 0b10000000) === 0b00000000) {
52
+ break;
53
+ }
54
+ // Multi-byte utf-8 character, intermediate binary form: 10xxxxxx
55
+ if ((a & 0b11000000) === 0b10000000) {
56
+ continue;
57
+ }
58
+ // 2 byte utf-8 character in binary form: 110xxxxx 10xxxxxx
59
+ if ((a & 0b11100000) === 0b11000000) {
60
+ if ( i !== 1) {
61
+ isTruncated = true;
62
+ }
63
+ break;
64
+ }
65
+ // 3 byte utf-8 character in binary form: 1110xxxx 10xxxxxx 10xxxxxx
66
+ if ((a & 0b11110000) === 0b11100000) {
67
+ if (i !== 2) {
68
+ isTruncated = true;
69
+ }
70
+ break;
71
+ }
72
+ // 4 byte utf-8 character in binary form: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
73
+ if ((a & 0b11111000) === 0b11110000) {
74
+ if (i !== 3) {
75
+ isTruncated = true;
76
+ }
77
+ break;
78
+ }
79
+ }
80
+
81
+ return isTruncated;
82
+ };
28
83
 
29
84
  export default {
30
85
  components: {
@@ -273,11 +328,54 @@ export default {
273
328
  console.error('Connect Error', e); // eslint-disable-line no-console
274
329
  });
275
330
 
331
+ let logBuffer = [];
332
+ let truncatedLog = '';
333
+
276
334
  this.socket.addEventListener(EVENT_MESSAGE, (e) => {
277
- const data = base64Decode(e.detail.data);
335
+ const decodedData = e.detail?.data || '';
336
+ const replacedData = decodedData.replace(/[-_]/g, (char) => char === '-' ? '+' : '/');
337
+ const b = base64DecodeToBuffer(replacedData);
338
+ const isTruncated = isLogTruncated(b);
339
+
340
+ if (isTruncated === true) {
341
+ logBuffer.push(...b);
342
+
343
+ return;
344
+ }
345
+
346
+ let d;
347
+
348
+ // If the logBuffer is not empty,
349
+ // there are truncated utf-8 characters in the previous message
350
+ // that need to be merged with the current message before decoding.
351
+ if (logBuffer.length > 0) {
352
+ // Convert arrayBuffer(Uint8Array) to string
353
+ // ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/of
354
+ d = ab2str(Uint8Array.of(...logBuffer, ...b));
355
+ logBuffer = [];
356
+ } else {
357
+ d = b.toString();
358
+ }
359
+ let data = d;
278
360
 
361
+ if (truncatedLog) {
362
+ data = `${ truncatedLog }${ d }`;
363
+ truncatedLog = '';
364
+ }
365
+
366
+ if (!d.endsWith('\n')) {
367
+ const lines = data.split(/\n/);
368
+
369
+ if (lines.length === 1) {
370
+ truncatedLog = data;
371
+
372
+ return;
373
+ }
374
+ data = lines.slice(0, -1).join('\n');
375
+ truncatedLog = lines.slice(-1);
376
+ }
279
377
  // Websocket message may contain multiple lines - loop through each line, one by one
280
- data.split('\n').forEach((line) => {
378
+ data.split('\n').filter((line) => line).forEach((line) => {
281
379
  let msg = line;
282
380
  let time = null;
283
381
 
@@ -79,6 +79,7 @@ export default {
79
79
  fitAddon: null,
80
80
  searchAddon: null,
81
81
  webglAddon: null,
82
+ canvasAddon: null,
82
83
  isOpen: false,
83
84
  isOpening: false,
84
85
  backlog: [],
@@ -155,6 +156,7 @@ export default {
155
156
  webgl: import(/* webpackChunkName: "xterm" */ 'xterm-addon-webgl'),
156
157
  weblinks: import(/* webpackChunkName: "xterm" */ 'xterm-addon-web-links'),
157
158
  search: import(/* webpackChunkName: "xterm" */ 'xterm-addon-search'),
159
+ canvas: import(/* webpackChunkName: "xterm" */ 'xterm-addon-canvas')
158
160
  });
159
161
 
160
162
  const terminal = new xterm.Terminal({
@@ -171,10 +173,11 @@ export default {
171
173
  this.searchAddon = new addons.search.SearchAddon();
172
174
 
173
175
  try {
174
- this.webglAddon = new addons.webgl.WebGlAddon();
176
+ this.webglAddon = new addons.webgl.WebglAddon();
175
177
  } catch (e) {
176
178
  // Some browsers (Safari) don't support the webgl renderer, so don't use it.
177
179
  this.webglAddon = null;
180
+ this.canvasAddon = new addons.canvas.CanvasAddon();
178
181
  }
179
182
 
180
183
  terminal.loadAddon(this.fitAddon);
@@ -184,6 +187,8 @@ export default {
184
187
 
185
188
  if (this.webglAddon) {
186
189
  terminal.loadAddon(this.webglAddon);
190
+ } else {
191
+ terminal.loadAddon(this.canvasAddon);
187
192
  }
188
193
 
189
194
  this.fit();