@rancher/shell 0.3.9 → 0.3.11

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 (132) hide show
  1. package/assets/translations/en-us.yaml +19 -24
  2. package/assets/translations/zh-hans.yaml +82 -16
  3. package/chart/istio.vue +11 -11
  4. package/chart/rancher-backup/S3.vue +1 -1
  5. package/components/AsyncButton.vue +2 -2
  6. package/components/ButtonGroup.vue +1 -1
  7. package/components/CompoundStatusBadge.vue +1 -1
  8. package/components/CopyCode.vue +1 -1
  9. package/components/DetailText.vue +1 -0
  10. package/components/DetailTop.vue +1 -1
  11. package/components/ExplorerProjectsNamespaces.vue +3 -3
  12. package/components/GlobalRoleBindings.vue +1 -1
  13. package/components/HarvesterServiceAddOnConfig.vue +2 -117
  14. package/components/ResourceDetail/Masthead.vue +1 -1
  15. package/components/ResourceList/Masthead.vue +0 -6
  16. package/components/ResourceList/ResourceLoadingIndicator.vue +1 -9
  17. package/components/ResourceList/index.vue +7 -6
  18. package/components/ResourceTable.vue +13 -3
  19. package/components/SortableTable/THead.vue +4 -3
  20. package/components/SortableTable/index.vue +3 -3
  21. package/components/Tabbed/Tab.vue +1 -1
  22. package/components/Tabbed/index.vue +1 -1
  23. package/components/Wizard.vue +9 -6
  24. package/components/__tests__/NamespaceFilter.test.ts +26 -7
  25. package/components/auth/RoleDetailEdit.vue +1 -1
  26. package/components/auth/SelectPrincipal.vue +1 -1
  27. package/components/fleet/FleetRepos.vue +1 -1
  28. package/components/form/ArrayList.vue +1 -1
  29. package/components/form/ChangePassword.vue +3 -0
  30. package/components/form/KeyValue.vue +3 -2
  31. package/components/form/Labels.vue +34 -14
  32. package/components/form/Members/ClusterPermissionsEditor.vue +1 -1
  33. package/components/form/NameNsDescription.vue +1 -1
  34. package/components/form/PlusMinus.vue +2 -2
  35. package/components/form/Probe.vue +1 -1
  36. package/components/form/ProjectMemberEditor.vue +8 -4
  37. package/components/form/ResourceQuota/NamespaceRow.vue +1 -1
  38. package/components/form/ServicePorts.vue +2 -2
  39. package/components/form/Tolerations.vue +30 -3
  40. package/components/form/WorkloadPorts.vue +2 -1
  41. package/components/form/__tests__/KeyValue.test.ts +17 -0
  42. package/components/formatter/ClusterLink.vue +3 -3
  43. package/components/formatter/LiveDate.vue +1 -1
  44. package/components/formatter/PodImages.vue +1 -1
  45. package/components/formatter/RKETemplateName.vue +1 -1
  46. package/components/formatter/Shortened.vue +1 -1
  47. package/components/nav/Header.vue +7 -7
  48. package/components/nav/NamespaceFilter.vue +103 -54
  49. package/config/labels-annotations.js +8 -5
  50. package/config/settings.ts +2 -5
  51. package/config/types.js +6 -4
  52. package/core/plugin-routes.ts +26 -7
  53. package/core/plugins-loader.js +2 -0
  54. package/detail/helm.cattle.io.projecthelmchart.vue +2 -2
  55. package/detail/provisioning.cattle.io.cluster.vue +4 -4
  56. package/edit/cis.cattle.io.clusterscan.vue +1 -1
  57. package/edit/k8s.cni.cncf.io.networkattachmentdefinition.vue +19 -149
  58. package/edit/logging-flow/index.vue +2 -2
  59. package/edit/logging.banzaicloud.io.output/providers/elasticsearch.vue +12 -0
  60. package/edit/logging.banzaicloud.io.output/providers/opensearch.vue +12 -0
  61. package/edit/management.cattle.io.project.vue +7 -0
  62. package/edit/monitoring.coreos.com.alertmanagerconfig/index.vue +1 -1
  63. package/edit/monitoring.coreos.com.alertmanagerconfig/routeConfig.vue +2 -2
  64. package/edit/monitoring.coreos.com.prometheusrule/GroupRules.vue +11 -8
  65. package/edit/networking.k8s.io.networkpolicy/PolicyRule.vue +2 -2
  66. package/edit/networking.k8s.io.networkpolicy/PolicyRuleTarget.vue +12 -4
  67. package/edit/networking.k8s.io.networkpolicy/__tests__/PolicyRuleTarget.spec.ts +140 -0
  68. package/edit/networking.k8s.io.networkpolicy/__tests__/utils/mock.json +158 -0
  69. package/edit/networking.k8s.io.networkpolicy/__tests__/utils/selectors.ts +45 -0
  70. package/edit/networking.k8s.io.networkpolicy/index.vue +1 -1
  71. package/edit/provisioning.cattle.io.cluster/AgentConfiguration.vue +1 -1
  72. package/edit/provisioning.cattle.io.cluster/MachinePool.vue +1 -1
  73. package/edit/provisioning.cattle.io.cluster/RegistryConfigs.vue +1 -1
  74. package/edit/provisioning.cattle.io.cluster/RegistryMirrors.vue +2 -2
  75. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +143 -169
  76. package/edit/provisioning.cattle.io.cluster/rke2.vue +15 -6
  77. package/edit/resources.cattle.io.restore.vue +2 -2
  78. package/edit/service.vue +22 -3
  79. package/edit/storage.k8s.io.storageclass/index.vue +1 -1
  80. package/edit/token.vue +1 -0
  81. package/edit/workload/Job.vue +2 -2
  82. package/edit/workload/index.vue +1 -1
  83. package/edit/workload/mixins/workload.js +7 -1
  84. package/edit/workload/storage/__tests__/Storage.test.ts +84 -5
  85. package/initialize/index.js +1 -0
  86. package/layouts/default.vue +1 -1
  87. package/mixins/resource-fetch-namespaced.js +19 -27
  88. package/mixins/resource-fetch.js +0 -5
  89. package/models/__tests__/namespace.test.ts +125 -0
  90. package/models/management.cattle.io.project.js +6 -1
  91. package/models/persistentvolume.js +1 -1
  92. package/models/workload.service.js +22 -7
  93. package/package.json +17 -5
  94. package/pages/account/index.vue +3 -0
  95. package/pages/auth/login.vue +46 -49
  96. package/pages/c/_cluster/apps/charts/chart.vue +1 -1
  97. package/pages/c/_cluster/apps/charts/install.vue +42 -51
  98. package/pages/c/_cluster/explorer/index.vue +1 -1
  99. package/pages/c/_cluster/monitoring/index.vue +1 -1
  100. package/pages/c/_cluster/settings/performance.vue +53 -18
  101. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +1 -1
  102. package/pages/c/_cluster/uiplugins/index.vue +16 -5
  103. package/pages/home.vue +1 -1
  104. package/pkg/vue.config.js +1 -0
  105. package/plugins/clean-html-directive.js +1 -1
  106. package/plugins/clean-tooltip-directive.js +33 -0
  107. package/plugins/dashboard-store/actions.js +4 -2
  108. package/plugins/dashboard-store/getters.js +6 -0
  109. package/plugins/dashboard-store/mutations.js +2 -2
  110. package/plugins/plugin.js +6 -1
  111. package/plugins/steve/actions.js +1 -1
  112. package/plugins/steve/getters.js +14 -3
  113. package/plugins/steve/resourceWatcher.js +36 -62
  114. package/plugins/steve/subscribe.js +164 -21
  115. package/plugins/steve/worker/index.js +8 -1
  116. package/plugins/steve/worker/web-worker.advanced.js +26 -8
  117. package/plugins/steve/worker/web-worker.basic.js +23 -4
  118. package/rancher-components/components/Form/Checkbox/Checkbox.vue +2 -2
  119. package/rancher-components/components/Form/Radio/RadioGroup.vue +2 -2
  120. package/rancher-components/components/LabeledTooltip/LabeledTooltip.vue +1 -1
  121. package/store/index.js +16 -61
  122. package/store/store-types.js +5 -0
  123. package/store/type-map.js +1 -1
  124. package/types/shell/index.d.ts +23 -7
  125. package/utils/__tests__/create-yaml.test.ts +63 -0
  126. package/utils/array.ts +4 -0
  127. package/utils/create-yaml.js +5 -5
  128. package/utils/namespace-filter.js +17 -5
  129. package/utils/projectAndNamespaceFiltering.utils.ts +62 -0
  130. package/utils/selector.js +6 -5
  131. package/utils/settings.ts +5 -7
  132. package/models/k8s.cni.cncf.io.networkattachmentdefinition.js +0 -93
