@rancher/shell 0.3.8 → 0.3.10

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 (145) hide show
  1. package/assets/translations/en-us.yaml +47 -26
  2. package/assets/translations/zh-hans.yaml +82 -16
  3. package/babel.config.js +17 -4
  4. package/chart/istio.vue +11 -11
  5. package/chart/rancher-backup/S3.vue +1 -1
  6. package/components/AsyncButton.vue +2 -2
  7. package/components/ButtonGroup.vue +1 -1
  8. package/components/CodeMirror.vue +146 -14
  9. package/components/CompoundStatusBadge.vue +1 -1
  10. package/components/ContainerResourceLimit.vue +14 -1
  11. package/components/CopyCode.vue +1 -1
  12. package/components/CruResource.vue +21 -5
  13. package/components/DetailTop.vue +1 -1
  14. package/components/ExplorerProjectsNamespaces.vue +8 -4
  15. package/components/GlobalRoleBindings.vue +1 -1
  16. package/components/GroupPanel.vue +57 -0
  17. package/components/HarvesterServiceAddOnConfig.vue +2 -117
  18. package/components/ResourceDetail/Masthead.vue +1 -1
  19. package/components/ResourceList/Masthead.vue +0 -6
  20. package/components/ResourceList/ResourceLoadingIndicator.vue +1 -9
  21. package/components/ResourceList/index.vue +7 -6
  22. package/components/ResourceTable.vue +13 -3
  23. package/components/SortableTable/THead.vue +3 -3
  24. package/components/SortableTable/index.vue +3 -3
  25. package/components/Tabbed/Tab.vue +1 -1
  26. package/components/Tabbed/index.vue +1 -1
  27. package/components/Wizard.vue +9 -6
  28. package/components/YamlEditor.vue +2 -2
  29. package/components/__tests__/NamespaceFilter.test.ts +26 -7
  30. package/components/auth/RoleDetailEdit.vue +1 -1
  31. package/components/auth/SelectPrincipal.vue +1 -1
  32. package/components/fleet/FleetRepos.vue +1 -1
  33. package/components/form/ArrayList.vue +2 -2
  34. package/components/form/KeyValue.vue +37 -3
  35. package/components/form/Labels.vue +34 -14
  36. package/components/form/MatchExpressions.vue +120 -21
  37. package/components/form/Members/ClusterPermissionsEditor.vue +1 -1
  38. package/components/form/NameNsDescription.vue +1 -1
  39. package/components/form/NodeAffinity.vue +54 -4
  40. package/components/form/PlusMinus.vue +2 -2
  41. package/components/form/PodAffinity.vue +160 -47
  42. package/components/form/Probe.vue +1 -1
  43. package/components/form/ProjectMemberEditor.vue +8 -4
  44. package/components/form/ResourceQuota/NamespaceRow.vue +1 -1
  45. package/components/form/ServicePorts.vue +2 -2
  46. package/components/form/Tolerations.vue +70 -7
  47. package/components/form/WorkloadPorts.vue +2 -1
  48. package/components/form/__tests__/ArrayList.test.ts +3 -3
  49. package/components/form/__tests__/KeyValue.test.ts +17 -0
  50. package/components/form/__tests__/MatchExpressions.test.ts +1 -1
  51. package/components/formatter/ClusterLink.vue +3 -3
  52. package/components/formatter/LiveDate.vue +1 -1
  53. package/components/formatter/PodImages.vue +1 -1
  54. package/components/formatter/RKETemplateName.vue +1 -1
  55. package/components/formatter/Shortened.vue +1 -1
  56. package/components/nav/Header.vue +9 -7
  57. package/components/nav/NamespaceFilter.vue +103 -54
  58. package/config/labels-annotations.js +8 -5
  59. package/config/settings.ts +8 -6
  60. package/config/types.js +6 -4
  61. package/core/plugin-routes.ts +26 -7
  62. package/detail/provisioning.cattle.io.cluster.vue +4 -4
  63. package/edit/cis.cattle.io.clusterscan.vue +1 -1
  64. package/edit/configmap.vue +33 -6
  65. package/edit/k8s.cni.cncf.io.networkattachmentdefinition.vue +19 -149
  66. package/edit/logging-flow/index.vue +2 -2
  67. package/edit/logging.banzaicloud.io.output/providers/elasticsearch.vue +12 -0
  68. package/edit/logging.banzaicloud.io.output/providers/opensearch.vue +12 -0
  69. package/edit/management.cattle.io.project.vue +7 -0
  70. package/edit/monitoring.coreos.com.alertmanagerconfig/index.vue +1 -1
  71. package/edit/monitoring.coreos.com.alertmanagerconfig/routeConfig.vue +2 -2
  72. package/edit/monitoring.coreos.com.prometheusrule/GroupRules.vue +11 -8
  73. package/edit/networking.k8s.io.networkpolicy/PolicyRule.vue +2 -2
  74. package/edit/networking.k8s.io.networkpolicy/PolicyRuleTarget.vue +12 -4
  75. package/edit/networking.k8s.io.networkpolicy/__tests__/PolicyRuleTarget.spec.ts +140 -0
  76. package/edit/networking.k8s.io.networkpolicy/__tests__/utils/mock.json +158 -0
  77. package/edit/networking.k8s.io.networkpolicy/__tests__/utils/selectors.ts +45 -0
  78. package/edit/networking.k8s.io.networkpolicy/index.vue +1 -1
  79. package/edit/provisioning.cattle.io.cluster/AgentConfiguration.vue +326 -0
  80. package/edit/provisioning.cattle.io.cluster/MachinePool.vue +1 -1
  81. package/edit/provisioning.cattle.io.cluster/RegistryConfigs.vue +1 -1
  82. package/edit/provisioning.cattle.io.cluster/RegistryMirrors.vue +2 -2
  83. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +143 -169
  84. package/edit/provisioning.cattle.io.cluster/index.vue +1 -0
  85. package/edit/provisioning.cattle.io.cluster/rke2.vue +75 -6
  86. package/edit/resources.cattle.io.restore.vue +2 -2
  87. package/edit/service.vue +22 -3
  88. package/edit/storage.k8s.io.storageclass/index.vue +1 -1
  89. package/edit/workload/Job.vue +2 -2
  90. package/edit/workload/index.vue +1 -1
  91. package/edit/workload/mixins/workload.js +7 -1
  92. package/edit/workload/storage/__tests__/Storage.test.ts +84 -5
  93. package/initialize/index.js +1 -0
  94. package/layouts/default.vue +1 -1
  95. package/mixins/chart.js +1 -1
  96. package/mixins/resource-fetch-namespaced.js +19 -27
  97. package/mixins/resource-fetch.js +0 -5
  98. package/models/__tests__/namespace.test.ts +125 -0
  99. package/models/batch.cronjob.js +18 -3
  100. package/models/management.cattle.io.project.js +6 -1
  101. package/models/persistentvolume.js +1 -1
  102. package/models/workload.js +1 -1
  103. package/models/workload.service.js +22 -7
  104. package/package.json +17 -6
  105. package/pages/auth/login.vue +47 -49
  106. package/pages/c/_cluster/apps/charts/chart.vue +1 -1
  107. package/pages/c/_cluster/apps/charts/install.vue +42 -51
  108. package/pages/c/_cluster/explorer/index.vue +1 -1
  109. package/pages/c/_cluster/monitoring/index.vue +1 -1
  110. package/pages/c/_cluster/settings/performance.vue +53 -18
  111. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +1 -1
  112. package/pages/c/_cluster/uiplugins/index.vue +16 -5
  113. package/pages/home.vue +1 -1
  114. package/pages/prefs.vue +18 -2
  115. package/plugins/clean-html-directive.js +1 -1
  116. package/plugins/clean-tooltip-directive.js +33 -0
  117. package/plugins/codemirror.js +158 -0
  118. package/plugins/dashboard-store/actions.js +4 -2
  119. package/plugins/dashboard-store/getters.js +6 -0
  120. package/plugins/dashboard-store/mutations.js +2 -2
  121. package/plugins/plugin.js +6 -1
  122. package/plugins/steve/actions.js +1 -1
  123. package/plugins/steve/getters.js +14 -3
  124. package/plugins/steve/resourceWatcher.js +36 -62
  125. package/plugins/steve/subscribe.js +137 -21
  126. package/plugins/steve/worker/index.js +7 -1
  127. package/plugins/steve/worker/web-worker.advanced.js +26 -8
  128. package/plugins/steve/worker/web-worker.basic.js +23 -4
  129. package/public/index.html +1 -1
  130. package/rancher-components/components/Form/Checkbox/Checkbox.vue +2 -2
  131. package/rancher-components/components/Form/Radio/RadioGroup.vue +2 -2
  132. package/rancher-components/components/LabeledTooltip/LabeledTooltip.vue +1 -1
  133. package/store/index.js +16 -61
  134. package/store/store-types.js +5 -0
  135. package/store/type-map.js +1 -1
  136. package/types/shell/index.d.ts +42 -7
  137. package/utils/__tests__/create-yaml.test.ts +63 -0
  138. package/utils/array.ts +4 -0
  139. package/utils/create-yaml.js +105 -8
  140. package/utils/namespace-filter.js +17 -5
  141. package/utils/projectAndNamespaceFiltering.utils.ts +62 -0
  142. package/utils/selector.js +6 -5
  143. package/utils/settings.ts +17 -7
  144. package/vue.config.js +2 -2
  145. package/models/k8s.cni.cncf.io.networkattachmentdefinition.js +0 -93
