@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.
- package/assets/translations/en-us.yaml +19 -24
- package/assets/translations/zh-hans.yaml +82 -16
- package/chart/istio.vue +11 -11
- package/chart/rancher-backup/S3.vue +1 -1
- package/components/AsyncButton.vue +2 -2
- package/components/ButtonGroup.vue +1 -1
- package/components/CompoundStatusBadge.vue +1 -1
- package/components/CopyCode.vue +1 -1
- package/components/DetailText.vue +1 -0
- package/components/DetailTop.vue +1 -1
- package/components/ExplorerProjectsNamespaces.vue +3 -3
- package/components/GlobalRoleBindings.vue +1 -1
- package/components/HarvesterServiceAddOnConfig.vue +2 -117
- package/components/ResourceDetail/Masthead.vue +1 -1
- package/components/ResourceList/Masthead.vue +0 -6
- package/components/ResourceList/ResourceLoadingIndicator.vue +1 -9
- package/components/ResourceList/index.vue +7 -6
- package/components/ResourceTable.vue +13 -3
- package/components/SortableTable/THead.vue +4 -3
- package/components/SortableTable/index.vue +3 -3
- package/components/Tabbed/Tab.vue +1 -1
- package/components/Tabbed/index.vue +1 -1
- package/components/Wizard.vue +9 -6
- package/components/__tests__/NamespaceFilter.test.ts +26 -7
- package/components/auth/RoleDetailEdit.vue +1 -1
- package/components/auth/SelectPrincipal.vue +1 -1
- package/components/fleet/FleetRepos.vue +1 -1
- package/components/form/ArrayList.vue +1 -1
- package/components/form/ChangePassword.vue +3 -0
- package/components/form/KeyValue.vue +3 -2
- package/components/form/Labels.vue +34 -14
- package/components/form/Members/ClusterPermissionsEditor.vue +1 -1
- package/components/form/NameNsDescription.vue +1 -1
- package/components/form/PlusMinus.vue +2 -2
- package/components/form/Probe.vue +1 -1
- package/components/form/ProjectMemberEditor.vue +8 -4
- package/components/form/ResourceQuota/NamespaceRow.vue +1 -1
- package/components/form/ServicePorts.vue +2 -2
- package/components/form/Tolerations.vue +30 -3
- package/components/form/WorkloadPorts.vue +2 -1
- package/components/form/__tests__/KeyValue.test.ts +17 -0
- package/components/formatter/ClusterLink.vue +3 -3
- package/components/formatter/LiveDate.vue +1 -1
- package/components/formatter/PodImages.vue +1 -1
- package/components/formatter/RKETemplateName.vue +1 -1
- package/components/formatter/Shortened.vue +1 -1
- package/components/nav/Header.vue +7 -7
- package/components/nav/NamespaceFilter.vue +103 -54
- package/config/labels-annotations.js +8 -5
- package/config/settings.ts +2 -5
- package/config/types.js +6 -4
- package/core/plugin-routes.ts +26 -7
- package/core/plugins-loader.js +2 -0
- package/detail/helm.cattle.io.projecthelmchart.vue +2 -2
- package/detail/provisioning.cattle.io.cluster.vue +4 -4
- package/edit/cis.cattle.io.clusterscan.vue +1 -1
- package/edit/k8s.cni.cncf.io.networkattachmentdefinition.vue +19 -149
- package/edit/logging-flow/index.vue +2 -2
- package/edit/logging.banzaicloud.io.output/providers/elasticsearch.vue +12 -0
- package/edit/logging.banzaicloud.io.output/providers/opensearch.vue +12 -0
- package/edit/management.cattle.io.project.vue +7 -0
- package/edit/monitoring.coreos.com.alertmanagerconfig/index.vue +1 -1
- package/edit/monitoring.coreos.com.alertmanagerconfig/routeConfig.vue +2 -2
- package/edit/monitoring.coreos.com.prometheusrule/GroupRules.vue +11 -8
- package/edit/networking.k8s.io.networkpolicy/PolicyRule.vue +2 -2
- package/edit/networking.k8s.io.networkpolicy/PolicyRuleTarget.vue +12 -4
- package/edit/networking.k8s.io.networkpolicy/__tests__/PolicyRuleTarget.spec.ts +140 -0
- package/edit/networking.k8s.io.networkpolicy/__tests__/utils/mock.json +158 -0
- package/edit/networking.k8s.io.networkpolicy/__tests__/utils/selectors.ts +45 -0
- package/edit/networking.k8s.io.networkpolicy/index.vue +1 -1
- package/edit/provisioning.cattle.io.cluster/AgentConfiguration.vue +1 -1
- package/edit/provisioning.cattle.io.cluster/MachinePool.vue +1 -1
- package/edit/provisioning.cattle.io.cluster/RegistryConfigs.vue +1 -1
- package/edit/provisioning.cattle.io.cluster/RegistryMirrors.vue +2 -2
- package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +143 -169
- package/edit/provisioning.cattle.io.cluster/rke2.vue +15 -6
- package/edit/resources.cattle.io.restore.vue +2 -2
- package/edit/service.vue +22 -3
- package/edit/storage.k8s.io.storageclass/index.vue +1 -1
- package/edit/token.vue +1 -0
- package/edit/workload/Job.vue +2 -2
- package/edit/workload/index.vue +1 -1
- package/edit/workload/mixins/workload.js +7 -1
- package/edit/workload/storage/__tests__/Storage.test.ts +84 -5
- package/initialize/index.js +1 -0
- package/layouts/default.vue +1 -1
- package/mixins/resource-fetch-namespaced.js +19 -27
- package/mixins/resource-fetch.js +0 -5
- package/models/__tests__/namespace.test.ts +125 -0
- package/models/management.cattle.io.project.js +6 -1
- package/models/persistentvolume.js +1 -1
- package/models/workload.service.js +22 -7
- package/package.json +17 -5
- package/pages/account/index.vue +3 -0
- package/pages/auth/login.vue +46 -49
- package/pages/c/_cluster/apps/charts/chart.vue +1 -1
- package/pages/c/_cluster/apps/charts/install.vue +42 -51
- package/pages/c/_cluster/explorer/index.vue +1 -1
- package/pages/c/_cluster/monitoring/index.vue +1 -1
- package/pages/c/_cluster/settings/performance.vue +53 -18
- package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +1 -1
- package/pages/c/_cluster/uiplugins/index.vue +16 -5
- package/pages/home.vue +1 -1
- package/pkg/vue.config.js +1 -0
- package/plugins/clean-html-directive.js +1 -1
- package/plugins/clean-tooltip-directive.js +33 -0
- package/plugins/dashboard-store/actions.js +4 -2
- package/plugins/dashboard-store/getters.js +6 -0
- package/plugins/dashboard-store/mutations.js +2 -2
- package/plugins/plugin.js +6 -1
- package/plugins/steve/actions.js +1 -1
- package/plugins/steve/getters.js +14 -3
- package/plugins/steve/resourceWatcher.js +36 -62
- package/plugins/steve/subscribe.js +164 -21
- package/plugins/steve/worker/index.js +8 -1
- package/plugins/steve/worker/web-worker.advanced.js +26 -8
- package/plugins/steve/worker/web-worker.basic.js +23 -4
- package/rancher-components/components/Form/Checkbox/Checkbox.vue +2 -2
- package/rancher-components/components/Form/Radio/RadioGroup.vue +2 -2
- package/rancher-components/components/LabeledTooltip/LabeledTooltip.vue +1 -1
- package/store/index.js +16 -61
- package/store/store-types.js +5 -0
- package/store/type-map.js +1 -1
- package/types/shell/index.d.ts +23 -7
- package/utils/__tests__/create-yaml.test.ts +63 -0
- package/utils/array.ts +4 -0
- package/utils/create-yaml.js +5 -5
- package/utils/namespace-filter.js +17 -5
- package/utils/projectAndNamespaceFiltering.utils.ts +62 -0
- package/utils/selector.js +6 -5
- package/utils/settings.ts +5 -7
- 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
|
-
|
|
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, //
|
|
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('#
|
|
17
|
-
|
|
90
|
+
await wrapper.find('#select-volume').trigger('click');
|
|
91
|
+
await wrapper.trigger('keydown.down');
|
|
92
|
+
await wrapper.trigger('keydown.enter');
|
|
18
93
|
|
|
19
|
-
expect(
|
|
94
|
+
expect(wrapper.vm.value.volumes[0]).toStrictEqual({
|
|
95
|
+
_type: 'awsElasticBlockStore',
|
|
96
|
+
awsElasticBlockStore: {},
|
|
97
|
+
name: ''
|
|
98
|
+
});
|
|
20
99
|
});
|
|
21
100
|
});
|
package/initialize/index.js
CHANGED
|
@@ -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';
|
package/layouts/default.vue
CHANGED
|
@@ -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', '
|
|
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.
|
|
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.
|
|
29
|
+
return this.__namespaceRequired ? this.__validFilter : '';
|
|
28
30
|
},
|
|
29
31
|
|
|
30
32
|
/**
|
|
31
|
-
* If the Project/Namespace filter from the header contains a
|
|
33
|
+
* If the Project/Namespace filter from the header contains a valid ns / project filter ... return it
|
|
32
34
|
*/
|
|
33
|
-
|
|
34
|
-
const
|
|
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
|
|
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 (!
|
|
47
|
+
if (!pAndNFiltering.isEnabled(this.$store.getters)) {
|
|
44
48
|
return false;
|
|
45
49
|
}
|
|
46
50
|
|
|
47
|
-
return
|
|
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
|
package/mixins/resource-fetch.js
CHANGED
|
@@ -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
|
-
|
|
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.
|
|
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
|
-
|
|
307
|
+
loadBalancerProxy = await this.$dispatch(`cluster/create`, loadBalancer, { root: true });
|
|
308
|
+
}
|
|
308
309
|
|
|
309
|
-
|
|
310
|
+
const portsWithIpam = ports.filter(p => p._ipam) || [];
|
|
310
311
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
}
|
|
312
|
+
if (portsWithIpam.length > 0) {
|
|
313
|
+
loadBalancerProxy.metadata.annotations[HCI_LABELS_ANNOTATIONS.CLOUD_PROVIDER_IPAM] = portsWithIpam[0]._ipam;
|
|
314
314
|
|
|
315
|
-
|
|
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.
|
|
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": "
|
|
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.
|
|
99
|
-
"jszip": "3.
|
|
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.
|
|
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",
|
package/pages/account/index.vue
CHANGED
|
@@ -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"
|
package/pages/auth/login.vue
CHANGED
|
@@ -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
|
-
<
|
|
333
|
+
<t
|
|
334
|
+
k="setup.defaultPassword.intro"
|
|
335
|
+
:raw="true"
|
|
336
|
+
/>
|
|
337
|
+
|
|
338
|
+
<div>
|
|
333
339
|
<t
|
|
334
|
-
k="setup.defaultPassword.
|
|
340
|
+
k="setup.defaultPassword.dockerPrefix"
|
|
335
341
|
:raw="true"
|
|
336
342
|
/>
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
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>&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.
|
|
347
|
+
k="setup.defaultPassword.dockerPs"
|
|
360
348
|
:raw="true"
|
|
361
349
|
/>
|
|
362
|
-
</
|
|
350
|
+
</li>
|
|
351
|
+
<li>
|
|
352
|
+
<CopyCode>
|
|
353
|
+
docker logs <u>container-id</u> 2>&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
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
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 }}
|