@rancher/shell 0.1.3 → 0.1.21

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 (245) hide show
  1. package/assets/brand/suse/dark/rancher-logo.svg +1 -148
  2. package/assets/brand/suse/favicon.png +0 -0
  3. package/assets/brand/suse/rancher-logo.svg +1 -130
  4. package/assets/images/featured/img1.jpg +0 -0
  5. package/assets/images/featured.jpg +0 -0
  6. package/assets/images/generic-plugin.svg +1 -0
  7. package/assets/styles/themes/_dark.scss +3 -0
  8. package/assets/styles/themes/_light.scss +3 -0
  9. package/assets/styles/themes/_suse.scss +1 -1
  10. package/assets/translations/en-us.yaml +219 -47
  11. package/assets/translations/zh-hans.yaml +21 -24
  12. package/components/AsyncButton.vue +17 -2
  13. package/components/ButtonDropdown.vue +4 -0
  14. package/components/Carousel.vue +291 -0
  15. package/components/CommunityLinks.vue +64 -22
  16. package/components/CruResource.vue +11 -3
  17. package/components/Dialog.vue +102 -0
  18. package/components/ExplorerMembers.vue +2 -4
  19. package/components/ExplorerProjectsNamespaces.vue +25 -9
  20. package/components/IconMessage.vue +9 -1
  21. package/components/LazyImage.vue +21 -8
  22. package/components/LocaleSelector.vue +62 -29
  23. package/components/PromptRemove.vue +2 -2
  24. package/components/ResourceList/Masthead.vue +21 -1
  25. package/components/ResourceList/ResourceLoadingIndicator.vue +0 -8
  26. package/components/ResourceList/index.vue +9 -23
  27. package/components/ResourceTable.vue +7 -2
  28. package/components/SimpleBox.vue +6 -4
  29. package/components/SortableTable/index.vue +18 -25
  30. package/components/Tabbed/Tab.vue +5 -0
  31. package/components/Tabbed/index.vue +54 -9
  32. package/components/TypeDescription.vue +10 -1
  33. package/components/auth/Principal.vue +1 -0
  34. package/components/fleet/FleetBundles.vue +8 -3
  35. package/components/fleet/FleetClusters.vue +6 -0
  36. package/components/fleet/FleetRepos.vue +7 -1
  37. package/components/fleet/FleetSummary.vue +6 -0
  38. package/components/form/Command.vue +5 -0
  39. package/components/form/EnvVars.vue +5 -0
  40. package/components/form/KeyValue.vue +80 -58
  41. package/components/form/NameNsDescription.vue +13 -5
  42. package/components/form/NodeScheduling.vue +6 -1
  43. package/components/form/PodAffinity.vue +5 -0
  44. package/components/form/ResourceTabs/index.vue +5 -1
  45. package/components/form/ServiceNameSelect.vue +5 -0
  46. package/components/form/ValueFromResource.vue +7 -1
  47. package/components/formatter/ClusterLink.vue +3 -7
  48. package/components/nav/NamespaceFilter.vue +3 -3
  49. package/components/nav/TopLevelMenu.vue +12 -29
  50. package/config/home-links.js +155 -0
  51. package/config/labels-annotations.js +2 -1
  52. package/config/private-label.js +1 -1
  53. package/config/product/explorer.js +5 -4
  54. package/config/product/legacy.js +0 -47
  55. package/config/product/manager.js +0 -2
  56. package/config/product/multi-cluster-apps.js +0 -12
  57. package/config/product/settings.js +12 -1
  58. package/config/product/uiplugins.js +17 -0
  59. package/config/settings.js +23 -2
  60. package/config/types.js +5 -1
  61. package/config/uiplugins.js +117 -0
  62. package/config/version.js +17 -0
  63. package/content/docs/en-us/getting-started.md +1 -26
  64. package/core/plugin.ts +12 -0
  65. package/core/plugins.js +38 -2
  66. package/core/types.ts +6 -0
  67. package/creators/app/{.eslintignore → files/.eslintignore} +0 -0
  68. package/creators/app/{.eslintrc.js → files/.eslintrc.js} +0 -0
  69. package/creators/app/{.vscode → files/.vscode}/settings.json +0 -0
  70. package/creators/app/{babel.config.js → files/babel.config.js} +0 -0
  71. package/creators/app/{nuxt.config.js → files/nuxt.config.js} +0 -0
  72. package/creators/app/{tsconfig.json → files/tsconfig.json} +2 -1
  73. package/creators/app/init +16 -17
  74. package/creators/app/package.json +6 -0
  75. package/creators/pkg/{babel.config.js → files/babel.config.js} +0 -0
  76. package/creators/pkg/{index.ts → files/index.ts} +0 -0
  77. package/creators/pkg/{tsconfig.json → files/tsconfig.json} +13 -12
  78. package/creators/pkg/{vue.config.js → files/vue.config.js} +0 -0
  79. package/creators/pkg/init +1 -1
  80. package/creators/update/init +54 -0
  81. package/creators/update/package.json +20 -0
  82. package/creators/update/upgrade +56 -0
  83. package/creators/update/yarn-error.log +54 -0
  84. package/detail/provisioning.cattle.io.cluster.vue +3 -3
  85. package/detail/workload/index.vue +3 -2
  86. package/dialog/DiagnosticTimingsDialog.vue +116 -0
  87. package/dialog/RotateCertificatesDialog.vue +9 -3
  88. package/edit/auth/azuread.vue +28 -9
  89. package/edit/networking.k8s.io.ingress/index.vue +2 -2
  90. package/edit/persistentvolume/index.vue +51 -13
  91. package/edit/persistentvolumeclaim.vue +31 -13
  92. package/edit/pod.vue +27 -0
  93. package/edit/provisioning.cattle.io.cluster/rke2.vue +103 -24
  94. package/edit/service.vue +7 -5
  95. package/edit/workload/__tests__/Upgrading.test.ts +1 -0
  96. package/edit/workload/index.vue +32 -10
  97. package/edit/workload/mixins/workload.js +121 -126
  98. package/edit/workload/storage/ContainerMountPaths.vue +240 -0
  99. package/edit/workload/storage/Mount.vue +1 -0
  100. package/edit/workload/storage/awsElasticBlockStore.vue +20 -1
  101. package/edit/workload/storage/azureDisk.vue +22 -2
  102. package/edit/workload/storage/azureFile.vue +20 -2
  103. package/edit/workload/storage/csi/index.vue +23 -1
  104. package/edit/workload/storage/gcePersistentDisk.vue +20 -2
  105. package/edit/workload/storage/index.vue +33 -65
  106. package/edit/workload/storage/persistentVolumeClaim/index.vue +5 -0
  107. package/edit/workload/storage/secret.vue +6 -1
  108. package/edit/workload/storage/vsphereVolume.vue +11 -1
  109. package/layouts/default.vue +14 -8
  110. package/layouts/home.vue +9 -4
  111. package/layouts/plain.vue +10 -5
  112. package/list/catalog.cattle.io.app.vue +10 -9
  113. package/list/catalog.cattle.io.clusterrepo.vue +6 -61
  114. package/list/cis.cattle.io.clusterscan.vue +12 -12
  115. package/list/fleet.cattle.io.bundle.vue +33 -28
  116. package/list/fleet.cattle.io.cluster.vue +26 -22
  117. package/list/fleet.cattle.io.clustergroup.vue +6 -0
  118. package/list/fleet.cattle.io.clusterregistrationtoken.vue +28 -24
  119. package/list/fleet.cattle.io.gitrepo.vue +25 -14
  120. package/list/helm.cattle.io.projecthelmchart.vue +52 -33
  121. package/list/logging.banzaicloud.io.clusterflow.vue +7 -12
  122. package/list/logging.banzaicloud.io.flow.vue +7 -14
  123. package/list/management.cattle.io.cluster.vue +26 -15
  124. package/list/management.cattle.io.feature.vue +13 -8
  125. package/list/management.cattle.io.setting.vue +3 -3
  126. package/list/management.cattle.io.user.vue +38 -19
  127. package/list/monitoring.coreos.com.alertmanagerconfig.vue +8 -15
  128. package/list/namespace.vue +14 -1
  129. package/list/node.vue +13 -16
  130. package/list/persistentvolume.vue +16 -9
  131. package/list/persistentvolumeclaim.vue +5 -8
  132. package/list/provisioning.cattle.io.cluster.vue +35 -9
  133. package/list/service.vue +24 -12
  134. package/list/ui.cattle.io.navlink.vue +6 -0
  135. package/list/workload.vue +2 -2
  136. package/machine-config/harvester.vue +5 -3
  137. package/middleware/authenticated.js +6 -0
  138. package/mixins/resource-fetch.js +12 -18
  139. package/mixins/resource-manager.js +126 -0
  140. package/models/catalog.cattle.io.uiplugin.js +38 -0
  141. package/models/cluster/node.js +25 -2
  142. package/models/fleet.cattle.io.bundle.js +1 -1
  143. package/models/harvesterhci.io.management.cluster.js +11 -5
  144. package/models/pod.js +15 -5
  145. package/models/provisioning.cattle.io.cluster.js +16 -6
  146. package/models/workload.js +5 -3
  147. package/models/workload.service.js +10 -0
  148. package/nuxt.config.js +70 -25
  149. package/package.json +108 -109
  150. package/pages/auth/login.vue +11 -1
  151. package/pages/auth/verify.vue +9 -0
  152. package/pages/c/_cluster/apps/charts/index.vue +46 -1
  153. package/pages/c/_cluster/apps/charts/install.vue +10 -9
  154. package/pages/c/_cluster/explorer/index.vue +72 -9
  155. package/pages/c/_cluster/explorer/tools/index.vue +12 -5
  156. package/pages/c/_cluster/mcapps/index.vue +1 -1
  157. package/pages/c/_cluster/settings/DefaultLinksEditor.vue +108 -0
  158. package/pages/c/_cluster/settings/brand.vue +0 -40
  159. package/pages/c/_cluster/settings/links.vue +152 -0
  160. package/pages/c/_cluster/settings/performance.vue +90 -7
  161. package/pages/c/_cluster/uiplugins/DeveloperInstallDialog.vue +232 -0
  162. package/pages/c/_cluster/uiplugins/InstallDialog.vue +293 -0
  163. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +300 -0
  164. package/pages/c/_cluster/uiplugins/RemoveUIPlugins.vue +125 -0
  165. package/pages/c/_cluster/uiplugins/SetupUIPlugins.vue +261 -0
  166. package/pages/c/_cluster/uiplugins/UninstallDialog.vue +122 -0
  167. package/pages/c/_cluster/uiplugins/index.vue +808 -0
  168. package/pages/diagnostic.vue +185 -101
  169. package/pages/docs/_doc.vue +3 -1
  170. package/pages/home.vue +21 -56
  171. package/pages/prefs.vue +108 -88
  172. package/pages/safeMode.vue +17 -0
  173. package/pages/support/index.vue +34 -137
  174. package/pkg/dynamic-importer.lib.js +4 -0
  175. package/plugins/dashboard-store/actions.js +19 -0
  176. package/plugins/dashboard-store/getters.js +20 -3
  177. package/plugins/dashboard-store/mutations.js +13 -7
  178. package/plugins/dashboard-store/resource-class.js +2 -2
  179. package/plugins/formatters.js +15 -0
  180. package/plugins/plugin.js +61 -6
  181. package/plugins/steve/getters.js +12 -0
  182. package/plugins/steve/mutations.js +1 -1
  183. package/plugins/steve/subscribe.js +94 -72
  184. package/plugins/steve/web-worker.steve-sub-worker.js +24 -15
  185. package/plugins/version.js +21 -0
  186. package/promptRemove/management.cattle.io.globalrole.vue +47 -0
  187. package/promptRemove/management.cattle.io.roletemplate.vue +47 -0
  188. package/promptRemove/mixin/roleDeletionCheck.js +97 -0
  189. package/rancher-components/Form/TextArea/TextAreaAutoGrow.vue +6 -7
  190. package/rancher-components/components/BadgeState/BadgeState.spec.ts +12 -0
  191. package/rancher-components/components/BadgeState/BadgeState.vue +107 -0
  192. package/rancher-components/components/BadgeState/index.ts +1 -0
  193. package/rancher-components/components/Banner/Banner.test.ts +13 -0
  194. package/rancher-components/components/Banner/Banner.vue +163 -0
  195. package/rancher-components/components/Banner/index.ts +1 -0
  196. package/rancher-components/components/Card/Card.vue +150 -0
  197. package/rancher-components/components/Card/index.ts +1 -0
  198. package/rancher-components/components/Form/Checkbox/Checkbox.test.ts +77 -0
  199. package/rancher-components/components/Form/Checkbox/Checkbox.vue +395 -0
  200. package/rancher-components/components/Form/Checkbox/index.ts +1 -0
  201. package/rancher-components/components/Form/LabeledInput/LabeledInput.test.ts +29 -0
  202. package/rancher-components/components/Form/LabeledInput/LabeledInput.vue +343 -0
  203. package/rancher-components/components/Form/LabeledInput/index.ts +1 -0
  204. package/rancher-components/components/Form/Radio/RadioButton.vue +270 -0
  205. package/rancher-components/components/Form/Radio/RadioGroup.vue +235 -0
  206. package/rancher-components/components/Form/Radio/index.ts +2 -0
  207. package/rancher-components/components/Form/TextArea/TextAreaAutoGrow.vue +168 -0
  208. package/rancher-components/components/Form/TextArea/index.ts +1 -0
  209. package/rancher-components/components/Form/ToggleSwitch/ToggleSwitch.test.ts +107 -0
  210. package/rancher-components/components/Form/ToggleSwitch/ToggleSwitch.vue +137 -0
  211. package/rancher-components/components/Form/ToggleSwitch/index.ts +1 -0
  212. package/rancher-components/components/Form/index.ts +5 -0
  213. package/rancher-components/components/LabeledTooltip/LabeledTooltip.vue +137 -0
  214. package/rancher-components/components/LabeledTooltip/index.ts +1 -0
  215. package/scripts/publish-shell.sh +40 -7
  216. package/scripts/record-deps.js +37 -0
  217. package/scripts/sync-shell-deps +37 -0
  218. package/scripts/test-plugins-build.sh +8 -5
  219. package/scripts/typegen.sh +84 -0
  220. package/store/auth.js +3 -0
  221. package/store/catalog.js +9 -8
  222. package/store/i18n.js +10 -1
  223. package/store/index.js +12 -3
  224. package/store/prefs.js +16 -0
  225. package/store/type-map.js +32 -5
  226. package/store/uiplugins.ts +15 -61
  227. package/types/shell/index.d.ts +3046 -0
  228. package/utils/__tests__/object.test.ts +0 -24
  229. package/utils/__tests__/selector.test.ts +1 -1
  230. package/utils/dynamic-importer.js +4 -0
  231. package/utils/favicon.js +8 -2
  232. package/utils/gc/gc-interval.ts +40 -0
  233. package/utils/gc/gc-root-store.js +76 -0
  234. package/utils/gc/gc-route-changed.ts +44 -0
  235. package/utils/gc/gc-types.ts +21 -0
  236. package/utils/gc/gc.ts +282 -0
  237. package/utils/grafana.js +2 -6
  238. package/utils/socket.js +41 -20
  239. package/utils/string.js +1 -7
  240. package/utils/validators/formRules/__tests__/index.test.ts +108 -0
  241. package/utils/validators/formRules/index.ts +9 -1
  242. package/config/footer.js +0 -19
  243. package/creators/pkg/nuxt.config.js +0 -6
  244. package/pages/plugins.vue +0 -387
  245. package/server/verdaccio-middleware.js +0 -56