@@ -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 }}
@@ -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
+ });
@@ -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);
@@ -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.9",
3
+ "version": "0.3.11",
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",
@@ -89,17 +89,18 @@
89
89
  "jest": "27.5.1",
90
90
  "jest-serializer-vue": "2.0.2",
91
91
  "jexl": "2.2.2",
92
+ "jquery": "3.5.1",
92
93
  "js-cookie": "2.2.1",
93
94
  "js-yaml": "4.1.0",
94
95
  "js-yaml-loader": "1.2.2",
95
96
  "jsdiff": "1.1.1",
96
97
  "jsdom-global": "3.0.2",
97
98
  "jsonpath-plus": "6.0.1",
98
- "jsrsasign": "10.2.0",
99
- "jszip": "3.7.0",
99
+ "jsrsasign": "10.5.25",
100
+ "jszip": "3.8.0",
100
101
  "lodash": "4.17.21",
101
102
  "marked": "4.0.17",
102
- "nodemon": "2.0.4",
103
+ "nodemon": "2.0.22",
103
104
  "nuxt": "2.15.8",
104
105
  "nyc": "15.1.0",
105
106
  "papaparse": "5.3.0",
@@ -143,6 +144,17 @@
143
144
  "worker-loader": "3.0.8",
144
145
  "yarn": "1.22.18"