@@ -265,7 +265,7 @@ export default {
265
265
  >
266
266
  {{ t('workload.upgrading.terminationGracePeriodSeconds.label') }}
267
267
  <i
268
- v-tooltip="t('workload.upgrading.terminationGracePeriodSeconds.tip')"
268
+ v-clean-tooltip="t('workload.upgrading.terminationGracePeriodSeconds.tip')"
269
269
  class="icon icon-info"
270
270
  />
271
271
  </label>
@@ -326,7 +326,7 @@ export default {
326
326
  >
327
327
  {{ t('workload.upgrading.terminationGracePeriodSeconds.label') }}
328
328
  <i
329
- v-tooltip="t('workload.upgrading.terminationGracePeriodSeconds.tip')"
329
+ v-clean-tooltip="t('workload.upgrading.terminationGracePeriodSeconds.tip')"
330
330
  class="icon icon-info"
331
331
  />
332
332
  </label>
@@ -252,7 +252,7 @@ export default {
252
252
  <h3>
253
253
  {{ t('workload.container.ports.expose') }}
254
254
  <i
255
- v-tooltip="t('workload.container.ports.toolTip')"
255
+ v-clean-tooltip="t('workload.container.ports.toolTip')"
256
256
  class="icon icon-info"
257
257
  />
258
258
  </h3>
@@ -12,6 +12,7 @@ import {
12
12
  CAPI,
13
13
  POD,
14
14
  LIST_WORKLOAD_TYPES,
15
+ HCI,
15
16
  } from '@shell/config/types';
16
17
  import Tab from '@shell/components/Tabbed/Tab';
17
18
  import CreateEditView from '@shell/mixins/create-edit-view';
@@ -49,6 +50,7 @@ import NameNsDescription from '@shell/components/form/NameNsDescription';
49
50
  import formRulesGenerator from '@shell/utils/validators/formRules';
50
51
  import { TYPES as SECRET_TYPES } from '@shell/models/secret';
51
52
  import { defaultContainer } from '@shell/models/workload';
53
+ import { allHash } from '@shell/utils/promise';
52
54
 
53
55
  const TAB_WEIGHT_MAP = {
54
56
  general: 99,
@@ -143,7 +145,11 @@ export default {
143
145
  },
144
146
 
145
147
  async fetch() {
146
- await this.$store.dispatch('management/findAll', { type: CAPI.RANCHER_CLUSTER });
148
+ // TODO Should remove these lines
149
+ await allHash({
150
+ rancherClusters: this.$store.dispatch('management/findAll', { type: CAPI.RANCHER_CLUSTER }),
151
+ harvesterConfigs: this.$store.dispatch('management/findAll', { type: HCI.HARVESTER_CONFIG }),
152
+ });
147
153
 
148
154
  // don't block UI for these resources
149
155
  this.resourceManagerFetchSecondaryResources(this.secondaryResourceData);
@@ -2,20 +2,99 @@ import { mount } from '@vue/test-utils';
2
2
  import Storage from '@shell/edit//workload/storage/index.vue';
3
3
 
4
4
  describe('component: Storage', () => {
5
- // TODO: Complete test after integrating #5631
5
+ describe.each([
6
+ 'awsElasticBlockStore',
7
+ 'azureDisk',
8
+ 'azureFile',
9
+ 'configMap',
10
+ // 'createPVC',
11
+ 'csi',
12
+ 'emptyDir',
13
+ 'gcePersistentDisk',
14
+ 'gcePersistentDisk',
15
+ 'hostPath',
16
+ 'secret',
17
+ 'vsphereVolume'
18
+ ])('given volume type %p', (volumeType) => {
19
+ it('should display the volume name as first input of the form array', () => {
20
+ const name = 'whatever';
21
+ const wrapper = mount(Storage, {
22
+ propsData: {
23
+ savePvcHookName: '',
24
+ value: {
25
+ volumes: [{
26
+ _type: volumeType,
27
+ [volumeType]: {},
28
+ name
29
+ }]
30
+ },
31
+ },
32
+ mocks: {
33
+ t: (text: string) => text, // Mock i18n global function used as alternative to the getter
34
+ $store: {
35
+ getters: {
36
+ 'i18n/t': jest.fn(),
37
+ 'i18n/exists': jest.fn()
38
+ }
39
+ }
40
+ },
41
+ });
42
+
43
+ const input = wrapper.find('input').element as HTMLInputElement;
44
+
45
+ expect(input.value).toStrictEqual(name);
46
+ });
47
+
48
+ it('should edit the volume name', () => {
49
+ const name = 'whatever';
50
+ const newName = 'new whatever';
51
+ const wrapper = mount(Storage, {
52
+ propsData: {
53
+ savePvcHookName: '',
54
+ value: {
55
+ volumes: [{
56
+ _type: volumeType,
57
+ [volumeType]: {},
58
+ name
59
+ }]
60
+ },
61
+ },
62
+ mocks: {
63
+ t: (text: string) => text, // Mock i18n global function used as alternative to the getter
64
+ $store: {
65
+ getters: {
66
+ 'i18n/t': jest.fn(),
67
+ 'i18n/exists': jest.fn()
68
+ }
69
+ }
70
+ },
71
+ });
72
+
73
+ wrapper.find('input').setValue(newName);
74
+
75
+ expect(wrapper.vm.value.volumes[0].name).toStrictEqual(newName);
76
+ });
77
+ });
78
+
79
+ // TODO: find how to interact with the tooltip selection outside of the component
6
80
  // eslint-disable-next-line jest/no-disabled-tests
7
81
  it.skip('should allow to add a new volume', async() => {
8
82
  const wrapper = mount(Storage, {
9
83
  propsData: { savePvcHookName: '' },
10
84
  mocks: {
11
- t: (text: string) => text, // Fixes another issue with another i18n logic not from the getters
85
+ t: (text: string) => text, // Mock i18n global function used as alternative to the getter
12
86
  $store: { getters: { 'i18n/t': jest.fn() } }
13
87
  },
14
88
  });
15
89
 
16
- await wrapper.find('#add-volume').trigger('click');
17
- const title = wrapper.find('h3');
90
+ await wrapper.find('#select-volume').trigger('click');
91
+ await wrapper.trigger('keydown.down');
92
+ await wrapper.trigger('keydown.enter');
18
93
 
19
- expect(title.isVisible).toBe(true);
94
+ expect(wrapper.vm.value.volumes[0]).toStrictEqual({
95
+ _type: 'awsElasticBlockStore',
96
+ awsElasticBlockStore: {},
97
+ name: ''
98
+ });
20
99
  });
21
100
  });
@@ -19,6 +19,7 @@ import plugins from '../core/plugins.js';
19
19
  import pluginsLoader from '../core/plugins-loader.js';
20
20
  import axiosShell from '../plugins/axios';
21
21
  import '../plugins/tooltip';
22
+ import '../plugins/clean-tooltip-directive';
22
23
  import '../plugins/vue-clipboard2';
23
24
  import '../plugins/v-select';
24
25
  import '../plugins/directives';
@@ -667,7 +667,7 @@ export default {
667
667
  </nuxt-link>
668
668
 
669
669
  <span
670
- v-tooltip="{content: displayVersion, placement: 'top'}"
670
+ v-clean-tooltip="{content: displayVersion, placement: 'top'}"
671
671
  class="clip version text-muted"
672
672
  >
673
673
  {{ displayVersion }}
package/mixins/chart.js CHANGED
@@ -14,7 +14,7 @@ import { CAPI, CATALOG } from '@shell/config/types';
14
14
  import { isPrerelease } from '@shell/utils/version';
15
15
  import difference from 'lodash/difference';
16
16
  import { LINUX } from '@shell/store/catalog';
17
- import { clone } from 'utils/object';
17
+ import { clone } from '@shell/utils/object';
18
18
  import { merge } from 'lodash';
19
19
 
20
20
  export default {
@@ -1,5 +1,7 @@
1
+ import { NAMESPACE_FILTER_NS_PREFIX, NAMESPACE_FILTER_P_PREFIX } from '@shell/utils/namespace-filter';
1
2
  import { mapGetters } from 'vuex';
2
3
  import { ResourceListComponentName } from '../components/ResourceList/resource-list.config';
4
+ import pAndNFiltering from '@shell/utils/projectAndNamespaceFiltering.utils';
3
5
 
4
6
  /**
5
7
  * Companion mixin used with `resource-fetch` for `ResourceList` to determine if the user needs to filter the list by a single namespace
@@ -11,46 +13,53 @@ export default {
11
13
  },
12
14
 
13
15
  computed: {
14
- ...mapGetters(['currentProduct', 'currentCluster', 'isSingleNamespace']),
16
+ ...mapGetters(['currentProduct', 'currentCluster', 'namespaceFilters']),
15
17
 
16
18
  /**
17
19
  * Does the user need to update the filter to supply a single namespace?
18
20
  */
19
21
  namespaceFilterRequired() {
20
- return this.__namespaceRequired && !this.__singleNamespaceFilter;
22
+ return this.__namespaceRequired && !this.__validFilter;
21
23
  },
22
24
 
23
25
  /**
24
26
  * Returns the namespace that requests should be filtered by
25
27
  */
26
28
  namespaceFilter() {
27
- return this.__namespaceRequired ? this.__singleNamespaceFilter : '';
29
+ return this.__namespaceRequired ? this.__validFilter : '';
28
30
  },
29
31
 
30
32
  /**
31
- * If the Project/Namespace filter from the header contains a single NS... return it
33
+ * If the Project/Namespace filter from the header contains a valid ns / project filter ... return it
32
34
  */
33
- __singleNamespaceFilter() {
34
- const ns = this.isSingleNamespace;
35
+ __validFilter() {
36
+ const valid = this.namespaceFilters.length && this.namespaceFilters.every(f => f.startsWith(NAMESPACE_FILTER_NS_PREFIX) || f.startsWith(NAMESPACE_FILTER_P_PREFIX));
35
37
 
36
- return ns ? ns.replace('ns://', '') : '';
38
+ return valid ? this.namespaceFilters : null;
37
39
  },
38
40
 
39
41
  /**
40
- * Do we need to filter the list by a namespace?
42
+ * Do we need to filter the list by a namespace? This will control whether the user is shown an error
43
+ *
44
+ * We shouldn't show an error on pages with resources that aren't namespaced
41
45
  */
42
46
  __namespaceRequired() {
43
- if (!this.forceNsFilter?.enabled || this.perfConfig.forceNsFilter.threshold === undefined) {
47
+ if (!pAndNFiltering.isEnabled(this.$store.getters)) {
44
48
  return false;
45
49
  }
46
50
 
47
- return !this.currentProduct.showWorkspaceSwitcher && this.__areResourcesNamespaced && this.__areResourcesTooMany;
51
+ return this.__areResourcesNamespaced;
48
52
  },
49
53
 
50
54
  /**
51
55
  * Are all core list resources namespaced?
52
56
  */
53
57
  __areResourcesNamespaced() {
58
+ // Only enable for the cluster store at the moment. In theory this should work in management as well, as they're both 'steve' stores
59
+ if (this.currentProduct.inStore !== 'cluster') {
60
+ return false;
61
+ }
62
+
54
63
  return (this.loadResources || []).every((type) => {
55
64
  const schema = this.$store.getters['cluster/schemaFor'](type);
56
65
 
@@ -58,26 +67,9 @@ export default {
58
67
  });
59
68
  },
60
69
 
61
- /**
62
- * Are there too many core list resources to show in the list?
63
- */
64
- __areResourcesTooMany() {
65
- // __getCountForResources is defined on resource-fetch mixin...
66
- const count = this.__getCountForResources(this.loadResources);
67
-
68
- return count > this.perfConfig.forceNsFilter.threshold;
69
- },
70
-
71
70
  },
72
71
 
73
72
  watch: {
74
- __namespaceRequired: {
75
- handler(neu) {
76
- this.$store.dispatch('setNamespaceFilterMode', neu ? 'namespace' : null, { root: true });
77
- },
78
- immediate: true,
79
- },
80
-
81
73
  async namespaceFilter(neu) {
82
74
  if (neu) {
83
75
  // When a NS filter is required and the user selects a different one, kick off a new set of API requests
@@ -38,11 +38,6 @@ export default {
38
38
  // incremental loading vars
39
39
  incremental: false,
40
40
  fetchedResourceType: [],
41
- // force ns filtering
42
- forceNsFilter: {
43
- ...perfConfig.forceNsFilter,
44
- threshold: parseInt(perfConfig?.forceNsFilter?.threshold || '0', 10)
45
- }
46
41
  };
47
42
  },
48
43
  beforeDestroy() {
@@ -0,0 +1,125 @@
1
+ import Namespace from '@shell/models/namespace';
2
+
3
+ describe('class Namespace', () => {
4
+ describe('checking if isSystem', () => {
5
+ it.each([
6
+ ['c-whatever-system', true],
7
+ ['whatever', false]
8
+ ])('should return true if end with "-system', (name, expectation) => {
9
+ const namespace = new Namespace({});
10
+
11
+ namespace.metadata = { ...namespace.metadata, name };
12
+
13
+ expect(namespace.isSystem).toBe(expectation);
14
+ });
15
+ });
16
+
17
+ it.todo('should check if isFleetManaged');
18
+
19
+ describe('checking if isObscure', () => {
20
+ it.each([
21
+ ['c-whatever-system', true],
22
+ ['whatever', false],
23
+ ['', false]
24
+ ])('should return a value if has or not a name in the metadata', (name, expectation) => {
25
+ const namespace = new Namespace({});
26
+
27
+ namespace.metadata = { ...namespace.metadata, name };
28
+
29
+ expect(namespace.isObscure).toBe(expectation);
30
+ });
31
+
32
+ it.todo('should return a value if is or not system');
33
+ });
34
+
35
+ it.each([
36
+ ['foo:bar', 'bar'],
37
+ ['', null]
38
+ ])('given %p annotation, projectId should be %p', (value, result) => {
39
+ const namespace = new Namespace({});
40
+
41
+ namespace.metadata = { name: '', annotations: { 'field.cattle.io/projectId': value } };
42
+
43
+ expect(namespace.projectId).toBe(result);
44
+ });
45
+
46
+ it.todo('should return the project');
47
+ it.todo('should return the groupByLabel with i18n');
48
+ it.todo('should return the project name with i18n');
49
+ it.todo('should return the projectNameSort');
50
+ it.todo('should check if istioInstalled');
51
+ it.todo('should check if injectionEnabled');
52
+
53
+ describe('handling Istio labels', () => {
54
+ const save = jest.fn();
55
+
56
+ it.each([
57
+ { metadata: { name: '', labels: { 'istio-injection': 'whatever' } }, save },
58
+ [{ metadata: { name: '', labels: { 'istio-injection': 'whatever' } }, save }],
59
+ ])('should handle both data as list and single object and save', (data) => {
60
+ const namespace = new Namespace({});
61
+
62
+ namespace.enableAutoInjection(data as unknown as Namespace);
63
+
64
+ expect(save).toHaveBeenCalledWith();
65
+ });
66
+
67
+ it('should add auto injection label as enable', () => {
68
+ const data = { metadata: { name: '', labels: { 'istio-injection': 'whatever' } }, save };
69
+ const namespace = new Namespace({});
70
+
71
+ namespace.enableAutoInjection(data as unknown as Namespace);
72
+
73
+ expect(data.metadata!.labels['istio-injection']).toBe('enabled');
74
+ });
75
+
76
+ it('should remove label on disable', () => {
77
+ const data = { metadata: { name: '', labels: { 'istio-injection': 'whatever' } }, save };
78
+ const namespace = new Namespace({});
79
+
80
+ namespace.enableAutoInjection(data as unknown as Namespace, false);
81
+
82
+ expect(data.metadata!.labels['istio-injection']).toBeUndefined();
83
+ });
84
+ });
85
+
86
+ it.todo('should disableAutoInjection');
87
+ it.todo('should check if confirmRemove');
88
+
89
+ describe('handling listLocation', () => {
90
+ it.each([
91
+ ['c-cluster-product-projectsnamespaces', true],
92
+ ['c-cluster-product-resource', false],
93
+ ])('should return the name %p if is Rancher (%p)', (name, isRancher) => {
94
+ const namespace = new Namespace({});
95
+
96
+ jest.spyOn(namespace, '$rootGetters', 'get').mockReturnValue({
97
+ isRancher,
98
+ currentProduct: { inStore: '' }
99
+ });
100
+
101
+ expect(namespace.listLocation.name).toBe(name);
102
+ });
103
+
104
+ it('should return the name and resource if Harvester', () => {
105
+ const namespace = new Namespace({});
106
+
107
+ jest.spyOn(namespace, '$rootGetters', 'get').mockReturnValue({
108
+ isRancher: true,
109
+ currentProduct: { inStore: 'harvester' }
110
+ });
111
+
112
+ const value = {
113
+ name: 'harvester-c-cluster-projectsnamespaces',
114
+ params: { resource: 'namespace' }
115
+ };
116
+
117
+ expect(namespace.listLocation).toStrictEqual(value);
118
+ });
119
+ });
120
+
121
+ it.todo('should return _detailLocation with a name');
122
+ it.todo('should return the resourceQuota');
123
+ it.todo('should set the resourceQuota as reactive Vue property');
124
+ it.todo('should reset project with cleanForNew');
125
+ });
@@ -2,6 +2,7 @@ import { insertAt } from '@shell/utils/array';
2
2
  import { clone } from '@shell/utils/object';
3
3
  import { WORKLOAD_TYPES } from '@shell/config/types';
4
4
  import Workload from './workload';
5
+ import { WORKLOAD_TYPE_TO_KIND_MAPPING } from '@shell/detail/workload/index';
5
6
 
6
7
  export default class CronJob extends Workload {
7
8
  get state() {
@@ -47,12 +48,26 @@ export default class CronJob extends Workload {
47
48
  }
48
49
 
49
50
  async runNow() {
50
- const job = await this.$dispatch('create', clone(this.spec.jobTemplate));
51
+ const ownerRef = {
52
+ apiVersion: this.apiVersion,
53
+ controller: true,
54
+ kind: this.kind,
55
+ name: this.metadata.name,
56
+ uid: this.metadata.uid
57
+ };
58
+
59
+ // Set type and kind to ensure the correct model is returned (via classify). This object will be persisted to the store
60
+ const job = await this.$dispatch('create', {
61
+ type: WORKLOAD_TYPES.JOB,
62
+ kind: WORKLOAD_TYPE_TO_KIND_MAPPING[WORKLOAD_TYPES.JOB],
63
+ ...clone(this.spec.jobTemplate)
64
+ });
51
65
 
52
- job.type = WORKLOAD_TYPES.JOB;
53
66
  job.metadata = job.metadata || {};
54
67
  job.metadata.namespace = this.metadata.namespace;
55
- job.metadata.generateName = `${ this.metadata.name }-`;
68
+ // Can't use `generatedName` and no `name`... as this fails schema validation
69
+ job.metadata.name = `${ this.metadata.name }-${ Date.now() }`;
70
+ job.metadata.ownerReferences = [ownerRef];
56
71
 
57
72
  await job.save();
58
73
 
@@ -128,7 +128,12 @@ export default class Project extends HybridModel {
128
128
  }
129
129
  };
130
130
 
131
- await finishProjectCreation();
131
+ // Only update PSP template if the value changed
132
+ const newPSPTemplateID = this.spec.podSecurityPolicyTemplateId || null;
133
+
134
+ if (norman.podSecurityPolicyTemplateId !== newPSPTemplateID) {
135
+ await finishProjectCreation();
136
+ }
132
137
 
133
138
  return newValue;
134
139
  }
@@ -115,7 +115,7 @@ export default class PV extends SteveModel {
115
115
  // plugin display value table
116
116
  get source() {
117
117
  const csiDriver = this.spec?.csi?.driver;
118
- const fallback = `${ csiDriver } ${ this.t('persistentVolume.csi.drivers.suffix') }`;
118
+ const fallback = `${ csiDriver } ${ this.t('persistentVolume.csi.suffix') }`;
119
119
 
120
120
  if (csiDriver) {
121
121
  return this.$rootGetters['i18n/withFallback'](`persistentVolume.csi.drivers.${ csiDriver.replaceAll('.', '-') }`, null, fallback);
@@ -296,7 +296,7 @@ export default class Workload extends WorkloadService {
296
296
  }
297
297
 
298
298
  get endpoint() {
299
- return this?.metadata?.annotations[CATTLE_PUBLIC_ENDPOINTS];
299
+ return this?.metadata?.annotations?.[CATTLE_PUBLIC_ENDPOINTS];
300
300
  }
301
301
 
302
302
  get desired() {
@@ -1,7 +1,7 @@
1
1
 
2
2
  import { findBy } from '@shell/utils/array';
3
3
  import { TARGET_WORKLOADS, UI_MANAGED, HCI as HCI_LABELS_ANNOTATIONS } from '@shell/config/labels-annotations';
4
- import { WORKLOAD_TYPES, SERVICE } from '@shell/config/types';
4
+ import { WORKLOAD_TYPES, SERVICE, CAPI, HCI } from '@shell/config/types';
5
5
  import { clone, get } from '@shell/utils/object';
6
6
  import SteveModel from '@shell/plugins/steve/steve-class';
7
7
  import { shortenedImage } from '@shell/utils/string';
@@ -304,16 +304,31 @@ export default class WorkloadService extends SteveModel {
304
304
  if (loadBalancer.id) {
305
305
  loadBalancerProxy = loadBalancer;
306
306
  } else {
307
- loadBalancer = clone(loadBalancer);
307
+ loadBalancerProxy = await this.$dispatch(`cluster/create`, loadBalancer, { root: true });
308
+ }
308
309
 
309
- const portsWithIpam = ports.filter(p => p._ipam) || [];
310
+ const portsWithIpam = ports.filter(p => p._ipam) || [];
310
311
 
311
- if (portsWithIpam.length > 0) {
312
- loadBalancer.metadata.annotations[HCI_LABELS_ANNOTATIONS.CLOUD_PROVIDER_IPAM] = portsWithIpam[0]._ipam;
313
- }
312
+ if (portsWithIpam.length > 0) {
313
+ loadBalancerProxy.metadata.annotations[HCI_LABELS_ANNOTATIONS.CLOUD_PROVIDER_IPAM] = portsWithIpam[0]._ipam;
314
314
 
315
- loadBalancerProxy = await this.$dispatch(`cluster/create`, loadBalancer, { root: true });
315
+ const clusters = this.$rootGetters['management/all'](CAPI.RANCHER_CLUSTER);
316
+ const configs = this.$rootGetters['management/all'](HCI.HARVESTER_CONFIG);
317
+ const currentCluster = this.$rootGetters['currentCluster'];
318
+ const cluster = clusters.find(c => c.status.clusterName === currentCluster.id);
319
+
320
+ const machinePools = cluster?.spec?.rkeConfig?.machinePools || [];
321
+ const machineConfigName = machinePools[0]?.machineConfigRef?.name;
322
+ const config = configs.find(c => c.id === `fleet-default/${ machineConfigName }`);
323
+
324
+ if (config) {
325
+ const { vmNamespace, networkName } = config;
326
+
327
+ loadBalancerProxy.metadata.annotations[HCI_LABELS_ANNOTATIONS.CLOUD_PROVIDER_NAMESPACE] = vmNamespace;
328
+ loadBalancerProxy.metadata.annotations[HCI_LABELS_ANNOTATIONS.CLOUD_PROVIDER_NETWORK] = networkName;
329
+ }
316
330
  }
331
+
317
332
  toSave.push(loadBalancerProxy);
318
333
  } else if (loadBalancer.id) {
319
334
  toRemove.push(loadBalancer);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rancher/shell",
3
- "version": "0.3.8",
3
+ "version": "0.3.10",
4
4
  "description": "Rancher Dashboard Shell",
5
5
  "repository": "https://github.com/rancherlabs/dashboard",
6
6
  "license": "Apache-2.0",
@@ -61,7 +61,7 @@
61
61
  "cron-validator": "1.2.0",
62
62
  "cronstrue": "1.95.0",
63
63
  "cross-env": "6.0.3",
64
- "css-loader": "4.3.0",
64
+ "css-loader": "6.7.3",
65
65
  "csv-loader": "3.0.3",
66
66
  "cypress": "10.3.1",
67
67
  "d3": "7.3.0",
@@ -69,7 +69,7 @@
69
69
  "dagre-d3": "0.6.4",
70
70
  "dayjs": "1.8.29",
71
71
  "diff2html": "2.11.2",
72
- "dompurify": "2.0.12",
72
+ "dompurify": "2.4.5",
73
73
  "eslint": "7.32.0",
74
74
  "eslint-config-standard": "16.0.3",
75
75
  "eslint-import-resolver-node": "0.3.4",
@@ -96,11 +96,11 @@
96
96
  "jsdiff": "1.1.1",
97
97
  "jsdom-global": "3.0.2",
98
98
  "jsonpath-plus": "6.0.1",
99
- "jsrsasign": "10.2.0",
100
- "jszip": "3.7.0",
99
+ "jsrsasign": "10.5.25",
100
+ "jszip": "3.8.0",
101
101
  "lodash": "4.17.21",
102
102
  "marked": "4.0.17",
103
- "nodemon": "2.0.4",
103
+ "nodemon": "2.0.22",
104
104
  "nuxt": "2.15.8",
105
105
  "nyc": "15.1.0",
106
106
  "papaparse": "5.3.0",
@@ -144,6 +144,17 @@
144
144
  "worker-loader": "3.0.8",
145
145
  "yarn": "1.22.18"
146
146
  },
147
+ "resolutions": {
148
+ "ejs": "^3.1.7",
149
+ "json5": ">=2.2.2",
150
+ "d3-color": ">=3.1.0",
151
+ "glob-parent": ">=5.1.2",
152
+ "node-forge": ">=1.3.0",
153
+ "qs": ">=6.7.3",
154
+ "nth-check": ">=2.0.1",
155
+ "follow-redirects": ">=1.14.7",
156
+ "merge": ">=2.1.1"
157
+ },
147
158
  "nyc": {
148
159
  "extension": [
149
160
  ".js",