@@ -1,3 +1,4 @@
1
+ import { mapGetters } from 'vuex';
1
2
  import omitBy from 'lodash/omitBy';
2
3
  import { cleanUp } from '@shell/utils/object';
3
4
  import {
@@ -9,10 +10,11 @@ import {
9
10
  PVC,
10
11
  SERVICE_ACCOUNT,
11
12
  CAPI,
13
+ POD,
12
14
  } from '@shell/config/types';
13
15
  import Tab from '@shell/components/Tabbed/Tab';
14
16
  import CreateEditView from '@shell/mixins/create-edit-view';
15
- import { allHash } from '@shell/utils/promise';
17
+ import ResourceManager from '@shell/mixins/resource-manager';
16
18
  import LabeledSelect from '@shell/components/form/LabeledSelect';
17
19
  import { LabeledInput } from '@components/Form/LabeledInput';
18
20
  import ServiceNameSelect from '@shell/components/form/ServiceNameSelect';
@@ -28,7 +30,7 @@ import WorkloadPorts from '@shell/components/form/WorkloadPorts';
28
30
  import ContainerResourceLimit from '@shell/components/ContainerResourceLimit';
29
31
  import KeyValue from '@shell/components/form/KeyValue';
30
32
  import Tabbed from '@shell/components/Tabbed';
31
- import { mapGetters } from 'vuex';
33
+
32
34
  import NodeScheduling from '@shell/components/form/NodeScheduling';
33
35
  import PodAffinity from '@shell/components/form/PodAffinity';
34
36
  import Tolerations from '@shell/components/form/Tolerations';
@@ -36,6 +38,7 @@ import CruResource from '@shell/components/CruResource';
36
38
  import Command from '@shell/components/form/Command';
37
39
  import LifecycleHooks from '@shell/components/form/LifecycleHooks';
38
40
  import Storage from '@shell/edit/workload/storage';
41
+ import ContainerMountPaths from '@shell/edit/workload/storage/ContainerMountPaths.vue';
39
42
  import Labels from '@shell/components/form/Labels';
40
43
  import { RadioGroup } from '@components/Form/Radio';
41
44
  import { UI_MANAGED } from '@shell/config/labels-annotations';
@@ -89,9 +92,10 @@ export default {
89
92
  Upgrading,
90
93
  VolumeClaimTemplate,
91
94
  WorkloadPorts,
95
+ ContainerMountPaths
92
96
  },
93
97
 
94
- mixins: [CreateEditView],
98
+ mixins: [CreateEditView, ResourceManager],
95
99
 
96
100
  props: {
97
101
  value: {
@@ -115,40 +119,14 @@ export default {
115
119
  },
116
120
 
117
121
  async fetch() {
118
- const requests = { rancherClusters: this.$store.dispatch('management/findAll', { type: CAPI.RANCHER_CLUSTER }) };
119
- const needed = {
120
- configMaps: CONFIG_MAP,
121
- nodes: NODE,
122
- services: SERVICE,
123
- pvcs: PVC,
124
- sas: SERVICE_ACCOUNT,
125
- secrets: SECRET,
126
- };
127
-
128
- // Only fetch types if the user can see them
129
- Object.keys(needed).forEach((key) => {
130
- const type = needed[key];
131
-
132
- if (this.$store.getters['cluster/schemaFor'](type)) {
133
- requests[key] = this.$store.dispatch('cluster/findAll', { type });
134
- }
135
- });
136
-
137
- const hash = await allHash(requests);
122
+ await this.$store.dispatch('management/findAll', { type: CAPI.RANCHER_CLUSTER });
138
123
 
139
- this.servicesOwned = hash.services ? await this.value.getServicesOwned() : [];
140
-
141
- this.allSecrets = hash.secrets || [];
142
- this.allConfigMaps = hash.configMaps || [];
143
- this.allNodeObjects = hash.nodes || [];
144
- this.allNodes = this.allNodeObjects.map(node => node.id);
145
- this.allServices = hash.services || [];
146
- this.pvcs = hash.pvcs || [];
147
- this.sas = hash.sas || [];
124
+ // don't block UI for these resources
125
+ this.resourceManagerFetchSecondaryResources(this.secondaryResourceData);
126
+ this.servicesOwned = await this.value.getServicesOwned();
148
127
  },
149
128
 
150
129
  data() {
151
- let defaultTab;
152
130
  let type = this.$route.params.resource;
153
131
  const createSidecar = !!this.$route.query.sidecar;
154
132
  const isInitContainer = !!this.$route.query.init;
@@ -159,28 +137,27 @@ export default {
159
137
 
160
138
  if (!this.value.spec) {
161
139
  this.value.spec = {};
162
- if (this.value.type === WORKLOAD_TYPES.POD) {
140
+ if (this.value.type === POD) {
163
141
  const podContainers = [{
164
142
  imagePullPolicy: 'Always',
165
143
  name: `container-0`,
166
144
  }];
167
145
 
168
- defaultTab = 'container-0';
146
+ const metadata = { ...this.value.metadata };
169
147
 
170
- const podSpec = { template: { spec: { containers: podContainers, initContainers: [] } } };
148
+ const podSpec = { template: { spec: { containers: podContainers, initContainers: [] }, metadata } };
171
149
 
172
150
  this.$set(this.value, 'spec', podSpec);
173
151
  }
174
152
  }
175
153
 
176
- if (this.mode === _CREATE) {
177
- defaultTab = 'container-0';
178
- }
179
-
154
+ // EDIT view for POD
155
+ // Transform it from POD world to workload
180
156
  if ((this.mode === _EDIT || this.mode === _VIEW ) && this.value.type === 'pod' ) {
181
157
  const podSpec = { ...this.value.spec };
158
+ const metadata = { ...this.value.metadata };
182
159
 
183
- this.$set(this.value.spec, 'template', { spec: podSpec });
160
+ this.$set(this.value.spec, 'template', { spec: podSpec, metadata });
184
161
  }
185
162
 
186
163
  const spec = this.value.spec;
@@ -214,7 +191,6 @@ export default {
214
191
  imagePullPolicy: 'Always',
215
192
  name: `container-${ allContainers.length }`,
216
193
  });
217
- defaultTab = 'container-0';
218
194
 
219
195
  containers = podTemplateSpec.initContainers;
220
196
  }
@@ -224,8 +200,6 @@ export default {
224
200
  name: `container-${ allContainers.length }`,
225
201
  };
226
202
 
227
- defaultTab = 'container-0';
228
-
229
203
  containers.push(container);
230
204
  } else {
231
205
  container = containers[0];
@@ -235,42 +209,93 @@ export default {
235
209
  this.selectContainer(container);
236
210
 
237
211
  return {
238
- allConfigMaps: [],
239
- allNodes: null,
240
- allNodeObjects: [],
241
- allSecrets: [],
242
- allServices: [],
243
- name: this.value?.metadata?.name || null,
244
- pvcs: [],
245
- sas: [],
246
- showTabs: false,
247
- pullPolicyOptions: ['Always', 'IfNotPresent', 'Never'],
212
+ secondaryResourceData: {
213
+ namespace: this.value?.metadata?.namespace || null,
214
+ data: {
215
+ [CONFIG_MAP]: { applyTo: [{ var: 'namespacedConfigMaps' }] },
216
+ [PVC]: { applyTo: [{ var: 'pvcs' }] },
217
+ [SERVICE_ACCOUNT]: { applyTo: [{ var: 'namespacedServiceNames' }] },
218
+ [SECRET]: {
219
+ applyTo: [
220
+ { var: 'namespacedSecrets' },
221
+ {
222
+ var: 'imagePullNamespacedSecrets',
223
+ parsingFunc: (data) => {
224
+ return data.filter(secret => (secret._type === SECRET_TYPES.DOCKER || secret._type === SECRET_TYPES.DOCKER_JSON));
225
+ }
226
+ }
227
+ ]
228
+ },
229
+ [NODE]: {
230
+ applyTo: [
231
+ { var: 'allNodeObjects' },
232
+ {
233
+ var: 'allNodes',
234
+ parsingFunc: (data) => {
235
+ return data.map(node => node.id);
236
+ }
237
+ }
238
+ ]
239
+ },
240
+ [SERVICE]: {
241
+ applyTo: [
242
+ { var: 'allServices' },
243
+ {
244
+ var: 'headlessServices',
245
+ parsingFunc: (data) => {
246
+ return data.filter(service => service.spec.clusterIP === 'None');
247
+ }
248
+ }
249
+ ]
250
+ },
251
+ }
252
+ },
253
+ namespacedConfigMaps: [],
254
+ allNodes: null,
255
+ allNodeObjects: [],
256
+ namespacedSecrets: [],
257
+ imagePullNamespacedSecrets: [],
258
+ allServices: [],
259
+ headlessServices: [],
260
+ name: this.value?.metadata?.name || null,
261
+ pvcs: [],
262
+ namespacedServiceNames: [],
263
+ showTabs: false,
264
+ pullPolicyOptions: ['Always', 'IfNotPresent', 'Never'],
248
265
  spec,
249
266
  type,
250
- servicesOwned: [],
251
- servicesToRemove: [],
252
- portsForServices: [],
267
+ servicesOwned: [],
268
+ servicesToRemove: [],
269
+ portsForServices: [],
253
270
  isInitContainer,
254
271
  container,
255
- containerChange: 0,
256
- tabChange: 0,
257
- podFsGroup: podTemplateSpec.securityContext?.fsGroup,
258
- savePvcHookName: 'savePvcHook',
259
- tabWeightMap: TAB_WEIGHT_MAP,
260
- fvFormRuleSets: [{
272
+ containerChange: 0,
273
+ tabChange: 0,
274
+ podFsGroup: podTemplateSpec.securityContext?.fsGroup,
275
+ savePvcHookName: 'savePvcHook',
276
+ tabWeightMap: TAB_WEIGHT_MAP,
277
+ fvFormRuleSets: [{
261
278
  path: 'image', rootObject: this.container, rules: ['required'], translationKey: 'workload.container.image'
262
279
  }],
263
280
  fvReportedValidationPaths: ['spec'],
264
- defaultTab
265
-
281
+ isNamespaceNew: false,
266
282
  };
267
283
  },
268
284
 
269
285
  computed: {
286
+ ...mapGetters(['currentCluster']),
270
287
  tabErrors() {
271
288
  return { general: this.fvGetPathErrors(['image'])?.length > 0 };
272
289
  },
273
290
 
291
+ defaultTab() {
292
+ if (!!this.$route.query.sidecar || this.$route.query.init || this.mode === _CREATE) {
293
+ return 'container-0';
294
+ }
295
+
296
+ return this.allContainers.length ? this.allContainers[0].name : '';
297
+ },
298
+
274
299
  isEdit() {
275
300
  return this.mode === _EDIT;
276
301
  },
@@ -297,7 +322,7 @@ export default {
297
322
  },
298
323
 
299
324
  isPod() {
300
- return this.value.type === WORKLOAD_TYPES.POD;
325
+ return this.value.type === POD;
301
326
  },
302
327
 
303
328
  isStatefulSet() {
@@ -326,13 +351,13 @@ export default {
326
351
  }
327
352
 
328
353
  return this.spec.jobTemplate.metadata.labels;
329
- } else {
330
- if (!this.spec.template.metadata) {
331
- this.$set(this.spec.template, 'metadata', { labels: {} });
332
- }
354
+ }
333
355
 
334
- return this.spec.template.metadata.labels;
356
+ if (!this.spec.template.metadata) {
357
+ this.$set(this.spec.template, 'metadata', { labels: {} });
335
358
  }
359
+
360
+ return this.spec.template.metadata.labels;
336
361
  },
337
362
  set(neu) {
338
363
  if (this.isCronJob) {
@@ -351,13 +376,12 @@ export default {
351
376
  }
352
377
 
353
378
  return this.spec.jobTemplate.metadata.annotations;
354
- } else {
355
- if (!this.spec.template.metadata) {
356
- this.$set(this.spec.template, 'metadata', { annotations: {} });
357
- }
358
-
359
- return this.spec.template.metadata.annotations;
360
379
  }
380
+ if (!this.spec.template.metadata) {
381
+ this.$set(this.spec.template, 'metadata', { annotations: {} });
382
+ }
383
+
384
+ return this.spec.template.metadata.annotations;
361
385
  },
362
386
  set(neu) {
363
387
  if (this.isCronJob) {
@@ -467,55 +491,6 @@ export default {
467
491
  return this.$store.getters['cluster/schemaFor'](this.type);
468
492
  },
469
493
 
470
- namespacedSecrets() {
471
- const namespace = this.value?.metadata?.namespace;
472
-
473
- if (namespace) {
474
- return this.allSecrets.filter(
475
- secret => secret.metadata.namespace === namespace
476
- );
477
- } else {
478
- return this.allSecrets;
479
- }
480
- },
481
-
482
- imagePullNamespacedSecrets() {
483
- const namespace = this.value?.metadata?.namespace;
484
-
485
- return this.allSecrets.filter(secret => secret.metadata.namespace === namespace && (secret._type === SECRET_TYPES.DOCKER || secret._type === SECRET_TYPES.DOCKER_JSON));
486
- },
487
-
488
- namespacedConfigMaps() {
489
- const namespace = this.value?.metadata?.namespace;
490
-
491
- if (namespace) {
492
- return this.allConfigMaps.filter(
493
- configMap => configMap.metadata.namespace === namespace
494
- );
495
- } else {
496
- return this.allConfigMaps;
497
- }
498
- },
499
-
500
- namespacedServiceNames() {
501
- const { namespace } = this.value?.metadata;
502
-
503
- if (namespace) {
504
- return this.sas.filter(
505
- serviceName => serviceName.metadata.namespace === namespace
506
- );
507
- } else {
508
- return this.sas;
509
- }
510
- },
511
-
512
- headlessServices() {
513
- return this.allServices.filter(
514
- service => service.spec.clusterIP === 'None' &&
515
- service.metadata.namespace === this.value.metadata.namespace
516
- );
517
- },
518
-
519
494
  workloadTypes() {
520
495
  return omitBy(WORKLOAD_TYPES, (type) => {
521
496
  return (
@@ -558,6 +533,25 @@ export default {
558
533
  },
559
534
 
560
535
  watch: {
536
+ async 'value.metadata.namespace'(neu) {
537
+ if (this.isNamespaceNew) {
538
+ // we don't need to re-fetch namespace specific (or non-namespace specific) resources when the namespace hasn't been created yet
539
+ return;
540
+ }
541
+ this.secondaryResourceData.namespace = neu;
542
+ // Fetch resources that are namespace specific, we don't need to re-fetch non-namespaced resources on namespace change
543
+ this.resourceManagerFetchSecondaryResources(this.secondaryResourceData, true);
544
+
545
+ this.servicesOwned = await this.value.getServicesOwned();
546
+ },
547
+
548
+ isNamespaceNew(neu, old) {
549
+ if (!old && neu) {
550
+ // As the namespace is new any resource that's been fetched with a namespace is now invalid
551
+ this.resourceManagerClearSecondaryResources(this.secondaryResourceData, true);
552
+ }
553
+ },
554
+
561
555
  type(neu, old) {
562
556
  const template =
563
557
  old === WORKLOAD_TYPES.CRON_JOB ? this.spec?.jobTemplate?.spec?.template : this.spec?.template;
@@ -689,6 +683,7 @@ export default {
689
683
  template = this.spec.template;
690
684
  }
691
685
 
686
+ // WORKLOADS
692
687
  if (
693
688
  this.type !== WORKLOAD_TYPES.JOB &&
694
689
  this.type !== WORKLOAD_TYPES.CRON_JOB &&
@@ -0,0 +1,240 @@
1
+ <script>
2
+ import { PVC } from '@shell/config/types';
3
+ import ButtonDropdown from '@shell/components/ButtonDropdown';
4
+ import Mount from '@shell/edit/workload/storage/Mount';
5
+ import { _VIEW } from '@shell/config/query-params';
6
+ import ArrayListGrouped from '@shell/components/form/ArrayListGrouped';
7
+ import { randomStr } from '@shell/utils/string';
8
+
9
+ export default {
10
+ name: 'ContainerMountPaths',
11
+ components: {
12
+ ArrayListGrouped, ButtonDropdown, Mount
13
+ },
14
+
15
+ props: {
16
+ mode: {
17
+ type: String,
18
+ default: 'create',
19
+ },
20
+
21
+ // pod spec
22
+ value: {
23
+ type: Object,
24
+ default: () => {
25
+ return {};
26
+ },
27
+ },
28
+
29
+ container: {
30
+ type: Object,
31
+ default: () => {
32
+ return {};
33
+ },
34
+ },
35
+ },
36
+
37
+ async fetch() {
38
+ if ( this.$store.getters['cluster/schemaFor'](PVC) ) {
39
+ this.pvcs = await this.$store.dispatch('cluster/findAll', { type: PVC });
40
+ } else {
41
+ this.pvcs = [];
42
+ }
43
+ },
44
+
45
+ data() {
46
+ this.initializeStorage();
47
+
48
+ return {
49
+ containerVolumes: [],
50
+ pvcs: [],
51
+ storageVolumes: this.getStorageVolumes(),
52
+ selectedContainerVolumes: this.getSelectedContainerVolumes()
53
+ };
54
+ },
55
+
56
+ computed: {
57
+ isView() {
58
+ return this.mode === _VIEW;
59
+ },
60
+
61
+ availableVolumeOptions() {
62
+ const containerVolumes = this.container.volumeMounts.map(item => item.name);
63
+
64
+ return this.value.volumes.filter(vol => !containerVolumes.includes(vol.name)).map((item) => {
65
+ return {
66
+ label: `${ item.name } (${ this.headerFor(item) })`,
67
+ action: this.selectVolume,
68
+ value: item.name
69
+ };
70
+ });
71
+ },
72
+ },
73
+
74
+ watch: {
75
+ value(neu, old) {
76
+ this.selectedVolumes = this.getSelectedContainerVolumes();
77
+ },
78
+ storageVolumes(neu, old) {
79
+ // removeObjects(this.value.volumes, old);
80
+ // addObjects(this.value.volumes, neu);
81
+ const names = neu.reduce((all, each) => {
82
+ all.push(each.name);
83
+
84
+ return all;
85
+ }, []);
86
+
87
+ this.container.volumeMounts = this.container.volumeMounts.filter(mount => names.includes(mount.name));
88
+ },
89
+
90
+ selectedContainerVolumes(neu, old) {
91
+ // removeObjects(this.value.volumes, old);
92
+ // addObjects(this.value.volumes, neu);
93
+ const names = neu.map(item => item.name);
94
+
95
+ this.container.volumeMounts = this.container.volumeMounts.filter(mount => names.includes(mount.name));
96
+ }
97
+
98
+ },
99
+
100
+ methods: {
101
+ /**
102
+ * Initialize missing values for the container
103
+ */
104
+ initializeStorage() {
105
+ if (!this.container.volumeMounts) {
106
+ this.$set(this.container, 'volumeMounts', []);
107
+ }
108
+ if (!this.value.volumes) {
109
+ this.$set(this.value, 'volumes', []);
110
+ }
111
+ },
112
+
113
+ /**
114
+ * Get existing paired storage volumes
115
+ */
116
+ getStorageVolumes() {
117
+ // Extract volume mounts to map storage volumes
118
+ const { volumeMounts = [] } = this.container;
119
+ const names = volumeMounts.map(({ name }) => name);
120
+
121
+ // Extract storage volumes to allow mutation, if matches mount map
122
+ return this.value.volumes.filter(volume => names.includes(volume.name));
123
+ },
124
+
125
+ getSelectedContainerVolumes() {
126
+ // Extract volume mounts to map storage volumes
127
+ const { volumeMounts = [] } = this.container;
128
+ const names = volumeMounts.map(({ name }) => name);
129
+
130
+ // Extract storage volumes to allow mutation, if matches mount map
131
+ return this.value.volumes.filter(volume => names.includes(volume.name));
132
+ },
133
+
134
+ /**
135
+ * Remove all mounts for given storage volume
136
+ */
137
+ removeVolume(volume) {
138
+ const removeName = volume.row.value.name;
139
+
140
+ this.selectedContainerVolumes = this.selectedContainerVolumes.filter(({ name }) => name !== removeName);
141
+ },
142
+
143
+ selectVolume(event) {
144
+ const selectedVolume = this.value.volumes.find(vol => vol.name === event.value);
145
+
146
+ this.selectedContainerVolumes.push(selectedVolume);
147
+
148
+ const { name } = selectedVolume;
149
+
150
+ this.container.volumeMounts.push(name);
151
+ },
152
+
153
+ addVolume(type) {
154
+ const name = `vol-${ randomStr(5).toLowerCase() }`;
155
+
156
+ if (type === 'createPVC') {
157
+ this.storageVolumes.push({
158
+ _type: 'createPVC',
159
+ persistentVolumeClaim: {},
160
+ name,
161
+ });
162
+ } else if (type === 'csi') {
163
+ this.storageVolumes.push({
164
+ _type: type,
165
+ csi: { volumeAttributes: {} },
166
+ name,
167
+ });
168
+ } else {
169
+ this.storageVolumes.push({
170
+ _type: type,
171
+ [type]: {},
172
+ name,
173
+ });
174
+ }
175
+
176
+ this.container.volumeMounts.push({ name });
177
+ },
178
+
179
+ headerFor(value) {
180
+ const type = Object.keys(value).filter(
181
+ key => typeof value[key] === 'object'
182
+ )[0];
183
+
184
+ if (
185
+ this.$store.getters['i18n/exists'](`workload.storage.subtypes.${ type }`)
186
+ ) {
187
+ return this.t(`workload.storage.subtypes.${ type }`);
188
+ } else {
189
+ return type;
190
+ }
191
+ },
192
+
193
+ openPopover() {
194
+ const button = this.$refs.buttonDropdown;
195
+
196
+ try {
197
+ button.togglePopover();
198
+ } catch (e) {}
199
+ },
200
+ },
201
+ };
202
+ </script>
203
+
204
+ <template>
205
+ <div>
206
+ <!-- Storage Volumes -->
207
+ <ArrayListGrouped
208
+ :key="selectedContainerVolumes.length"
209
+ v-model="selectedContainerVolumes"
210
+ :mode="mode"
211
+ @remove="removeVolume"
212
+ >
213
+ <!-- Custom/default storage volume form -->
214
+ <template #default="props">
215
+ <h3>{{ props.row.value.name }} ({{ headerFor(props.row.value) }})</h3>
216
+ <Mount
217
+ :container="container"
218
+ :name="props.row.value.name"
219
+ :mode="mode"
220
+ />
221
+ </template>
222
+
223
+ <!-- Add Storage Volume -->
224
+ <template #add>
225
+ <ButtonDropdown
226
+ v-if="!isView"
227
+ id="add-volume"
228
+ :button-label="t('workload.storage.selectVolume')"
229
+ :dropdown-options="availableVolumeOptions"
230
+ size="sm"
231
+ @click-action="e=>selectVolume(e)"
232
+ >
233
+ <template #no-options>
234
+ {{ t('workload.storage.noVolumes') }}
235
+ </template>
236
+ </ButtonDropdown>
237
+ </template>
238
+ </ArrayListGrouped>
239
+ </div>
240
+ </template>
@@ -4,6 +4,7 @@ import { Checkbox } from '@components/Form/Checkbox';
4
4
  import { mapGetters } from 'vuex';
5
5
  import { removeObject } from '@shell/utils/array';
6
6
  export default {
7
+ name: 'Mount',
7
8
  components: { LabeledInput, Checkbox },
8
9
  props: {
9
10
  mode: {
@@ -1,9 +1,11 @@
1
1
  <script>
2
2
  import { LabeledInput } from '@components/Form/LabeledInput';
3
+ import { Checkbox } from '@components/Form/Checkbox';
4
+
3
5
  import { mapGetters } from 'vuex';
4
6
 
5
7
  export default {
6
- components: { LabeledInput },
8
+ components: { LabeledInput, Checkbox },
7
9
  props: {
8
10
  // volumeAttributes object
9
11
  value: {
@@ -24,6 +26,23 @@ export default {
24
26
 
25
27
  <template>
26
28
  <div>
29
+ <div class="row mb-10">
30
+ <div class="col span-6">
31
+ <LabeledInput
32
+ v-model="value.name"
33
+ :required="true"
34
+ :mode="mode"
35
+ :label="t('workload.storage.volumeName')"
36
+ />
37
+ </div>
38
+ <div class="col span-6">
39
+ <Checkbox
40
+ v-model="value.awsElasticBlockStore.readOnly"
41
+ :mode="mode"
42
+ :label="t('workload.storage.readOnly')"
43
+ />
44
+ </div>
45
+ </div>
27
46
  <div class="row mb-10">
28
47
  <div class="col span-6">
29
48
  <LabeledInput