145
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
+ },
146
158
  "nyc": {
147
159
  "extension": [
148
160
  ".js",
@@ -159,6 +159,7 @@ export default {
159
159
  v-if="canChangePassword"
160
160
  type="button"
161
161
  class="btn role-primary"
162
+ data-testid="account_change_password"
162
163
  @click="$refs.promptChangePassword.show(true)"
163
164
  >
164
165
  {{ t("accountAndKeys.account.change") }}
@@ -179,6 +180,7 @@ export default {
179
180
  <button
180
181
  v-if="apiKeySchema"
181
182
  class="btn role-primary add mb-20"
183
+ data-testid="account_create_api_keys"
182
184
  @click="addKey"
183
185
  >
184
186
  {{ t('accountAndKeys.apiKeys.add.label') }}
@@ -193,6 +195,7 @@ export default {
193
195
  :rows="apiKeys"
194
196
  :headers="apiKeyheaders"
195
197
  key-field="id"
198
+ data-testid="api_keys_list"
196
199
  :search="true"
197
200
  :row-actions="true"
198
201
  :table-actions="true"
@@ -326,60 +326,59 @@ export default {
326
326
  </div>
327
327
  <div
328
328
  v-if="firstLogin"
329
- class="first-login-message"
329
+ class="first-login-message pl-10 pr-10"
330
+ :class="{'mt-30': !hasLoginMessage}"
330
331
  data-testid="first-login-message"
331
332
  >
332
- <InfoBox color="info">
333
+ <t
334
+ k="setup.defaultPassword.intro"
335
+ :raw="true"
336
+ />
337
+
338
+ <div>
333
339
  <t
334
- k="setup.defaultPassword.intro"
340
+ k="setup.defaultPassword.dockerPrefix"
335
341
  :raw="true"
336
342
  />
337
-
338
- <div>
339
- <t
340
- k="setup.defaultPassword.dockerPrefix"
341
- :raw="true"
342
- />
343
- </div>
344
- <ul>
345
- <li>
346
- <t
347
- k="setup.defaultPassword.dockerPs"
348
- :raw="true"
349
- />
350
- </li>
351
- <li>
352
- <CopyCode>
353
- docker logs <u>container-id</u> 2&gt;&amp;1 | grep "Bootstrap Password:"
354
- </CopyCode>
355
- </li>
356
- </ul>
357
- <div>
343
+ </div>
344
+ <ul>
345
+ <li>
358
346
  <t
359
- k="setup.defaultPassword.dockerSuffix"
347
+ k="setup.defaultPassword.dockerPs"
360
348
  :raw="true"
361
349
  />
362
- </div>
350
+ </li>
351
+ <li>
352
+ <CopyCode>
353
+ docker logs <u>container-id</u> 2&gt;&amp;1 | grep "Bootstrap Password:"
354
+ </CopyCode>
355
+ </li>
356
+ </ul>
357
+ <div>
358
+ <t
359
+ k="setup.defaultPassword.dockerSuffix"
360
+ :raw="true"
361
+ />
362
+ </div>
363
363
 
364
- <br>
365
- <div>
366
- <t
367
- k="setup.defaultPassword.helmPrefix"
368
- :raw="true"
369
- />
370
- </div>
371
- <br>
372
- <CopyCode>
373
- {{ kubectlCmd }}
374
- </CopyCode>
375
- <br>
376
- <div>
377
- <t
378
- k="setup.defaultPassword.helmSuffix"
379
- :raw="true"
380
- />
381
- </div>
382
- </InfoBox>
364
+ <br>
365
+ <div>
366
+ <t
367
+ k="setup.defaultPassword.helmPrefix"
368
+ :raw="true"
369
+ />
370
+ </div>
371
+ <br>
372
+ <CopyCode>
373
+ {{ kubectlCmd }}
374
+ </CopyCode>
375
+ <br>
376
+ <div>
377
+ <t
378
+ k="setup.defaultPassword.helmSuffix"
379
+ :raw="true"
380
+ />
381
+ </div>
383
382
  </div>
384
383
 
385
384
  <div
@@ -508,6 +507,8 @@ export default {
508
507
  }
509
508
 
510
509
  .login-messages {
510
+ display: flex;
511
+ justify-content: center;
511
512
  align-items: center;
512
513
 
513
514
  .banner {
@@ -519,11 +520,7 @@ export default {
519
520
  &--hasContent {
520
521
  min-height: 70px;
521
522
  }
522
- }
523
523
 
524
- .login-messages, .first-login-message {
525
- display: flex;
526
- justify-content: center;
527
524
  .text-error, .banner {
528
525
  max-width: 80%;
529
526
  }
@@ -224,7 +224,7 @@ export default {
224
224
  <b v-if="vers.originalVersion === version.version">{{ vers.originalVersion === currentVersion ? t('catalog.install.versions.current', { ver: currentVersion }): vers.shortLabel }}</b>
225
225
  <a
226
226
  v-else
227
- v-tooltip="vers.label.length > 16 ? vers.label : null"
227
+ v-clean-tooltip="vers.label.length > 16 ? vers.label : null"
228
228
  @click.prevent="selectVersion(vers)"
229
229
  >
230
230
  {{ vers.originalVersion === currentVersion ? t('catalog.install.versions.current', { ver: currentVersion }): vers.shortLabel }}