@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.
- package/assets/translations/en-us.yaml +47 -26
- package/assets/translations/zh-hans.yaml +82 -16
- package/babel.config.js +17 -4
- 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/CodeMirror.vue +146 -14
- package/components/CompoundStatusBadge.vue +1 -1
- package/components/ContainerResourceLimit.vue +14 -1
- package/components/CopyCode.vue +1 -1
- package/components/CruResource.vue +21 -5
- package/components/DetailTop.vue +1 -1
- package/components/ExplorerProjectsNamespaces.vue +8 -4
- package/components/GlobalRoleBindings.vue +1 -1
- package/components/GroupPanel.vue +57 -0
- 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 +3 -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/YamlEditor.vue +2 -2
- 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 +2 -2
- package/components/form/KeyValue.vue +37 -3
- package/components/form/Labels.vue +34 -14
- package/components/form/MatchExpressions.vue +120 -21
- package/components/form/Members/ClusterPermissionsEditor.vue +1 -1
- package/components/form/NameNsDescription.vue +1 -1
- package/components/form/NodeAffinity.vue +54 -4
- package/components/form/PlusMinus.vue +2 -2
- package/components/form/PodAffinity.vue +160 -47
- 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 +70 -7
- package/components/form/WorkloadPorts.vue +2 -1
- package/components/form/__tests__/ArrayList.test.ts +3 -3
- package/components/form/__tests__/KeyValue.test.ts +17 -0
- package/components/form/__tests__/MatchExpressions.test.ts +1 -1
- 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 +9 -7
- package/components/nav/NamespaceFilter.vue +103 -54
- package/config/labels-annotations.js +8 -5
- package/config/settings.ts +8 -6
- package/config/types.js +6 -4
- package/core/plugin-routes.ts +26 -7
- package/detail/provisioning.cattle.io.cluster.vue +4 -4
- package/edit/cis.cattle.io.clusterscan.vue +1 -1
- package/edit/configmap.vue +33 -6
- 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 +326 -0
- 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/index.vue +1 -0
- package/edit/provisioning.cattle.io.cluster/rke2.vue +75 -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/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/chart.js +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/batch.cronjob.js +18 -3
- package/models/management.cattle.io.project.js +6 -1
- package/models/persistentvolume.js +1 -1
- package/models/workload.js +1 -1
- package/models/workload.service.js +22 -7
- package/package.json +17 -6
- package/pages/auth/login.vue +47 -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/pages/prefs.vue +18 -2
- package/plugins/clean-html-directive.js +1 -1
- package/plugins/clean-tooltip-directive.js +33 -0
- package/plugins/codemirror.js +158 -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 +137 -21
- package/plugins/steve/worker/index.js +7 -1
- package/plugins/steve/worker/web-worker.advanced.js +26 -8
- package/plugins/steve/worker/web-worker.basic.js +23 -4
- package/public/index.html +1 -1
- 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 +42 -7
- package/utils/__tests__/create-yaml.test.ts +63 -0
- package/utils/array.ts +4 -0
- package/utils/create-yaml.js +105 -8
- package/utils/namespace-filter.js +17 -5
- package/utils/projectAndNamespaceFiltering.utils.ts +62 -0
- package/utils/selector.js +6 -5
- package/utils/settings.ts +17 -7
- package/vue.config.js +2 -2
- package/models/k8s.cni.cncf.io.networkattachmentdefinition.js +0 -93
package/edit/workload/Job.vue
CHANGED
|
@@ -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>
|
package/edit/workload/index.vue
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
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', '
|
|
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
|
+
});
|
package/models/batch.cronjob.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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
|
-
|
|
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);
|
package/models/workload.js
CHANGED
|
@@ -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.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": "
|
|
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.
|
|
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.
|
|
100
|
-
"jszip": "3.
|
|
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.
|
|
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",
|