@rancher/shell 2.0.0 → 2.0.2-rc.1
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 +69 -29
- package/assets/translations/zh-hans.yaml +1 -0
- package/components/AlertTable.vue +17 -7
- package/components/AssignTo.vue +2 -0
- package/components/GrafanaDashboard.vue +6 -4
- package/components/PromptRemove.vue +1 -0
- package/components/Questions/index.vue +2 -2
- package/components/auth/RoleDetailEdit.vue +5 -4
- package/components/form/KeyValue.vue +1 -0
- package/components/form/Members/ClusterPermissionsEditor.vue +1 -1
- package/components/form/ProjectMemberEditor.vue +1 -1
- package/components/form/ResourceLabeledSelect.vue +11 -3
- package/components/form/Taints.vue +13 -7
- package/components/form/__tests__/Taints.test.ts +70 -0
- package/components/form/labeled-select-utils/labeled-select.utils.ts +1 -1
- package/components/nav/Header.vue +1 -1
- package/components/nav/TopLevelMenu.vue +1 -4
- package/config/pagination-table-headers.js +5 -4
- package/config/product/auth.js +1 -1
- package/config/roles.ts +34 -19
- package/config/router/navigation-guards/attempt-first-login.js +1 -1
- package/config/router/navigation-guards/authentication.js +1 -1
- package/config/router/navigation-guards/i18n.js +13 -0
- package/config/router/navigation-guards/index.js +3 -1
- package/config/router/navigation-guards/load-initial-settings.js +1 -1
- package/config/router/navigation-guards/runtime-extension-route.js +31 -0
- package/config/router/routes.js +10 -1
- package/config/uiplugins.js +130 -61
- package/core/plugin.ts +5 -0
- package/core/plugins.js +7 -1
- package/detail/__tests__/provisioning.cattle.io.cluster.test.ts +42 -0
- package/detail/provisioning.cattle.io.cluster.vue +4 -4
- package/dialog/DeactivateDriverDialog.vue +30 -11
- package/edit/auth/__tests__/oidc.test.ts +2 -2
- package/edit/provisioning.cattle.io.cluster/__tests__/Basics.test.ts +86 -13
- package/edit/provisioning.cattle.io.cluster/__tests__/DirectoryConfig.test.ts +3 -134
- package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +209 -0
- package/edit/provisioning.cattle.io.cluster/index.vue +8 -4
- package/edit/provisioning.cattle.io.cluster/rke2.vue +115 -17
- package/edit/provisioning.cattle.io.cluster/tabs/AddOnAdditionalManifest.vue +50 -0
- package/edit/provisioning.cattle.io.cluster/tabs/AddOnConfig.vue +29 -64
- package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +42 -3
- package/edit/provisioning.cattle.io.cluster/tabs/DirectoryConfig.vue +22 -86
- package/edit/provisioning.cattle.io.cluster/tabs/registries/RegistryConfigs.vue +8 -2
- package/edit/provisioning.cattle.io.cluster/tabs/registries/__tests__/RegistryConfigs.test.ts +61 -0
- package/edit/token.vue +2 -1
- package/initialize/entry-helpers.js +4 -24
- package/list/management.cattle.io.feature.vue +4 -2
- package/middleware/authenticated.js +0 -19
- package/mixins/__tests__/chart.test.ts +4 -1
- package/mixins/auth-config.js +1 -1
- package/mixins/chart.js +30 -14
- package/models/__tests__/apps.deployment.test.ts +93 -0
- package/models/apps.deployment.js +18 -4
- package/models/driver.js +3 -2
- package/models/kontainerdriver.js +30 -13
- package/models/management.cattle.io.authconfig.js +2 -2
- package/models/management.cattle.io.cluster.js +2 -2
- package/models/management.cattle.io.user.js +3 -3
- package/models/nodedriver.js +35 -13
- package/models/provisioning.cattle.io.cluster.js +4 -0
- package/package.json +3 -2
- package/pages/404.vue +15 -0
- package/pages/auth/login.vue +4 -1
- package/pages/auth/setup.vue +4 -1
- package/pages/c/_cluster/apps/charts/install.vue +3 -2
- package/pages/c/_cluster/explorer/index.vue +5 -0
- package/pages/c/_cluster/manager/drivers/kontainerDriver/index.vue +0 -3
- package/pages/c/_cluster/manager/drivers/nodeDriver/index.vue +1 -4
- package/pages/c/_cluster/manager/jwt.authentication/index.vue +10 -4
- package/pages/c/_cluster/settings/performance.vue +2 -2
- package/pages/c/_cluster/uiplugins/InstallDialog.vue +2 -1
- package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +7 -10
- package/pages/c/_cluster/uiplugins/index.vue +24 -16
- package/pages/home.vue +1 -13
- package/plugins/dashboard-store/actions.js +1 -1
- package/plugins/dashboard-store/getters.js +1 -1
- package/plugins/steve/__tests__/getters.test.ts +5 -5
- package/plugins/steve/getters.js +6 -4
- package/plugins/steve/hybrid-class.js +1 -5
- package/promptRemove/pod.vue +15 -7
- package/scripts/extension/helm/charts/ui-plugin-server/Chart.yaml +1 -1
- package/scripts/publish-shell.sh +54 -55
- package/scripts/test-plugins-build.sh +45 -39
- package/shell/types/shell/index.d.ts +2 -0
- package/store/auth.js +1 -1
- package/store/index.js +1 -1
- package/store/type-map.js +4 -2
- package/types/store/pagination.types.ts +1 -1
- package/utils/__tests__/kontainer.test.ts +89 -1
- package/utils/auth.js +1 -1
- package/utils/cluster.js +9 -0
- package/utils/kontainer.ts +5 -1
- package/utils/settings.ts +3 -1
- package/utils/version.js +2 -1
- package/creators/app/app.package.json +0 -13
- package/creators/app/files/.eslintignore +0 -16
- package/creators/app/files/.eslintrc.js +0 -173
- package/creators/app/files/.gitignore +0 -70
- package/creators/app/files/.gitlab-ci.yml +0 -14
- package/creators/app/files/.vscode/settings.json +0 -21
- package/creators/app/files/babel.config.js +0 -1
- package/creators/app/files/tsconfig.json +0 -42
- package/creators/app/files/vue.config.js +0 -6
- package/creators/app/init +0 -120
- package/creators/app/package.json +0 -25
- package/creators/pkg/files/.github/workflows/build-extension-catalog.yml +0 -24
- package/creators/pkg/files/.github/workflows/build-extension-charts.yml +0 -22
- package/creators/pkg/files/babel.config.js +0 -1
- package/creators/pkg/files/index.ts +0 -14
- package/creators/pkg/files/tsconfig.json +0 -53
- package/creators/pkg/files/vue.config.js +0 -1
- package/creators/pkg/init +0 -286
- package/creators/pkg/package.json +0 -19
- package/creators/pkg/pkg.package.json +0 -21
- package/creators/pkg/vue-shim.ts +0 -4
- package/creators/update/init +0 -56
- package/creators/update/package.json +0 -20
- package/creators/update/upgrade +0 -56
- package/rancher-components/components/Accordion/Accordion.test.ts +0 -45
- package/rancher-components/components/Accordion/Accordion.vue +0 -86
- package/rancher-components/components/Accordion/index.ts +0 -1
- package/rancher-components/components/BadgeState/BadgeState.test.ts +0 -12
- package/rancher-components/components/BadgeState/BadgeState.vue +0 -111
- package/rancher-components/components/BadgeState/index.ts +0 -1
- package/rancher-components/components/Banner/Banner.test.ts +0 -59
- package/rancher-components/components/Banner/Banner.vue +0 -244
- package/rancher-components/components/Banner/index.ts +0 -1
- package/rancher-components/components/Card/Card.test.ts +0 -37
- package/rancher-components/components/Card/Card.vue +0 -167
- package/rancher-components/components/Card/index.ts +0 -1
- package/rancher-components/components/Form/Checkbox/Checkbox.test.ts +0 -68
- package/rancher-components/components/Form/Checkbox/Checkbox.vue +0 -421
- package/rancher-components/components/Form/Checkbox/index.ts +0 -1
- package/rancher-components/components/Form/LabeledInput/LabeledInput.test.ts +0 -40
- package/rancher-components/components/Form/LabeledInput/LabeledInput.vue +0 -402
- package/rancher-components/components/Form/LabeledInput/index.ts +0 -1
- package/rancher-components/components/Form/Radio/RadioButton.test.ts +0 -33
- package/rancher-components/components/Form/Radio/RadioButton.vue +0 -293
- package/rancher-components/components/Form/Radio/RadioGroup.test.ts +0 -30
- package/rancher-components/components/Form/Radio/RadioGroup.vue +0 -259
- package/rancher-components/components/Form/Radio/index.ts +0 -2
- package/rancher-components/components/Form/TextArea/TextAreaAutoGrow.vue +0 -172
- package/rancher-components/components/Form/TextArea/index.ts +0 -1
- package/rancher-components/components/Form/ToggleSwitch/ToggleSwitch.test.ts +0 -94
- package/rancher-components/components/Form/ToggleSwitch/ToggleSwitch.vue +0 -152
- package/rancher-components/components/Form/ToggleSwitch/index.ts +0 -1
- package/rancher-components/components/Form/index.ts +0 -5
- package/rancher-components/components/LabeledTooltip/LabeledTooltip.vue +0 -156
- package/rancher-components/components/LabeledTooltip/index.ts +0 -1
- package/rancher-components/components/StringList/StringList.test.ts +0 -754
- package/rancher-components/components/StringList/StringList.vue +0 -650
- package/rancher-components/components/StringList/index.ts +0 -1
- package/types/shell/index.d.ts +0 -4585
|
@@ -1,16 +1,12 @@
|
|
|
1
1
|
|
|
2
2
|
<script>
|
|
3
|
-
import { Checkbox } from '@components/Form/Checkbox';
|
|
4
3
|
import { LabeledInput } from '@components/Form/LabeledInput';
|
|
5
4
|
import { _CREATE } from '@shell/config/query-params';
|
|
6
5
|
|
|
7
6
|
export default {
|
|
8
7
|
name: 'DirectoryConfig',
|
|
9
|
-
components: {
|
|
10
|
-
|
|
11
|
-
LabeledInput,
|
|
12
|
-
},
|
|
13
|
-
props: {
|
|
8
|
+
components: { LabeledInput },
|
|
9
|
+
props: {
|
|
14
10
|
mode: {
|
|
15
11
|
type: String,
|
|
16
12
|
required: true,
|
|
@@ -21,50 +17,11 @@ export default {
|
|
|
21
17
|
required: true,
|
|
22
18
|
},
|
|
23
19
|
},
|
|
24
|
-
data() {
|
|
25
|
-
let atLeastOneDirWithAnIdentifier = false;
|
|
26
|
-
let allDirsWithSameIdentifier = false;
|
|
27
|
-
|
|
28
|
-
if (this.value.systemAgent.length || this.value.provisioning.length || this.value.k8sDistro.length) {
|
|
29
|
-
atLeastOneDirWithAnIdentifier = true;
|
|
30
|
-
if (this.value.systemAgent === this.value.provisioning && this.value.provisioning === this.value.k8sDistro &&
|
|
31
|
-
this.value.systemAgent === this.value.k8sDistro) {
|
|
32
|
-
allDirsWithSameIdentifier = true;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
return {
|
|
37
|
-
isSettingCommonConfig: !(atLeastOneDirWithAnIdentifier && !allDirsWithSameIdentifier),
|
|
38
|
-
commonConfig: allDirsWithSameIdentifier ? this.value.systemAgent : '',
|
|
39
|
-
};
|
|
40
|
-
},
|
|
41
|
-
watch: {
|
|
42
|
-
commonConfig(neu) {
|
|
43
|
-
if (neu && neu.length && this.isSettingCommonConfig) {
|
|
44
|
-
this.value.systemAgent = neu;
|
|
45
|
-
this.value.provisioning = neu;
|
|
46
|
-
this.value.k8sDistro = neu;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
},
|
|
50
20
|
computed: {
|
|
51
21
|
disableEditInput() {
|
|
52
22
|
return this.mode !== _CREATE;
|
|
53
23
|
}
|
|
54
|
-
}
|
|
55
|
-
methods: {
|
|
56
|
-
handleCommonConfig(val) {
|
|
57
|
-
this.isSettingCommonConfig = val;
|
|
58
|
-
|
|
59
|
-
if (val) {
|
|
60
|
-
this.value.systemAgent = '';
|
|
61
|
-
this.value.provisioning = '';
|
|
62
|
-
this.value.k8sDistro = '';
|
|
63
|
-
} else {
|
|
64
|
-
this.commonConfig = '';
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
},
|
|
24
|
+
}
|
|
68
25
|
};
|
|
69
26
|
</script>
|
|
70
27
|
|
|
@@ -74,54 +31,33 @@ export default {
|
|
|
74
31
|
<h3 class="mb-20">
|
|
75
32
|
{{ t('cluster.directoryConfig.title') }}
|
|
76
33
|
</h3>
|
|
77
|
-
<
|
|
78
|
-
|
|
79
|
-
|
|
34
|
+
<LabeledInput
|
|
35
|
+
v-model="value.systemAgent"
|
|
36
|
+
class="mb-20"
|
|
37
|
+
:mode="mode"
|
|
38
|
+
:label="t('cluster.directoryConfig.systemAgent.label')"
|
|
39
|
+
:tooltip="t('cluster.directoryConfig.systemAgent.tooltip')"
|
|
40
|
+
:disabled="disableEditInput"
|
|
41
|
+
data-testid="rke2-directory-config-systemAgent-data-dir"
|
|
42
|
+
/>
|
|
43
|
+
<LabeledInput
|
|
44
|
+
v-model="value.provisioning"
|
|
45
|
+
class="mb-20"
|
|
80
46
|
:mode="mode"
|
|
81
|
-
:label="t('cluster.directoryConfig.
|
|
47
|
+
:label="t('cluster.directoryConfig.provisioning.label')"
|
|
48
|
+
:tooltip="t('cluster.directoryConfig.provisioning.tooltip')"
|
|
82
49
|
:disabled="disableEditInput"
|
|
83
|
-
data-testid="rke2-directory-config-
|
|
84
|
-
@input="handleCommonConfig"
|
|
50
|
+
data-testid="rke2-directory-config-provisioning-data-dir"
|
|
85
51
|
/>
|
|
86
52
|
<LabeledInput
|
|
87
|
-
v-
|
|
88
|
-
v-model="commonConfig"
|
|
53
|
+
v-model="value.k8sDistro"
|
|
89
54
|
class="mb-20"
|
|
90
55
|
:mode="mode"
|
|
91
|
-
:label="t('cluster.directoryConfig.
|
|
92
|
-
:tooltip="t('cluster.directoryConfig.
|
|
56
|
+
:label="t('cluster.directoryConfig.k8sDistro.label')"
|
|
57
|
+
:tooltip="t('cluster.directoryConfig.k8sDistro.tooltip')"
|
|
93
58
|
:disabled="disableEditInput"
|
|
94
|
-
data-testid="rke2-directory-config-
|
|
59
|
+
data-testid="rke2-directory-config-k8sDistro-data-dir"
|
|
95
60
|
/>
|
|
96
|
-
<div v-if="!isSettingCommonConfig">
|
|
97
|
-
<LabeledInput
|
|
98
|
-
v-model="value.systemAgent"
|
|
99
|
-
class="mb-20"
|
|
100
|
-
:mode="mode"
|
|
101
|
-
:label="t('cluster.directoryConfig.systemAgent.label')"
|
|
102
|
-
:tooltip="t('cluster.directoryConfig.systemAgent.tooltip')"
|
|
103
|
-
:disabled="disableEditInput"
|
|
104
|
-
data-testid="rke2-directory-config-systemAgent-data-dir"
|
|
105
|
-
/>
|
|
106
|
-
<LabeledInput
|
|
107
|
-
v-model="value.provisioning"
|
|
108
|
-
class="mb-20"
|
|
109
|
-
:mode="mode"
|
|
110
|
-
:label="t('cluster.directoryConfig.provisioning.label')"
|
|
111
|
-
:tooltip="t('cluster.directoryConfig.provisioning.tooltip')"
|
|
112
|
-
:disabled="disableEditInput"
|
|
113
|
-
data-testid="rke2-directory-config-provisioning-data-dir"
|
|
114
|
-
/>
|
|
115
|
-
<LabeledInput
|
|
116
|
-
v-model="value.k8sDistro"
|
|
117
|
-
class="mb-20"
|
|
118
|
-
:mode="mode"
|
|
119
|
-
:label="t('cluster.directoryConfig.k8sDistro.label')"
|
|
120
|
-
:tooltip="t('cluster.directoryConfig.k8sDistro.tooltip')"
|
|
121
|
-
:disabled="disableEditInput"
|
|
122
|
-
data-testid="rke2-directory-config-k8sDistro-data-dir"
|
|
123
|
-
/>
|
|
124
|
-
</div>
|
|
125
61
|
<div class="mb-40" />
|
|
126
62
|
</div>
|
|
127
63
|
</div>
|
|
@@ -7,6 +7,7 @@ import SelectOrCreateAuthSecret from '@shell/components/form/SelectOrCreateAuthS
|
|
|
7
7
|
import CreateEditView from '@shell/mixins/create-edit-view';
|
|
8
8
|
import SecretSelector from '@shell/components/form/SecretSelector';
|
|
9
9
|
import { SECRET_TYPES as TYPES } from '@shell/config/secret';
|
|
10
|
+
import { base64Decode, base64Encode } from '@shell/utils/crypto';
|
|
10
11
|
|
|
11
12
|
export default {
|
|
12
13
|
components: {
|
|
@@ -55,7 +56,7 @@ export default {
|
|
|
55
56
|
if (configMap[hostname]) {
|
|
56
57
|
configMap[hostname].insecureSkipVerify = configMap[hostname].insecureSkipVerify ?? defaultAddValue.insecureSkipVerify;
|
|
57
58
|
configMap[hostname].authConfigSecretName = configMap[hostname].authConfigSecretName ?? defaultAddValue.authConfigSecretName;
|
|
58
|
-
configMap[hostname].caBundle = configMap[hostname].caBundle ?? defaultAddValue.caBundle;
|
|
59
|
+
configMap[hostname].caBundle = base64Decode(configMap[hostname].caBundle ?? defaultAddValue.caBundle);
|
|
59
60
|
configMap[hostname].tlsSecretName = configMap[hostname].tlsSecretName ?? defaultAddValue.tlsSecretName;
|
|
60
61
|
}
|
|
61
62
|
entries.push({
|
|
@@ -94,7 +95,11 @@ export default {
|
|
|
94
95
|
continue;
|
|
95
96
|
}
|
|
96
97
|
|
|
97
|
-
configs[h] = {
|
|
98
|
+
configs[h] = {
|
|
99
|
+
...entry,
|
|
100
|
+
caBundle: base64Encode(entry.caBundle)
|
|
101
|
+
};
|
|
102
|
+
|
|
98
103
|
delete configs[h].hostname;
|
|
99
104
|
}
|
|
100
105
|
|
|
@@ -177,6 +182,7 @@ export default {
|
|
|
177
182
|
|
|
178
183
|
<LabeledInput
|
|
179
184
|
v-model="row.value.caBundle"
|
|
185
|
+
:data-testid="`registry-caBundle-${i}`"
|
|
180
186
|
class="mt-20"
|
|
181
187
|
type="multiline"
|
|
182
188
|
label="CA Cert Bundle"
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { mount, Wrapper } from '@vue/test-utils';
|
|
2
|
+
import { clone } from '@shell/utils/object';
|
|
3
|
+
import { _EDIT } from '@shell/config/query-params';
|
|
4
|
+
import { PROV_CLUSTER } from '@shell/edit/provisioning.cattle.io.cluster/__tests__/utils/cluster';
|
|
5
|
+
import RegistryConfigs from '@shell/edit/provisioning.cattle.io.cluster/tabs/registries/RegistryConfigs.vue';
|
|
6
|
+
|
|
7
|
+
describe('component: RegistryConfigs', () => {
|
|
8
|
+
let wrapper: Wrapper<InstanceType<typeof RegistryConfigs> & { [key: string]: any }>;
|
|
9
|
+
|
|
10
|
+
const mountOptions = {
|
|
11
|
+
propsData: {
|
|
12
|
+
value: {},
|
|
13
|
+
mode: _EDIT,
|
|
14
|
+
clusterRegisterBeforeHook: () => {}
|
|
15
|
+
},
|
|
16
|
+
stubs: {
|
|
17
|
+
SelectOrCreateAuthSecret: true,
|
|
18
|
+
SecretSelector: true,
|
|
19
|
+
},
|
|
20
|
+
mocks: { $store: { getters: { 'i18n/t': jest.fn() } } }
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
describe('key CA Cert Bundle', () => {
|
|
24
|
+
it('should display default key', () => {
|
|
25
|
+
const value = clone(PROV_CLUSTER);
|
|
26
|
+
|
|
27
|
+
value.spec.rkeConfig.registries.configs = { foo: { caBundle: 'Zm9vYmFy' } };
|
|
28
|
+
|
|
29
|
+
mountOptions.propsData.value = value;
|
|
30
|
+
|
|
31
|
+
wrapper = mount(
|
|
32
|
+
RegistryConfigs,
|
|
33
|
+
mountOptions
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
const registry = wrapper.find('[data-testid^="registry-caBundle"]').element as HTMLTextAreaElement;
|
|
37
|
+
|
|
38
|
+
expect(registry.value).toBe('foobar');
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should update key in base64 format', async() => {
|
|
42
|
+
const value = clone(PROV_CLUSTER);
|
|
43
|
+
|
|
44
|
+
value.spec.rkeConfig.registries.configs = { foo: { caBundle: 'Zm9vYmFy' } };
|
|
45
|
+
|
|
46
|
+
mountOptions.propsData.value = value;
|
|
47
|
+
|
|
48
|
+
wrapper = mount(
|
|
49
|
+
RegistryConfigs,
|
|
50
|
+
mountOptions
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
const registry = wrapper.find('[data-testid^="registry-caBundle"]');
|
|
54
|
+
|
|
55
|
+
await registry.setValue('ssh key');
|
|
56
|
+
wrapper.vm.update();
|
|
57
|
+
|
|
58
|
+
expect(wrapper.emitted('updateConfigs')![0][0]['foo']['caBundle']).toBe('c3NoIGtleQ==');
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
});
|
package/edit/token.vue
CHANGED
|
@@ -13,6 +13,7 @@ import Select from '@shell/components/form/Select';
|
|
|
13
13
|
import CreateEditView from '@shell/mixins/create-edit-view';
|
|
14
14
|
import { diffFrom } from '@shell/utils/time';
|
|
15
15
|
import { filterHiddenLocalCluster, filterOnlyKubernetesClusters } from '@shell/utils/cluster';
|
|
16
|
+
import { SETTING } from '@shell/config/settings';
|
|
16
17
|
|
|
17
18
|
export default {
|
|
18
19
|
components: {
|
|
@@ -29,7 +30,7 @@ export default {
|
|
|
29
30
|
|
|
30
31
|
data() {
|
|
31
32
|
// Get the setting that defines the max token TTL allowed (in minutes)
|
|
32
|
-
const maxTTLSetting = this.$store.getters['management/byId'](MANAGEMENT.SETTING,
|
|
33
|
+
const maxTTLSetting = this.$store.getters['management/byId'](MANAGEMENT.SETTING, SETTING.AUTH_TOKEN_MAX_TTL_MINUTES);
|
|
33
34
|
let maxTTL = 0;
|
|
34
35
|
|
|
35
36
|
try {
|
|
@@ -190,34 +190,14 @@ async function render(to, from, next) {
|
|
|
190
190
|
next: _next.bind(this)
|
|
191
191
|
});
|
|
192
192
|
|
|
193
|
+
if (this.$loading.start && !this.$loading.manual) {
|
|
194
|
+
this.$loading.start();
|
|
195
|
+
}
|
|
196
|
+
|
|
193
197
|
// Get route's matched components
|
|
194
198
|
const matches = [];
|
|
195
199
|
const Components = getMatchedComponents(to, matches);
|
|
196
200
|
|
|
197
|
-
// If no Components matched, generate 404
|
|
198
|
-
if (!Components.length) {
|
|
199
|
-
// Call the authenticated middleware. This used to attempt to load the error layout but because it was missing it would:
|
|
200
|
-
// 1. load the default layout instead
|
|
201
|
-
// 2. then call the authenticated middleware
|
|
202
|
-
// 3. Authenticated middleware would then load plugins and check to see if there was a valid route and navigate to that if it existed
|
|
203
|
-
// 4. This would allow harvester cluster pages to load on page reload
|
|
204
|
-
// We should really make authenticated middleware do less...
|
|
205
|
-
await callMiddleware.call(this, [{ options: { middleware: ['authenticated'] } }], app.context);
|
|
206
|
-
|
|
207
|
-
// We used to have i18n middleware which was called each time we called middleware. This is also needed to support harvester because of the way harvester loads as outlined in the comment above
|
|
208
|
-
await this.$store.dispatch('i18n/init');
|
|
209
|
-
|
|
210
|
-
if (nextCalled) {
|
|
211
|
-
return;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// Show error page
|
|
215
|
-
// this.error({ statusCode: 404, message: 'This page could not be found' });
|
|
216
|
-
errorRedirect(this, new Error('404: This page could not be found'));
|
|
217
|
-
|
|
218
|
-
return next();
|
|
219
|
-
}
|
|
220
|
-
|
|
221
201
|
try {
|
|
222
202
|
// Call middleware
|
|
223
203
|
await callMiddleware.call(this, Components, app.context);
|
|
@@ -163,14 +163,16 @@ export default {
|
|
|
163
163
|
const response = await this.$axios.get(url, { timeout: 5000 });
|
|
164
164
|
|
|
165
165
|
if (response?.status === 200) {
|
|
166
|
-
|
|
166
|
+
await this.$store.dispatch('management/findAll', { type: this.resource, opt: { force: true } });
|
|
167
167
|
btnCB(true);
|
|
168
168
|
this.close();
|
|
169
169
|
this.waiting = false;
|
|
170
170
|
}
|
|
171
171
|
} catch (e) {}
|
|
172
172
|
|
|
173
|
-
this.
|
|
173
|
+
if (this.waiting) {
|
|
174
|
+
this.waitForBackend(btnCB, id);
|
|
175
|
+
}
|
|
174
176
|
}, 5000);
|
|
175
177
|
},
|
|
176
178
|
|
|
@@ -2,7 +2,6 @@ import { DEFAULT_WORKSPACE } from '@shell/config/types';
|
|
|
2
2
|
import { applyProducts } from '@shell/store/type-map';
|
|
3
3
|
import { ClusterNotFoundError, RedirectToError } from '@shell/utils/error';
|
|
4
4
|
import { get } from '@shell/utils/object';
|
|
5
|
-
import dynamicPluginLoader from '@shell/pkg/dynamic-plugin-loader';
|
|
6
5
|
import { AFTER_LOGIN_ROUTE, WORKSPACE } from '@shell/store/prefs';
|
|
7
6
|
import { BACK_TO } from '@shell/config/local-storage';
|
|
8
7
|
import { NAME as FLEET_NAME } from '@shell/config/product/fleet.js';
|
|
@@ -107,24 +106,6 @@ export default async function({
|
|
|
107
106
|
});
|
|
108
107
|
}
|
|
109
108
|
|
|
110
|
-
if (!route.matched?.length) {
|
|
111
|
-
// If there are no matching routes we could be trying to nav to a page belonging to a dynamic plugin which needs loading
|
|
112
|
-
await Promise.all([
|
|
113
|
-
...always,
|
|
114
|
-
]);
|
|
115
|
-
|
|
116
|
-
// If a plugin claims the route and is loaded correctly we'll get a route back
|
|
117
|
-
const newLocation = await dynamicPluginLoader.check({ route, store });
|
|
118
|
-
|
|
119
|
-
// If we have a new location, double check that it's actually valid
|
|
120
|
-
const resolvedRoute = newLocation ? store.app.router.resolve(newLocation) : null;
|
|
121
|
-
|
|
122
|
-
if (resolvedRoute?.route.matched.length) {
|
|
123
|
-
// Note - don't use `redirect` or `store.app.route` (breaks feature by failing to run middleware in default layout)
|
|
124
|
-
return next(newLocation);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
109
|
// Ensure that the activeNamespaceCache is updated given the change of context either from or to a place where it uses workspaces
|
|
129
110
|
// When fleet moves to it's own package this should be moved to pkg onEnter/onLeave
|
|
130
111
|
if ((oldProduct === FLEET_NAME || product === FLEET_NAME) && oldProduct !== product) {
|
|
@@ -24,8 +24,9 @@ describe('chartMixin', () => {
|
|
|
24
24
|
localVue.mixin(ChartMixin);
|
|
25
25
|
|
|
26
26
|
it.each(testCases.opa)(
|
|
27
|
-
'should add OPA deprecation warning properly', (chartId, expected) => {
|
|
27
|
+
'should add OPA deprecation warning properly', async(chartId, expected) => {
|
|
28
28
|
const store = new Vuex.Store({
|
|
29
|
+
actions: { 'catalog/load': () => {} },
|
|
29
30
|
getters: {
|
|
30
31
|
currentCluster: () => {},
|
|
31
32
|
isRancher: () => true,
|
|
@@ -44,6 +45,8 @@ describe('chartMixin', () => {
|
|
|
44
45
|
|
|
45
46
|
instance.$route = { query: { chart: 'chart_name' } };
|
|
46
47
|
|
|
48
|
+
await instance.fetchChart();
|
|
49
|
+
|
|
47
50
|
const warnings = instance.warnings;
|
|
48
51
|
|
|
49
52
|
expect(warnings).toHaveLength(expected);
|
package/mixins/auth-config.js
CHANGED
|
@@ -273,7 +273,7 @@ export default {
|
|
|
273
273
|
|
|
274
274
|
// KeyCloakOIDCConfig --> OIDCConfig
|
|
275
275
|
set(this.model, 'rancherUrl', `${ serverUrl }/verify-auth`);
|
|
276
|
-
set(this.model, 'scope', BASE_SCOPES.
|
|
276
|
+
set(this.model, 'scope', this.model.id === 'keycloakoidc' ? BASE_SCOPES.keycloakoidc[0] : BASE_SCOPES.genericoidc[0]);
|
|
277
277
|
break;
|
|
278
278
|
}
|
|
279
279
|
|
package/mixins/chart.js
CHANGED
|
@@ -27,6 +27,8 @@ export default {
|
|
|
27
27
|
existing: null,
|
|
28
28
|
|
|
29
29
|
ignoreWarning: false,
|
|
30
|
+
|
|
31
|
+
chart: null,
|
|
30
32
|
};
|
|
31
33
|
},
|
|
32
34
|
|
|
@@ -35,20 +37,6 @@ export default {
|
|
|
35
37
|
|
|
36
38
|
showPreRelease: mapPref(SHOW_PRE_RELEASE),
|
|
37
39
|
|
|
38
|
-
chart() {
|
|
39
|
-
if ( this.repo && this.query.chartName ) {
|
|
40
|
-
return this.$store.getters['catalog/chart']({
|
|
41
|
-
repoType: this.query.repoType,
|
|
42
|
-
repoName: this.query.repoName,
|
|
43
|
-
chartName: this.query.chartName,
|
|
44
|
-
includeHidden: true,
|
|
45
|
-
showDeprecated: this.showDeprecated
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
return null;
|
|
50
|
-
},
|
|
51
|
-
|
|
52
40
|
repo() {
|
|
53
41
|
return this.$store.getters['catalog/repo']({
|
|
54
42
|
repoType: this.query.repoType,
|
|
@@ -258,11 +246,39 @@ export default {
|
|
|
258
246
|
},
|
|
259
247
|
|
|
260
248
|
methods: {
|
|
249
|
+
/**
|
|
250
|
+
* Populate `this.chart`
|
|
251
|
+
*
|
|
252
|
+
* `chart` used to be a computed property pointing at getter catalog/chart
|
|
253
|
+
*
|
|
254
|
+
* this however stopped recalculating given changes to the store
|
|
255
|
+
*
|
|
256
|
+
* (the store would populate a charts collection, which the getter uses to find the chart,
|
|
257
|
+
* however this did not kick off the computed property, so this.charts was not populated)
|
|
258
|
+
*
|
|
259
|
+
* Now we find and cache the chart
|
|
260
|
+
*/
|
|
261
|
+
fetchStoreChart() {
|
|
262
|
+
if (!this.chart && this.repo && this.query.chartName) {
|
|
263
|
+
this.chart = this.$store.getters['catalog/chart']({
|
|
264
|
+
repoType: this.query.repoType,
|
|
265
|
+
repoName: this.query.repoName,
|
|
266
|
+
chartName: this.query.chartName,
|
|
267
|
+
includeHidden: true,
|
|
268
|
+
showDeprecated: this.showDeprecated
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return this.chart;
|
|
273
|
+
},
|
|
274
|
+
|
|
261
275
|
async fetchChart() {
|
|
262
276
|
this.versionInfoError = null;
|
|
263
277
|
|
|
264
278
|
await this.$store.dispatch('catalog/load'); // not the problem
|
|
265
279
|
|
|
280
|
+
this.fetchStoreChart();
|
|
281
|
+
|
|
266
282
|
if ( this.query.appNamespace && this.query.appName ) {
|
|
267
283
|
// First check the URL query for an app name and namespace.
|
|
268
284
|
// Use those values to check for a catalog app resource.
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import Deployment from '@shell/models/apps.deployment';
|
|
2
|
+
import { WORKLOAD_TYPES } from '@shell/config/types';
|
|
3
|
+
|
|
4
|
+
describe('class Deployment', () => {
|
|
5
|
+
describe('replicaSetId', () => {
|
|
6
|
+
it.each([{
|
|
7
|
+
relationships: [],
|
|
8
|
+
expected: undefined,
|
|
9
|
+
}, {
|
|
10
|
+
relationships: [{
|
|
11
|
+
rel: 'owner',
|
|
12
|
+
toType: WORKLOAD_TYPES.REPLICA_SET,
|
|
13
|
+
toId: 'rel-id'
|
|
14
|
+
}],
|
|
15
|
+
expected: 'rel-id',
|
|
16
|
+
}, {
|
|
17
|
+
relationships: [{
|
|
18
|
+
rel: 'owner',
|
|
19
|
+
toType: WORKLOAD_TYPES.REPLICA_SET,
|
|
20
|
+
toId: 'rel-id-1',
|
|
21
|
+
message: 'ReplicaSet is available. Replicas: 1'
|
|
22
|
+
}],
|
|
23
|
+
expected: 'rel-id-1',
|
|
24
|
+
}, {
|
|
25
|
+
relationships: [{
|
|
26
|
+
rel: 'owner',
|
|
27
|
+
toType: WORKLOAD_TYPES.REPLICA_SET,
|
|
28
|
+
toId: 'rel-id-1',
|
|
29
|
+
message: 'ReplicaSet is available. Replicas: 0'
|
|
30
|
+
}, {
|
|
31
|
+
rel: 'owner',
|
|
32
|
+
toType: WORKLOAD_TYPES.REPLICA_SET,
|
|
33
|
+
toId: 'rel-id-2',
|
|
34
|
+
message: 'ReplicaSet is available. Replicas: 1'
|
|
35
|
+
}],
|
|
36
|
+
expected: 'rel-id-2',
|
|
37
|
+
}, {
|
|
38
|
+
relationships: [{
|
|
39
|
+
rel: 'owner',
|
|
40
|
+
toType: WORKLOAD_TYPES.REPLICA_SET,
|
|
41
|
+
toId: 'rel-id-1',
|
|
42
|
+
message: 'Message without replicas count'
|
|
43
|
+
}, {
|
|
44
|
+
rel: 'owner',
|
|
45
|
+
toType: WORKLOAD_TYPES.REPLICA_SET,
|
|
46
|
+
toId: 'rel-id-2',
|
|
47
|
+
message: 'Another message without replicas count'
|
|
48
|
+
}],
|
|
49
|
+
expected: 'rel-id-1',
|
|
50
|
+
}, {
|
|
51
|
+
relationships: [{
|
|
52
|
+
rel: 'owner',
|
|
53
|
+
toType: WORKLOAD_TYPES.REPLICA_SET,
|
|
54
|
+
toId: 'rel-id-1',
|
|
55
|
+
message: 'ReplicaSet is available. Replicas: 0'
|
|
56
|
+
}, {
|
|
57
|
+
rel: 'owner',
|
|
58
|
+
toType: WORKLOAD_TYPES.REPLICA_SET,
|
|
59
|
+
toId: 'rel-id-2',
|
|
60
|
+
message: 'ReplicaSet is available. Replicas: 0'
|
|
61
|
+
}],
|
|
62
|
+
expected: 'rel-id-1',
|
|
63
|
+
}, {
|
|
64
|
+
relationships: [{
|
|
65
|
+
rel: 'owner',
|
|
66
|
+
toType: WORKLOAD_TYPES.REPLICA_SET,
|
|
67
|
+
toId: 'rel-id-1',
|
|
68
|
+
message: 'Message without replicas count'
|
|
69
|
+
}, {
|
|
70
|
+
rel: 'owner',
|
|
71
|
+
toType: WORKLOAD_TYPES.REPLICA_SET,
|
|
72
|
+
toId: 'rel-id-2',
|
|
73
|
+
message: 'ReplicaSet is available. Replicas: 0'
|
|
74
|
+
}],
|
|
75
|
+
expected: 'rel-id-1',
|
|
76
|
+
}])('replicaSetId', ({ relationships, expected }) => {
|
|
77
|
+
const deploymentData = {
|
|
78
|
+
id: 'any-id',
|
|
79
|
+
type: WORKLOAD_TYPES.DEPLOYMENT,
|
|
80
|
+
metadata: {
|
|
81
|
+
name: 'any-name',
|
|
82
|
+
namespace: 'any-namespace',
|
|
83
|
+
uid: 'any-uid',
|
|
84
|
+
relationships,
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const deployment = new Deployment(deploymentData);
|
|
89
|
+
|
|
90
|
+
expect(deployment.replicaSetId).toStrictEqual(expected);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
});
|
|
@@ -10,14 +10,28 @@ const IGNORED_ANNOTATIONS = [
|
|
|
10
10
|
'deprecated.deployment.rollback.to',
|
|
11
11
|
];
|
|
12
12
|
|
|
13
|
+
const replicasRegEx = /Replicas: (\d+)/;
|
|
14
|
+
|
|
13
15
|
export default class Deployment extends Workload {
|
|
14
16
|
get replicaSetId() {
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
const relationships = this.metadata?.relationships || [];
|
|
18
|
+
|
|
19
|
+
// Find all relevant ReplicaSet relationships
|
|
20
|
+
const replicaSetRelationships = relationships.filter((relationship) => relationship.rel === 'owner' && relationship.toType === WORKLOAD_TYPES.REPLICA_SET
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
// Filter the ReplicaSets based on replicas > 0
|
|
24
|
+
const activeReplicaSet = replicaSetRelationships.find((relationship) => {
|
|
25
|
+
const replicasMatch = relationship.message?.match(replicasRegEx);
|
|
26
|
+
const replicas = replicasMatch ? parseInt(replicasMatch[1], 10) : 0;
|
|
27
|
+
|
|
28
|
+
return replicas > 0;
|
|
18
29
|
});
|
|
19
30
|
|
|
20
|
-
|
|
31
|
+
// If no active ReplicaSet is found, fall back to the first one from the list
|
|
32
|
+
const selectedReplicaSet = activeReplicaSet || replicaSetRelationships[0];
|
|
33
|
+
|
|
34
|
+
return selectedReplicaSet?.toId?.replace(`${ this.namespace }/`, '');
|
|
21
35
|
}
|
|
22
36
|
|
|
23
37
|
async rollBack(cluster, deployment, revision) {
|
package/models/driver.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { DESCRIPTION } from '@shell/config/labels-annotations';
|
|
2
2
|
import NormanModel from '@shell/plugins/steve/norman-class';
|
|
3
3
|
import { KONTAINER_TO_DRIVER } from './management.cattle.io.kontainerdriver';
|
|
4
|
+
import capitalize from 'lodash/capitalize';
|
|
4
5
|
|
|
5
6
|
export default class Driver extends NormanModel {
|
|
6
7
|
get canViewYaml() {
|
|
@@ -20,12 +21,12 @@ export default class Driver extends NormanModel {
|
|
|
20
21
|
}
|
|
21
22
|
}
|
|
22
23
|
|
|
23
|
-
return KONTAINER_TO_DRIVER[this.id] || this.id;
|
|
24
|
+
return KONTAINER_TO_DRIVER[this.id] || this.name || this.id;
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
get nameDisplay() {
|
|
27
28
|
const path = `cluster.provider.${ this.driverName }`;
|
|
28
|
-
const label = this.driverName
|
|
29
|
+
const label = capitalize(this.driverName);
|
|
29
30
|
|
|
30
31
|
return this.$rootGetters['i18n/withFallback'](path, label);
|
|
31
32
|
}
|
|
@@ -8,19 +8,21 @@ export default class KontainerDriver extends Driver {
|
|
|
8
8
|
get _availableActions() {
|
|
9
9
|
const out = [
|
|
10
10
|
{
|
|
11
|
-
action:
|
|
12
|
-
label:
|
|
13
|
-
icon:
|
|
14
|
-
bulkable:
|
|
15
|
-
|
|
11
|
+
action: 'activate',
|
|
12
|
+
label: this.t('action.activate'),
|
|
13
|
+
icon: 'icon icon-play',
|
|
14
|
+
bulkable: true,
|
|
15
|
+
bulkAction: 'activateBulk',
|
|
16
|
+
enabled: !!this.links.update && !this.active
|
|
16
17
|
},
|
|
17
18
|
{
|
|
18
|
-
action:
|
|
19
|
-
label:
|
|
20
|
-
icon:
|
|
21
|
-
bulkable:
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
action: 'deactivate',
|
|
20
|
+
label: this.t('action.deactivate'),
|
|
21
|
+
icon: 'icon icon-pause',
|
|
22
|
+
bulkable: true,
|
|
23
|
+
bulkAction: 'deactivateBulk',
|
|
24
|
+
enabled: !!this.links.update && !!this.active,
|
|
25
|
+
weight: -1
|
|
24
26
|
},
|
|
25
27
|
{ divider: true },
|
|
26
28
|
{
|
|
@@ -52,9 +54,16 @@ export default class KontainerDriver extends Driver {
|
|
|
52
54
|
return out;
|
|
53
55
|
}
|
|
54
56
|
|
|
55
|
-
deactivate() {
|
|
57
|
+
deactivate(resources = [this]) {
|
|
58
|
+
this.$dispatch('promptModal', {
|
|
59
|
+
componentProps: { drivers: resources, driverType: 'kontainerDrivers' },
|
|
60
|
+
component: 'DeactivateDriverDialog'
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
deactivateBulk(resources) {
|
|
56
65
|
this.$dispatch('promptModal', {
|
|
57
|
-
componentProps: {
|
|
66
|
+
componentProps: { drivers: resources, driverType: 'kontainerDrivers' },
|
|
58
67
|
component: 'DeactivateDriverDialog'
|
|
59
68
|
});
|
|
60
69
|
}
|
|
@@ -65,4 +74,12 @@ export default class KontainerDriver extends Driver {
|
|
|
65
74
|
method: 'post',
|
|
66
75
|
}, { root: true });
|
|
67
76
|
}
|
|
77
|
+
|
|
78
|
+
async activateBulk(resources) {
|
|
79
|
+
await Promise.all(resources.map((resource) => this.$dispatch('rancher/request', {
|
|
80
|
+
url: `v3/kontainerDrivers/${ escape(resource.id) }?action=activate`,
|
|
81
|
+
method: 'post',
|
|
82
|
+
}, { root: true }
|
|
83
|
+
)));
|
|
84
|
+
}
|
|
68
85
|
}
|