@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
@@ -15,6 +15,7 @@ import Tab from '@shell/components/Tabbed/Tab';
15
15
 
16
16
  import { allHash } from '@shell/utils/promise';
17
17
  import { STORAGE_CLASS, PVC, SECRET, WORKLOAD_TYPES } from '@shell/config/types';
18
+ import { CATTLE_MONITORING_NAMESPACE } from '@shell/utils/monitoring';
18
19
 
19
20
  export default {
20
21
  components: {
@@ -52,17 +53,40 @@ export default {
52
53
  async fetch() {
53
54
  const { $store } = this;
54
55
 
55
- const hash = await allHash({
56
- namespaces: $store.getters['namespaces'](),
57
- pvcs: $store.dispatch('cluster/findAll', { type: PVC }),
58
- secrets: $store.dispatch('cluster/findAll', { type: SECRET }),
56
+ // Fetch all the resources required for all the tabs asyncronously up front
57
+ const hashPromises = {
58
+ namespaces: $store.getters['namespaces'](),
59
+ pvcs: $store.dispatch('cluster/findAll', { type: PVC }),
60
+ // Used in Alerting tab
61
+ monitoringSecrets: $store.dispatch('cluster/findAll', {
62
+ type: SECRET,
63
+ opt: { namespaced: CATTLE_MONITORING_NAMESPACE }
64
+ }),
59
65
  storageClasses: $store.dispatch('cluster/findAll', { type: STORAGE_CLASS }),
66
+ };
67
+
68
+ // Are we editing an existing chart?
69
+ // (ported from shell/chart/monitoring/prometheus/index.vue)
70
+ const { existing = false } = this.$attrs;
71
+
72
+ // If needed, fetch all the workloads that have prometheus operator like containers
73
+ this.workloadTypes = !existing ? Object.values(WORKLOAD_TYPES) : [];
74
+
75
+ this.workloadTypes.forEach((type) => {
76
+ // We'll use a filter to fetch the results. Atm there's no neat way to differentiate between ALL results and JUST filtered
77
+ // So to avoid calls to all getting these filtered (and vice-versa) forget type before and after
78
+ $store.dispatch('cluster/forgetType', type);
79
+ hashPromises[type] = $store.dispatch('cluster/findAll', {
80
+ type,
81
+ opt: {
82
+ watch: false,
83
+ // We're only interested in images with operator like names (note: these will match partial strings)
84
+ filter: { 'spec.template.spec.containers.image': ['quay.io/coreos/prometheus-operator', 'rancher/coreos-prometheus-operator'] }
85
+ }
86
+ });
60
87
  });
61
88
 
62
- await Promise.all(
63
- Object.values(WORKLOAD_TYPES).map((type) => this.$store.dispatch('cluster/findAll', { type })
64
- )
65
- );
89
+ const hash = await allHash(hashPromises);
66
90
 
67
91
  this.targetNamespace = hash.namespaces[this.chart.targetNamespace] || false;
68
92
 
@@ -74,9 +98,21 @@ export default {
74
98
  this.pvcs = hash.pvcs;
75
99
  }
76
100
 
77
- if (!isEmpty(hash.secrets)) {
78
- this.secrets = hash.secrets;
101
+ if (!isEmpty(hash.monitoringSecrets)) {
102
+ this.monitoringSecrets = hash.monitoringSecrets;
79
103
  }
104
+
105
+ this.workloadTypes.forEach((type) => {
106
+ if (hash[type]) {
107
+ this.filteredWorkloads.push(...hash[type]);
108
+ }
109
+ });
110
+ },
111
+
112
+ beforeDestroy() {
113
+ this.workloadTypes.forEach((type) => {
114
+ this.$store.dispatch('cluster/forgetType', type);
115
+ });
80
116
  },
81
117
 
82
118
  data() {
@@ -99,9 +135,11 @@ export default {
99
135
  disableAggregateRoles: false,
100
136
  prometheusResources: [],
101
137
  pvcs: [],
102
- secrets: [],
138
+ monitoringSecrets: [],
103
139
  storageClasses: [],
104
140
  targetNamespace: null,
141
+ filteredWorkloads: [],
142
+ workloadTypes: []
105
143
  };
106
144
  },
107
145
 
@@ -110,10 +148,6 @@ export default {
110
148
  provider() {
111
149
  return this.currentCluster.status.provider.toLowerCase();
112
150
  },
113
- workloads() {
114
- return Object.values(WORKLOAD_TYPES).flatMap((type) => this.$store.getters['cluster/all'](type)
115
- );
116
- },
117
151
  },
118
152
 
119
153
  watch: {
@@ -278,7 +312,7 @@ export default {
278
312
  :mode="mode"
279
313
  :storage-classes="storageClasses"
280
314
  :prometheus-pods="prometheusResources"
281
- :workloads="workloads"
315
+ :filteredWorkloads="filteredWorkloads"
282
316
  />
283
317
  </div>
284
318
  </Tab>
@@ -291,7 +325,7 @@ export default {
291
325
  <Alerting
292
326
  v-model="value"
293
327
  :mode="mode"
294
- :secrets="secrets"
328
+ :monitoringSecrets="monitoringSecrets"
295
329
  />
296
330
  </div>
297
331
  </Tab>
@@ -1,5 +1,4 @@
1
1
  <script>
2
- import isEmpty from 'lodash/isEmpty';
3
2
  import { mapGetters } from 'vuex';
4
3
 
5
4
  import { Banner } from '@components/Banner';
@@ -51,21 +50,19 @@ export default {
51
50
  default: () => ({}),
52
51
  },
53
52
 
54
- workloads: {
53
+ filteredWorkloads: {
55
54
  type: Array,
56
55
  default: () => ([]),
57
56
  },
58
57
  },
59
58
 
60
59
  data() {
61
- return {
62
- enablePersistentStorage: !!this.value?.prometheus?.prometheusSpec?.storageSpec?.volumeClaimTemplate?.spec,
63
- warnUser: false,
64
- };
60
+ return { enablePersistentStorage: !!this.value?.prometheus?.prometheusSpec?.storageSpec?.volumeClaimTemplate?.spec };
65
61
  },
66
62
 
67
63
  computed: {
68
64
  ...mapGetters(['currentCluster']),
65
+
69
66
  matchExpressions: {
70
67
  get() {
71
68
  const selector = this.value?.prometheus?.prometheusSpec?.storageSpec?.volumeClaimTemplate?.spec?.selector;
@@ -80,42 +77,21 @@ export default {
80
77
  }
81
78
  }
82
79
  },
83
- filteredWorkloads() {
84
- let { workloads } = this;
85
- const { existing = false } = this.$attrs;
86
-
87
- if (!existing) {
88
- workloads = workloads.filter((workload) => {
89
- if (
90
- !isEmpty(workload?.spec?.template?.spec?.containers) &&
91
- (workload.spec.template.spec.containers.find((c) => c.image.includes('quay.io/coreos/prometheus-operator') ||
92
- c.image.includes('rancher/coreos-prometheus-operator'))
93
- )
94
- ) {
95
- if (!this.warnUser) {
96
- this.warnUser = true;
97
- }
98
80
 
99
- return workload;
100
- }
101
- });
102
- }
103
-
104
- return workloads.map((wl) => {
105
- return {
106
- label: wl.id,
107
- link: {
108
- name: 'c-cluster-product-resource-namespace-id',
109
- params: {
110
- cluster: this.currentCluster.id,
111
- product: 'explorer',
112
- resource: wl.type,
113
- namespace: wl.metadata.namespace,
114
- id: wl.metadata.name
115
- },
116
- }
117
- };
118
- });
81
+ mappedFilteredWorkloads() {
82
+ return this.filteredWorkloads.map((wl) => ({
83
+ label: wl.id,
84
+ link: {
85
+ name: 'c-cluster-product-resource-namespace-id',
86
+ params: {
87
+ cluster: this.currentCluster.id,
88
+ product: 'explorer',
89
+ resource: wl.type,
90
+ namespace: wl.metadata.namespace,
91
+ id: wl.metadata.name
92
+ },
93
+ }
94
+ }));
119
95
  },
120
96
 
121
97
  podsAndNamespaces() {
@@ -179,6 +155,23 @@ export default {
179
155
 
180
156
  this.$set(storageSpec.selector, 'matchLabels', matchLabels);
181
157
  this.$set(storageSpec.selector, 'matchExpressions', matchExpressions);
158
+
159
+ // Remove an empty selector object if present
160
+ // User can add a selector and then remove the selector - this will leave an empty structure as above
161
+ // We want to ensure we remove .selector.matchExpressions, .selector.matchLabels, and .selector if empty
162
+ // See: https://github.com/rancher/dashboard/issues/10016
163
+
164
+ if (storageSpec.selector.matchExpressions?.length === 0) {
165
+ delete storageSpec.selector.matchExpressions;
166
+ }
167
+
168
+ if (storageSpec.selector.matchLabels && Object.keys(storageSpec.selector.matchLabels).length === 0) {
169
+ delete storageSpec.selector.matchLabels;
170
+ }
171
+
172
+ if (Object.keys(storageSpec.selector).length === 0) {
173
+ delete storageSpec.selector;
174
+ }
182
175
  },
183
176
  },
184
177
  };
@@ -189,8 +182,9 @@ export default {
189
182
  <div class="title">
190
183
  <h3>{{ t('monitoring.prometheus.title') }}</h3>
191
184
  </div>
185
+ <!-- https://github.com/rancher/dashboard/issues/1167 -->
192
186
  <Banner
193
- v-if="filteredWorkloads && warnUser"
187
+ v-if="mappedFilteredWorkloads.length"
194
188
  color="warning"
195
189
  >
196
190
  <template #default>
@@ -199,7 +193,7 @@ export default {
199
193
  :raw="true"
200
194
  />
201
195
  <div
202
- v-for="wl in filteredWorkloads"
196
+ v-for="wl in mappedFilteredWorkloads"
203
197
  :key="wl.id"
204
198
  class="mt-10"
205
199
  >
@@ -65,7 +65,7 @@ export default {
65
65
 
66
66
  computed: {
67
67
  defaultStorageClass() {
68
- return this.storageClasses.filter((sc) => sc.metadata.annotations[STORAGE.DEFAULT_STORAGE_CLASS] && sc.metadata.annotations[STORAGE.DEFAULT_STORAGE_CLASS] !== 'false' )[0] || '';
68
+ return this.storageClasses.filter((sc) => sc.metadata.annotations?.[STORAGE.DEFAULT_STORAGE_CLASS] && sc.metadata.annotations[STORAGE.DEFAULT_STORAGE_CLASS] !== 'false' )[0] || '';
69
69
  },
70
70
 
71
71
  availablePVs() {
@@ -200,6 +200,7 @@ export default {
200
200
  :status="reclaimWarning ? 'warning' : null"
201
201
  :options="storageClasses"
202
202
  :hover-tooltip="true"
203
+ data-testid="backup-chart-select-existing-storage-class"
203
204
  />
204
205
  </div>
205
206
  <div class="col span-6">
@@ -2,11 +2,9 @@
2
2
  import CreateEditView from '@shell/mixins/create-edit-view';
3
3
  import { LabeledInput } from '@components/Form/LabeledInput';
4
4
  import { azureEnvironments } from '@shell/machine-config/azure';
5
+ import { parseAzureError } from '@shell/utils/azure';
5
6
  import LabeledSelect from '@shell/components/form/LabeledSelect';
6
7
 
7
- const AZURE_ERROR_MSG_REGEX = /^.*Message=\"(.*)\"$/;
8
- const AZURE_ERROR_JSON_REGEX = /^.*Response body: ({.*})/;
9
-
10
8
  export default {
11
9
  components: { LabeledInput, LabeledSelect },
12
10
  mixins: [CreateEditView],
@@ -59,21 +57,10 @@ export default {
59
57
  return true;
60
58
  } catch (e) {
61
59
  if (e.error) {
62
- // Try and parse the response from Azure a couple of ways
63
- const msgMatch = e.error.match(AZURE_ERROR_MSG_REGEX);
64
-
65
- if (msgMatch?.length === 2) {
66
- return { errors: [msgMatch[1]] };
67
- } else {
68
- const jsonMatch = e.error.match(AZURE_ERROR_JSON_REGEX);
69
-
70
- if (jsonMatch?.length === 2) {
71
- try {
72
- const errorObj = JSON.parse(jsonMatch[1]);
60
+ const parsed = parseAzureError(e.error);
73
61
 
74
- return { errors: [errorObj.error_description] };
75
- } catch (e) {}
76
- }
62
+ if (parsed) {
63
+ return { errors: [parsed] };
77
64
  }
78
65
  }
79
66
 
@@ -0,0 +1,164 @@
1
+ <script lang="ts">
2
+ import Vue from 'vue';
3
+ import { mapGetters } from 'vuex';
4
+ import ResourceTable from '@shell/components/ResourceTable';
5
+ import { SECRET } from '@shell/config/types';
6
+ import { NAME as NAME_COL, NAMESPACE as NAMESPACE_COL, AGE, STATE } from '@shell/config/table-headers';
7
+ import Secret, { TYPES } from '@shell/models/secret';
8
+ import { Banner } from '@components/Banner';
9
+ import { STATES_ENUM } from '@shell/plugins/dashboard-store/resource-class';
10
+ import { BadgeState } from '@components/BadgeState';
11
+
12
+ interface Data {
13
+ schema: Object,
14
+ headers: Object[],
15
+ certs: Secret[],
16
+ pagingParams: {
17
+ pluralLabel: string,
18
+ singularLabel: string
19
+ }
20
+ }
21
+
22
+ export default Vue.extend<Data, any, any, any>({
23
+ components: {
24
+ ResourceTable, Banner, BadgeState
25
+ },
26
+
27
+ async fetch() {
28
+ // We're fetching secrets with a filter, this will clash with secrets in other contexts
29
+ this.$store.dispatch('cluster/forgetType', SECRET);
30
+
31
+ this.certs = await this.$store.dispatch('cluster/findAll', {
32
+ type: SECRET,
33
+ opt: {
34
+ watch: false,
35
+ // Note - urlOptions handles filter in a weird way
36
+ filter: { 'metadata.fields.1': TYPES.TLS }
37
+ }
38
+ });
39
+ },
40
+
41
+ data(): Data {
42
+ return {
43
+ schema: this.$store.getters['cluster/schemaFor'](SECRET),
44
+ headers: [
45
+ {
46
+ ...STATE,
47
+ formatter: null,
48
+ name: 'certState',
49
+ sort: ['certState', 'nameSort'],
50
+ value: 'certState',
51
+ },
52
+ NAME_COL,
53
+ NAMESPACE_COL,
54
+ {
55
+ name: 'cn',
56
+ labelKey: 'secret.certificate.cn',
57
+ value: (row: Secret) => {
58
+ if (!row.cn) {
59
+ return;
60
+ }
61
+
62
+ return row.cn + (row.unrepeatedSans.length ? ` ${ this.t('secret.certificate.plusMore', { n: row.unrepeatedSans.length }) }` : '');
63
+ },
64
+ sort: ['cn'],
65
+ search: ['cn'],
66
+ }, {
67
+ name: 'cert-expires2',
68
+ labelKey: 'secret.certificate.expiresDuration',
69
+ value: (row: Secret) => row.timeTilExpirationDate,
70
+ formatter: 'LiveDate',
71
+ sort: ['timeTilExpiration'],
72
+ search: ['timeTilExpiration'],
73
+ defaultSort: true,
74
+ width: 100
75
+ }, {
76
+ name: 'cert-expires',
77
+ labelKey: 'secret.certificate.expiresOn',
78
+ value: 'cachedCertInfo.notAfter',
79
+ formatter: 'Date',
80
+ sort: ['cachedCertInfo.notAfter'],
81
+ search: ['cachedCertInfo.notAfter'],
82
+ }, {
83
+ name: 'cert-lifetime',
84
+ labelKey: 'secret.certificate.lifetime',
85
+ value: (row: Secret) => row.certLifetime,
86
+ sort: ['certLifetime'],
87
+ search: ['certLifetime'],
88
+ },
89
+ AGE
90
+ ],
91
+ certs: [],
92
+ pagingParams: {
93
+ pluralLabel: this.t('secret.certificate.certificates'),
94
+ singularLabel: this.t('secret.certificate.certificate')
95
+ }
96
+ };
97
+ },
98
+
99
+ computed: {
100
+ ...mapGetters(['isAllNamespaces']),
101
+
102
+ expiredData() {
103
+ let expiring = 0;
104
+ let expired = 0;
105
+
106
+ for (let i = 0; i < this.certs.length; i++) {
107
+ const cert = this.certs[i];
108
+
109
+ if (cert.certState === STATES_ENUM.EXPIRING) {
110
+ expiring++;
111
+ }
112
+ if (cert.certState === STATES_ENUM.EXPIRED) {
113
+ expired++;
114
+ }
115
+ }
116
+
117
+ const filterWarning = !this.isAllNamespaces ? this.t('secret.certificate.warnings.filtered') : '';
118
+
119
+ return {
120
+ expiring: expiring ? this.t('secret.certificate.warnings.expiring', { count: expiring, filtered: !this.isAllNamespaces }) + filterWarning : '',
121
+ expired: expired ? this.t('secret.certificate.warnings.expired', { count: expired, filtered: !this.isAllNamespaces }) + filterWarning : '',
122
+ };
123
+ }
124
+ },
125
+
126
+ beforeDestroy() {
127
+ // We're fetching secrets with a filter, clear it so as to not clash with other contexts
128
+ this.$store.dispatch('cluster/forgetType', SECRET);
129
+ },
130
+
131
+ });
132
+ </script>
133
+
134
+ <template>
135
+ <div>
136
+ <Banner
137
+ v-if="expiredData.expiring"
138
+ color="warning"
139
+ :label="expiredData.expiring"
140
+ />
141
+ <Banner
142
+ v-if="expiredData.expired"
143
+ color="error"
144
+ :label="expiredData.expired"
145
+ />
146
+ <ResourceTable
147
+ :loading="$fetchState.pending"
148
+ :schema="schema"
149
+ :headers="headers"
150
+ :rows="certs"
151
+ :paging-label="'secret.certificate.paging'"
152
+ :paging-params="pagingParams"
153
+ >
154
+ <template #col:certState="{row}">
155
+ <td>
156
+ <BadgeState
157
+ :color="row.certStateBackground"
158
+ :label="row.certStateDisplay"
159
+ />
160
+ </td>
161
+ </template>
162
+ </ResourceTable>
163
+ </div>
164
+ </template>
@@ -130,28 +130,26 @@ export default {
130
130
  </script>
131
131
 
132
132
  <template>
133
- <client-only placeholder=" Loading...">
134
- <div
135
- class="code-mirror"
136
- :class="{['as-text-area']: asTextArea}"
137
- >
138
- <codemirror
139
- v-if="loaded"
140
- ref="codeMirrorRef"
141
- :value="value"
142
- :options="combinedOptions"
143
- :disabled="isDisabled"
144
- @ready="onReady"
145
- @input="onInput"
146
- @changes="onChanges"
147
- @focus="onFocus"
148
- @blur="onBlur"
149
- />
150
- <div v-else>
151
- Loading...
152
- </div>
133
+ <div
134
+ class="code-mirror"
135
+ :class="{['as-text-area']: asTextArea}"
136
+ >
137
+ <codemirror
138
+ v-if="loaded"
139
+ ref="codeMirrorRef"
140
+ :value="value"
141
+ :options="combinedOptions"
142
+ :disabled="isDisabled"
143
+ @ready="onReady"
144
+ @input="onInput"
145
+ @changes="onChanges"
146
+ @focus="onFocus"
147
+ @blur="onBlur"
148
+ />
149
+ <div v-else>
150
+ Loading...
153
151
  </div>
154
- </client-only>
152
+ </div>
155
153
  </template>
156
154
 
157
155
  <style lang="scss">
@@ -435,6 +435,7 @@ export default {
435
435
  v-for="(err, i) in errors"
436
436
  :key="i"
437
437
  color="error"
438
+ :data-testid="`error-banner${i}`"
438
439
  :label="stringify(mappedErrors[err].message)"
439
440
  :icon="mappedErrors[err].icon"
440
441
  :closable="true"
@@ -11,7 +11,7 @@ export default {
11
11
  const inStore = this.$store.getters['currentProduct'].inStore;
12
12
  let monitoringVersion = '';
13
13
 
14
- if (this.$store.getters[`${ inStore }/canList}`](CATALOG.APP)) {
14
+ if (this.$store.getters[`${ inStore }/canList`](CATALOG.APP)) {
15
15
  try {
16
16
  const res = await this.$store.dispatch(`${ inStore }/find`, { type: CATALOG.APP, id: 'cattle-monitoring-system/rancher-monitoring' });
17
17
 
@@ -391,7 +391,7 @@ export default {
391
391
  />
392
392
  <ResourceTable
393
393
  ref="table"
394
- class="table"
394
+ class="table project-namespaces-table"
395
395
  v-bind="$attrs"
396
396
  :schema="schema"
397
397
  :headers="headers"
@@ -505,6 +505,10 @@ export default {
505
505
  <style lang="scss" scoped>
506
506
  .project-namespaces {
507
507
  & ::v-deep {
508
+ .project-namespaces-table table {
509
+ table-layout: fixed;
510
+ }
511
+
508
512
  .project-name {
509
513
  line-height: 30px;
510
514
  }
@@ -514,6 +518,26 @@ export default {
514
518
  flex-direction: row;
515
519
  justify-content: space-between;
516
520
 
521
+ .group-tab {
522
+ max-width: calc(100% - 230px);
523
+ }
524
+
525
+ .project-name {
526
+ display: flex;
527
+ flex-direction: row;
528
+ align-items: center;
529
+
530
+ span:first-child {
531
+ padding-right: 8px;
532
+ }
533
+
534
+ span:last-child {
535
+ text-overflow: ellipsis;
536
+ overflow: hidden;
537
+ white-space: nowrap;
538
+ }
539
+ }
540
+
517
541
  &.has-description {
518
542
  .right {
519
543
  margin-top: 5px;
@@ -76,7 +76,7 @@ export default {
76
76
  for (let x = 0; x < Object.keys(stylesheet.cssRules).length; x++) {
77
77
  const cssRules = stylesheet.cssRules[x];
78
78
 
79
- if (cssRules.selectorText && ((currTheme === 'light' && cssRules.selectorText.includes('body') &&
79
+ if (cssRules.selectorText && ((currTheme === 'light' && (cssRules.selectorText.includes('body') || cssRules.selectorText.includes('BODY')) &&
80
80
  cssRules.selectorText.includes('.theme-light') && cssRules.style.cssText.includes('--link:')) ||
81
81
  (currTheme === 'dark' && cssRules.selectorText.includes('.theme-dark')))) {
82
82
  // grab the colors to be used on the icon from the css rules
@@ -15,10 +15,7 @@ export default {
15
15
  },
16
16
 
17
17
  async fetch() {
18
- this.clusters = await this.$store.dispatch('management/findAll', {
19
- type: MANAGEMENT.CLUSTER,
20
- opt: { url: MANAGEMENT.CLUSTER }
21
- });
18
+ this.clusters = await this.$store.dispatch('management/findAll', { type: MANAGEMENT.CLUSTER });
22
19
  },
23
20
 
24
21
  data() {
@@ -258,7 +258,7 @@ export default {
258
258
  }
259
259
 
260
260
  if ( this.tabbed === 'multiple' ) {
261
- return this.groups.length > 1;
261
+ return !!this.groups.length;
262
262
  }
263
263
 
264
264
  return true;
@@ -422,6 +422,7 @@ export default {
422
422
  <span v-if="value.detailPageHeaderActionOverride && value.detailPageHeaderActionOverride(realMode)">{{ value.detailPageHeaderActionOverride(realMode) }}</span>
423
423
  <t
424
424
  v-else
425
+ class="mastehead-resource-title"
425
426
  :k="'resourceDetail.header.' + realMode"
426
427
  :subtype="resourceSubtype"
427
428
  :name="displayName"
@@ -544,6 +545,10 @@ export default {
544
545
 
545
546
  HEADER {
546
547
  margin: 0;
548
+
549
+ .title {
550
+ overflow: hidden;
551
+ }
547
552
  }
548
553
 
549
554
  .primaryheader {
@@ -553,6 +558,17 @@ export default {
553
558
 
554
559
  h1 {
555
560
  margin: 0;
561
+ overflow: hidden;
562
+ display: flex;
563
+ flex-direction: row;
564
+ align-items: center;
565
+
566
+ .mastehead-resource-title {
567
+ padding: 0 8px;
568
+ text-overflow: ellipsis;
569
+ overflow: hidden;
570
+ white-space: nowrap;
571
+ }
556
572
  }
557
573
  }
558
574
 
@@ -575,9 +591,6 @@ export default {
575
591
 
576
592
  .masthead-state {
577
593
  font-size: initial;
578
- display: inline-block;
579
- position: relative;
580
- top: -2px;
581
594
  }
582
595
 
583
596
  .masthead-istio {