@rancher/shell 3.0.9-rc.5 → 3.0.9-rc.6
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/images/providers/oci-open-containers.svg +22 -0
- package/assets/images/providers/traefik.png +0 -0
- package/assets/styles/themes/_dark.scss +2 -0
- package/assets/styles/themes/_light.scss +2 -0
- package/assets/styles/themes/_modern.scss +6 -0
- package/assets/translations/en-us.yaml +129 -25
- package/components/CruResource.vue +3 -1
- package/components/ExplorerProjectsNamespaces.vue +12 -12
- package/components/Resource/Detail/Card/StatusCard/__tests__/StatusCard.test.ts +109 -0
- package/components/Resource/Detail/Card/StatusCard/index.vue +21 -4
- package/components/Resource/Detail/Metadata/IdentifyingInformation/__tests__/identifying-fields.test.ts +19 -2
- package/components/Resource/Detail/Metadata/IdentifyingInformation/identifying-fields.ts +19 -11
- package/components/Resource/Detail/ResourcePopover/__tests__/index.test.ts +12 -0
- package/components/Resource/Detail/ResourcePopover/index.vue +2 -0
- package/components/Resource/Detail/ResourceRow.vue +2 -2
- package/components/ResourceList/index.vue +7 -4
- package/components/Window/ContainerLogs.vue +48 -37
- package/components/fleet/FleetClusterTargets/TargetsList.vue +2 -2
- package/components/fleet/FleetClusterTargets/index.vue +6 -1
- package/components/fleet/GitRepoAdvancedTab.vue +333 -0
- package/components/fleet/GitRepoMetadataTab.vue +43 -0
- package/components/fleet/GitRepoRepositoryTab.vue +101 -0
- package/components/fleet/GitRepoTargetTab.vue +77 -0
- package/components/fleet/HelmOpAdvancedTab.vue +247 -0
- package/components/fleet/HelmOpChartTab.vue +158 -0
- package/components/fleet/HelmOpMetadataTab.vue +46 -0
- package/components/fleet/HelmOpTargetTab.vue +84 -0
- package/components/fleet/HelmOpValuesTab.vue +147 -0
- package/components/fleet/__tests__/FleetClusterTargets.test.ts +119 -70
- package/components/form/NodeScheduling.vue +81 -7
- package/components/form/PodAffinity.vue +1 -36
- package/components/form/ResourceLabeledSelect.vue +8 -4
- package/components/form/ResourceQuota/Namespace.vue +30 -9
- package/components/form/ResourceQuota/NamespaceRow.vue +25 -7
- package/components/form/ResourceQuota/Project.vue +140 -82
- package/components/form/ResourceQuota/ResourceQuotaEntry.vue +145 -0
- package/components/form/ResourceQuota/__tests__/Namespace.test.ts +307 -0
- package/components/form/ResourceQuota/__tests__/NamespaceRow.test.ts +281 -0
- package/components/form/ResourceQuota/__tests__/Project.test.ts +274 -27
- package/components/form/ResourceQuota/__tests__/ResourceQuotaEntry.test.ts +215 -0
- package/components/form/SchedulingCustomization.vue +14 -6
- package/components/form/SelectOrCreateAuthSecret.vue +107 -18
- package/components/form/__tests__/NodeScheduling.test.ts +12 -9
- package/components/form/__tests__/PodAffinity.test.ts +21 -2
- package/components/form/__tests__/SchedulingCustomization.test.ts +240 -0
- package/components/formatter/ClusterLink.vue +8 -0
- package/components/formatter/SecretOrigin.vue +79 -0
- package/config/labels-annotations.js +7 -6
- package/config/pagination-table-headers.js +6 -4
- package/config/product/explorer.js +1 -11
- package/config/query-params.js +3 -0
- package/config/settings.ts +15 -2
- package/config/table-headers.js +21 -17
- package/config/types.js +23 -8
- package/detail/workload/index.vue +11 -16
- package/dialog/DeactivateDriverDialog.vue +1 -1
- package/dialog/Ipv6NetworkingDialog.vue +156 -0
- package/dialog/ScalePoolDownDialog.vue +2 -2
- package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +1 -1
- package/edit/__tests__/fleet.cattle.io.helmop.test.ts +1 -0
- package/edit/__tests__/management.cattle.io.project.test.js +56 -128
- package/edit/auth/oidc.vue +1 -1
- package/edit/catalog.cattle.io.clusterrepo.vue +155 -25
- package/edit/fleet.cattle.io.gitrepo.vue +153 -283
- package/edit/fleet.cattle.io.helmop.vue +190 -332
- package/edit/management.cattle.io.project.vue +5 -42
- package/edit/management.cattle.io.setting.vue +6 -0
- package/edit/provisioning.cattle.io.cluster/__tests__/Basics.test.ts +55 -24
- package/edit/provisioning.cattle.io.cluster/__tests__/Networking.test.ts +1 -103
- package/edit/provisioning.cattle.io.cluster/__tests__/index.test.ts +13 -1
- package/edit/provisioning.cattle.io.cluster/__tests__/rke2-fleet-cluster-agent.test.ts +283 -0
- package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +65 -49
- package/edit/provisioning.cattle.io.cluster/ingress/IngressCards.vue +112 -0
- package/edit/provisioning.cattle.io.cluster/ingress/IngressConfiguration.vue +158 -0
- package/edit/provisioning.cattle.io.cluster/rke2.vue +171 -72
- package/edit/provisioning.cattle.io.cluster/shared.ts +36 -1
- package/edit/provisioning.cattle.io.cluster/tabs/AgentConfiguration.vue +2 -1
- package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +55 -7
- package/edit/provisioning.cattle.io.cluster/tabs/Ingress.vue +319 -0
- package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +2 -1
- package/edit/provisioning.cattle.io.cluster/tabs/etcd/__tests__/S3Config.test.ts +13 -1
- package/edit/provisioning.cattle.io.cluster/tabs/networking/index.vue +10 -44
- package/edit/secret/index.vue +1 -1
- package/edit/token.vue +68 -29
- package/edit/workload/__tests__/index.test.ts +2 -37
- package/edit/workload/index.vue +6 -2
- package/edit/workload/mixins/workload.js +0 -32
- package/list/__tests__/management.cattle.io.setting.test.ts +198 -0
- package/list/management.cattle.io.setting.vue +13 -0
- package/list/provisioning.cattle.io.cluster.vue +50 -1
- package/list/secret.vue +4 -9
- package/list/service.vue +6 -8
- package/machine-config/amazonec2.vue +11 -4
- package/machine-config/components/EC2Networking.vue +46 -30
- package/machine-config/components/__tests__/EC2Networking.test.ts +7 -7
- package/machine-config/components/__tests__/utils/vpcSubnetMockData.js +0 -9
- package/machine-config/digitalocean.vue +3 -3
- package/models/__tests__/namespace.test.ts +11 -0
- package/models/__tests__/provisioning.cattle.io.cluster.test.ts +96 -0
- package/models/__tests__/workload.test.ts +42 -1
- package/models/catalog.cattle.io.clusterrepo.js +30 -4
- package/models/ext.cattle.io.token.js +48 -0
- package/models/kontainerdriver.js +2 -2
- package/models/namespace.js +7 -1
- package/models/nodedriver.js +2 -2
- package/models/provisioning.cattle.io.cluster.js +28 -7
- package/models/secret.js +0 -17
- package/models/service.js +44 -1
- package/models/token.js +4 -0
- package/models/workload.js +12 -6
- package/package.json +1 -1
- package/pages/account/index.vue +96 -67
- package/pages/auth/setup.vue +5 -14
- package/pages/c/_cluster/apps/charts/__tests__/install.test.ts +4 -1
- package/pages/c/_cluster/apps/charts/index.vue +93 -4
- package/pages/c/_cluster/apps/charts/install.vue +317 -42
- package/pages/c/_cluster/manager/drivers/kontainerDriver/index.vue +5 -4
- package/pages/c/_cluster/settings/index.vue +3 -1
- package/plugins/dashboard-store/__tests__/getters.test.ts +108 -0
- package/plugins/dashboard-store/__tests__/resource-class.test.ts +27 -0
- package/plugins/dashboard-store/actions.js +3 -8
- package/plugins/dashboard-store/getters.js +7 -5
- package/plugins/dashboard-store/mutations.js +4 -1
- package/plugins/dashboard-store/resource-class.js +3 -3
- package/plugins/steve/__tests__/steve-class.test.ts +102 -141
- package/plugins/steve/steve-class.js +12 -3
- package/plugins/steve/steve-pagination-utils.ts +6 -2
- package/rancher-components/RcIcon/types.ts +2 -0
- package/rancher-components/RcItemCard/RcItemCard.vue +64 -19
- package/store/prefs.js +3 -0
- package/types/aws-sdk.d.ts +121 -0
- package/types/resources/node.ts +15 -0
- package/types/shell/index.d.ts +536 -506
- package/types/store/pagination.types.ts +5 -5
- package/utils/__tests__/array.test.ts +1 -29
- package/utils/__tests__/cluster-agent-configuration.test.ts +203 -0
- package/utils/array.ts +0 -11
- package/utils/aws.ts +21 -0
- package/utils/cluster.js +22 -2
- package/utils/selector-typed.ts +1 -1
- package/components/__tests__/ProjectRow.test.ts +0 -206
- package/components/form/ResourceQuota/ProjectRow.vue +0 -277
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { _CREATE, _VIEW, _EDIT } from '@shell/config/query-params';
|
|
3
|
+
import { ref, computed, useTemplateRef } from 'vue';
|
|
4
|
+
import { useStore } from 'vuex';
|
|
5
|
+
import { useI18n } from '@shell/composables/useI18n';
|
|
6
|
+
import Checkbox from '@components/Form/Checkbox/Checkbox.vue';
|
|
7
|
+
import { Banner } from '@components/Banner';
|
|
8
|
+
import IngressCards from '@shell/edit/provisioning.cattle.io.cluster/ingress/IngressCards.vue';
|
|
9
|
+
import {
|
|
10
|
+
INGRESS_OPTIONS, INGRESS_DUAL, TRAEFIK, INGRESS_NGINX, INGRESS_NONE, INGRESS_MIGRATION_KB_LINK, INGRESS_CLASS_DEFAULT, INGRESS_CONTROLLER_CLASS_DEFAULT, INGRESS_CLASS_MIGRATION, INGRESS_CONTROLLER_CLASS_MIGRATION
|
|
11
|
+
} from '@shell/edit/provisioning.cattle.io.cluster/shared';
|
|
12
|
+
import IngressConfiguration from '@shell/edit/provisioning.cattle.io.cluster/ingress/IngressConfiguration.vue';
|
|
13
|
+
import YamlEditor, { EDITOR_MODES } from '@shell/components/YamlEditor';
|
|
14
|
+
import { set, get, mergeWithReplace } from '@shell/utils/object';
|
|
15
|
+
import { saferDump } from '@shell/utils/create-yaml';
|
|
16
|
+
import RichTranslation from '@shell/components/RichTranslation.vue';
|
|
17
|
+
|
|
18
|
+
interface Props {
|
|
19
|
+
mode?: string;
|
|
20
|
+
value: string | string[];
|
|
21
|
+
nginxSupported: boolean;
|
|
22
|
+
nginxChart: string;
|
|
23
|
+
traefikChart: string;
|
|
24
|
+
userChartValues: any;
|
|
25
|
+
versionInfo: any;
|
|
26
|
+
}
|
|
27
|
+
const {
|
|
28
|
+
mode = _CREATE,
|
|
29
|
+
value,
|
|
30
|
+
nginxChart,
|
|
31
|
+
traefikChart,
|
|
32
|
+
nginxSupported,
|
|
33
|
+
userChartValues,
|
|
34
|
+
versionInfo
|
|
35
|
+
} = defineProps<Props>();
|
|
36
|
+
|
|
37
|
+
const emit = defineEmits(['update:value', 'error', 'config-validation-changed', 'yaml-validation-changed', 'update-values']);
|
|
38
|
+
const store = useStore();
|
|
39
|
+
const { t } = useI18n(store);
|
|
40
|
+
const nginxYaml = useTemplateRef('nginx-yaml');
|
|
41
|
+
const traefikYaml = useTemplateRef('traefik-yaml');
|
|
42
|
+
|
|
43
|
+
const showAdvanced = ref<Boolean>(false);
|
|
44
|
+
const isView = computed(() => mode === _VIEW);
|
|
45
|
+
const isEdit = computed(() => mode === _EDIT);
|
|
46
|
+
const showTraefikBanner = ref<Boolean>(false);
|
|
47
|
+
|
|
48
|
+
const ingressSelection = computed(() => {
|
|
49
|
+
if (Array.isArray(value) ) {
|
|
50
|
+
return INGRESS_DUAL;
|
|
51
|
+
} else if (!value || value.length === 0) {
|
|
52
|
+
return INGRESS_NONE;
|
|
53
|
+
} else {
|
|
54
|
+
return value;
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
const ingressOptions = computed(() => {
|
|
58
|
+
return INGRESS_OPTIONS.filter((option) => !(option.id === INGRESS_DUAL && mode === _CREATE) &&
|
|
59
|
+
!((option.id === INGRESS_NGINX || option.id === INGRESS_DUAL) && !nginxSupported)
|
|
60
|
+
).map((option) => {
|
|
61
|
+
return {
|
|
62
|
+
...option,
|
|
63
|
+
selected: option.id === ingressSelection.value
|
|
64
|
+
};
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const ingressEnabled = computed({
|
|
69
|
+
get() {
|
|
70
|
+
return ingressSelection.value !== INGRESS_NONE;
|
|
71
|
+
},
|
|
72
|
+
set(val) {
|
|
73
|
+
if (!val) {
|
|
74
|
+
emit('update:value', INGRESS_NONE);
|
|
75
|
+
} else {
|
|
76
|
+
emit('update:value', TRAEFIK);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
function initYamlEditor(chart: string) {
|
|
82
|
+
const defaultChartValue = versionInfo[chart];
|
|
83
|
+
|
|
84
|
+
return mergeWithReplace(defaultChartValue?.values, userChartValues[chart]);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function setCompatibilityModeValues(val: boolean) {
|
|
88
|
+
set(traefikMerged.value, 'providers.kubernetesIngressNginx.enabled', val);
|
|
89
|
+
if (!val) {
|
|
90
|
+
set(traefikMerged.value, 'providers.kubernetesIngressNginx.ingressClass', INGRESS_CLASS_DEFAULT);
|
|
91
|
+
set(traefikMerged.value, 'providers.kubernetesIngressNginx.controllerClass', INGRESS_CONTROLLER_CLASS_DEFAULT);
|
|
92
|
+
} else {
|
|
93
|
+
set(traefikMerged.value, 'providers.kubernetesIngressNginx.ingressClass', INGRESS_CLASS_MIGRATION);
|
|
94
|
+
set(traefikMerged.value, 'providers.kubernetesIngressNginx.controllerClass', INGRESS_CONTROLLER_CLASS_MIGRATION);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function preconfigureTraefik() {
|
|
99
|
+
set(traefikMerged.value, 'ports.web.hostPort', 8000);
|
|
100
|
+
set(traefikMerged.value, 'ports.websecure.hostPort', 8443);
|
|
101
|
+
setCompatibilityModeValues(true);
|
|
102
|
+
emit('update-values', traefikChart, traefikMerged.value);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const traefikMerged = ref(initYamlEditor(traefikChart));
|
|
106
|
+
const nginxMerged = ref(initYamlEditor(nginxChart));
|
|
107
|
+
|
|
108
|
+
const nginxHttp = computed({
|
|
109
|
+
get() {
|
|
110
|
+
return get(nginxMerged.value, 'controller.hostPort.ports.http');
|
|
111
|
+
},
|
|
112
|
+
set(val: string) {
|
|
113
|
+
set(nginxMerged.value, 'controller.hostPort.ports.http', Number(val));
|
|
114
|
+
emit('update-values', nginxChart, nginxMerged.value);
|
|
115
|
+
updateYaml(nginxYaml.value, nginxMerged.value);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
const nginxHttps = computed({
|
|
119
|
+
get() {
|
|
120
|
+
return get(nginxMerged.value, 'controller.hostPort.ports.https');
|
|
121
|
+
},
|
|
122
|
+
set(val: string) {
|
|
123
|
+
set(nginxMerged.value, 'controller.hostPort.ports.https', Number(val));
|
|
124
|
+
emit('update-values', nginxChart, nginxMerged.value);
|
|
125
|
+
updateYaml(nginxYaml.value, nginxMerged.value);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
const traefikHttp = computed({
|
|
129
|
+
get() {
|
|
130
|
+
return get(traefikMerged.value, 'ports.web.hostPort');
|
|
131
|
+
},
|
|
132
|
+
set(val: string) {
|
|
133
|
+
set(traefikMerged.value, 'ports.web.hostPort', Number(val));
|
|
134
|
+
emit('update-values', traefikChart, traefikMerged.value);
|
|
135
|
+
updateYaml(traefikYaml.value, traefikMerged.value);
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
const traefikHttps = computed({
|
|
139
|
+
get() {
|
|
140
|
+
return get(traefikMerged.value, 'ports.websecure.hostPort');
|
|
141
|
+
},
|
|
142
|
+
set(val: string) {
|
|
143
|
+
set(traefikMerged.value, 'ports.websecure.hostPort', Number(val));
|
|
144
|
+
emit('update-values', traefikChart, traefikMerged.value);
|
|
145
|
+
updateYaml(traefikYaml.value, traefikMerged.value);
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
const compatibilityMode = computed({
|
|
150
|
+
get() {
|
|
151
|
+
return get(traefikMerged.value, 'providers.kubernetesIngressNginx.enabled');
|
|
152
|
+
},
|
|
153
|
+
set(val: boolean) {
|
|
154
|
+
setCompatibilityModeValues(val);
|
|
155
|
+
emit('update-values', traefikChart, traefikMerged.value);
|
|
156
|
+
updateYaml(traefikYaml.value, traefikMerged.value);
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
function selectIngress(id: string) {
|
|
161
|
+
if ( id === INGRESS_DUAL) {
|
|
162
|
+
emit('update:value', [TRAEFIK, INGRESS_NGINX]);
|
|
163
|
+
preconfigureTraefik();
|
|
164
|
+
} else {
|
|
165
|
+
emit('update:value', id);
|
|
166
|
+
if (id === TRAEFIK && !!isEdit.value) {
|
|
167
|
+
showTraefikBanner.value = true;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function updateYaml(component: any, value: any) {
|
|
173
|
+
if (component) {
|
|
174
|
+
component.updateValue(saferDump(value));
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
</script>
|
|
179
|
+
<template>
|
|
180
|
+
<h3 class="mb-10">
|
|
181
|
+
{{ t('cluster.ingress.title') }}
|
|
182
|
+
</h3>
|
|
183
|
+
<Checkbox
|
|
184
|
+
v-model:value="ingressEnabled"
|
|
185
|
+
:mode="mode"
|
|
186
|
+
:label="t('cluster.ingress.enableIngress')"
|
|
187
|
+
/>
|
|
188
|
+
<div v-if="!ingressEnabled">
|
|
189
|
+
<Banner
|
|
190
|
+
color="warning"
|
|
191
|
+
label-key="cluster.ingress.banners.disabled.label"
|
|
192
|
+
/>
|
|
193
|
+
</div>
|
|
194
|
+
<div v-else>
|
|
195
|
+
<Banner
|
|
196
|
+
color="info"
|
|
197
|
+
label-key="cluster.ingress.banners.transitioning.label"
|
|
198
|
+
/>
|
|
199
|
+
<IngressCards
|
|
200
|
+
:options="ingressOptions"
|
|
201
|
+
:mode="mode"
|
|
202
|
+
@select="selectIngress"
|
|
203
|
+
/>
|
|
204
|
+
<Banner
|
|
205
|
+
v-if="(isEdit && ingressSelection !== TRAEFIK) || showTraefikBanner"
|
|
206
|
+
color="warning"
|
|
207
|
+
>
|
|
208
|
+
<RichTranslation :k="`cluster.ingress.banners.selected.${ingressSelection}.label`">
|
|
209
|
+
<template #docsUrl="{ content }">
|
|
210
|
+
<a
|
|
211
|
+
:href="`${INGRESS_MIGRATION_KB_LINK}`"
|
|
212
|
+
tabindex="0"
|
|
213
|
+
target="_blank"
|
|
214
|
+
rel="noopener noreferrer nofollow"
|
|
215
|
+
>
|
|
216
|
+
{{ content }} <i class="icon icon-external-link" />
|
|
217
|
+
<span class="sr-only">{{ t('generic.opensInNewTab') }}</span>
|
|
218
|
+
</a>
|
|
219
|
+
</template>
|
|
220
|
+
</RichTranslation>
|
|
221
|
+
</Banner>
|
|
222
|
+
<div class="mt-20">
|
|
223
|
+
<IngressConfiguration
|
|
224
|
+
v-model:compatibility-mode="compatibilityMode"
|
|
225
|
+
v-model:nginxHttp="nginxHttp"
|
|
226
|
+
v-model:nginxHttps="nginxHttps"
|
|
227
|
+
v-model:traefikHttp="traefikHttp"
|
|
228
|
+
v-model:traefikHttps="traefikHttps"
|
|
229
|
+
:mode="mode"
|
|
230
|
+
:ingress-selection="ingressSelection"
|
|
231
|
+
@validation-changed="emit('config-validation-changed', $event)"
|
|
232
|
+
/>
|
|
233
|
+
</div>
|
|
234
|
+
<div>
|
|
235
|
+
<button
|
|
236
|
+
type="button"
|
|
237
|
+
class="btn role-link advanced-toggle mb-0"
|
|
238
|
+
@click="showAdvanced = !showAdvanced"
|
|
239
|
+
>
|
|
240
|
+
{{ showAdvanced ? t('cluster.ingress.hideAdvanced') : t('cluster.ingress.showAdvanced') }}
|
|
241
|
+
</button>
|
|
242
|
+
</div>
|
|
243
|
+
<template v-if="showAdvanced">
|
|
244
|
+
<div class="row">
|
|
245
|
+
<div
|
|
246
|
+
v-if="ingressSelection === TRAEFIK || ingressSelection === INGRESS_DUAL"
|
|
247
|
+
:class="{ 'col': true, 'span-6': ingressSelection === INGRESS_DUAL, 'span-12': ingressSelection !== INGRESS_DUAL }"
|
|
248
|
+
>
|
|
249
|
+
<p
|
|
250
|
+
v-if="ingressSelection === INGRESS_DUAL"
|
|
251
|
+
class="mb-10"
|
|
252
|
+
>
|
|
253
|
+
{{ t('cluster.ingress.traefik.header') }}
|
|
254
|
+
</p>
|
|
255
|
+
<YamlEditor
|
|
256
|
+
ref="traefik-yaml"
|
|
257
|
+
class="ingress-yaml-editor"
|
|
258
|
+
data-testid="traefik-yaml-editor"
|
|
259
|
+
:value="traefikMerged"
|
|
260
|
+
:mode="mode"
|
|
261
|
+
:scrolling="true"
|
|
262
|
+
:as-object="true"
|
|
263
|
+
:editor-mode="isView ? EDITOR_MODES.VIEW_CODE : EDITOR_MODES.EDIT_CODE"
|
|
264
|
+
:hide-preview-buttons="true"
|
|
265
|
+
@update:value="emit('update-values', traefikChart, $event)"
|
|
266
|
+
@validationChanged="emit('yaml-validation-changed', {name: traefikChart, val: $event})"
|
|
267
|
+
/>
|
|
268
|
+
</div>
|
|
269
|
+
<div
|
|
270
|
+
v-if="ingressSelection === INGRESS_NGINX || ingressSelection === INGRESS_DUAL"
|
|
271
|
+
:class="{ 'col': true, 'span-6': ingressSelection === INGRESS_DUAL, 'span-12': ingressSelection !== INGRESS_DUAL }"
|
|
272
|
+
>
|
|
273
|
+
<p
|
|
274
|
+
v-if="ingressSelection === INGRESS_DUAL"
|
|
275
|
+
class="mb-10"
|
|
276
|
+
>
|
|
277
|
+
{{ t('cluster.ingress.nginx.header') }}
|
|
278
|
+
</p>
|
|
279
|
+
<YamlEditor
|
|
280
|
+
ref="nginx-yaml"
|
|
281
|
+
class="ingress-yaml-editor"
|
|
282
|
+
data-testid="ingress-nginx-yaml-editor"
|
|
283
|
+
:value="nginxMerged"
|
|
284
|
+
:mode="mode"
|
|
285
|
+
:scrolling="true"
|
|
286
|
+
:as-object="true"
|
|
287
|
+
:editor-mode="isView ? EDITOR_MODES.VIEW_CODE : EDITOR_MODES.EDIT_CODE"
|
|
288
|
+
:hide-preview-buttons="true"
|
|
289
|
+
@update:value="emit('update-values', nginxChart, $event)"
|
|
290
|
+
@validationChanged="emit('yaml-validation-changed', {name: nginxChart, val: $event})"
|
|
291
|
+
/>
|
|
292
|
+
</div>
|
|
293
|
+
</div>
|
|
294
|
+
</template>
|
|
295
|
+
</div>
|
|
296
|
+
</template>
|
|
297
|
+
|
|
298
|
+
<style scoped lang="scss">
|
|
299
|
+
.advanced-toggle {
|
|
300
|
+
padding: 0;
|
|
301
|
+
gap: 0;
|
|
302
|
+
min-height: 20px;
|
|
303
|
+
|
|
304
|
+
&:focus-visible {
|
|
305
|
+
border-color: var(--primary);
|
|
306
|
+
@include focus-outline;
|
|
307
|
+
outline-offset: -2px;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
.ingress-yaml-editor {
|
|
312
|
+
:deep(.CodeMirror) {
|
|
313
|
+
height: auto !important;
|
|
314
|
+
}
|
|
315
|
+
:deep(.CodeMirror-scroll) {
|
|
316
|
+
max-height: 600px;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
</style>
|
|
@@ -354,7 +354,8 @@ export default {
|
|
|
354
354
|
:is="configComponent"
|
|
355
355
|
v-if="value.config && configComponent"
|
|
356
356
|
ref="configComponent"
|
|
357
|
-
v-model:
|
|
357
|
+
v-model:is-ipv6="value.isIpv6"
|
|
358
|
+
v-model:is-dualStack="value.isDualStack"
|
|
358
359
|
:cluster="cluster"
|
|
359
360
|
:uuid="uuid"
|
|
360
361
|
:mode="mode"
|
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
import { shallowMount } from '@vue/test-utils';
|
|
2
2
|
import S3Config from '@shell/edit/provisioning.cattle.io.cluster/tabs/etcd/S3Config.vue';
|
|
3
|
-
|
|
3
|
+
jest.mock('@shell/edit/provisioning.cattle.io.cluster/shared', () => ({
|
|
4
|
+
RETENTION_DEFAULT: 5,
|
|
5
|
+
RKE2_INGRESS_NGINX: 'rke2-ingress-nginx',
|
|
6
|
+
RKE2_TRAEFIK: 'rke2-traefik',
|
|
7
|
+
INGRESS_NGINX: 'ingress-nginx',
|
|
8
|
+
INGRESS_CONTROLLER: 'ingress-controller',
|
|
9
|
+
TRAEFIK: 'traefik',
|
|
10
|
+
HARVESTER: 'harvester',
|
|
11
|
+
INGRESS_DUAL: 'dual',
|
|
12
|
+
INGRESS_NONE: 'none',
|
|
13
|
+
INGRESS_OPTIONS: [],
|
|
14
|
+
INGRESS_MIGRATION_KB_LINK: 'mock-link'
|
|
15
|
+
}));
|
|
4
16
|
describe('s3Config', () => {
|
|
5
17
|
const defaultProps = {
|
|
6
18
|
mode: 'create',
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { LabeledInput } from '@components/Form/LabeledInput';
|
|
3
3
|
import { Banner } from '@components/Banner';
|
|
4
4
|
import { Checkbox } from '@components/Form/Checkbox';
|
|
5
|
-
import { _EDIT, _VIEW
|
|
5
|
+
import { _EDIT, _VIEW } from '@shell/config/query-params';
|
|
6
6
|
import ArrayList from '@shell/components/form/ArrayList';
|
|
7
7
|
import ACE from '@shell/edit/provisioning.cattle.io.cluster/tabs/networking/ACE';
|
|
8
8
|
import LabeledSelect from '@shell/components/form/LabeledSelect';
|
|
@@ -20,7 +20,7 @@ export default {
|
|
|
20
20
|
'update:value', 'cluster-cidr-changed', 'local-cluster-auth-endpoint-changed',
|
|
21
21
|
'service-cidr-changed', 'cluster-domain-changed', 'cluster-dns-changed',
|
|
22
22
|
'truncate-hostname-changed', 'ca-certs-changed', 'service-node-port-range-changed',
|
|
23
|
-
'fqdn-changed', 'tls-san-changed', 'stack-preference-changed', 'validationChanged', '
|
|
23
|
+
'fqdn-changed', 'tls-san-changed', 'stack-preference-changed', 'validationChanged', 'flannel-ipv6-masq-changed'
|
|
24
24
|
],
|
|
25
25
|
|
|
26
26
|
components: {
|
|
@@ -59,29 +59,12 @@ export default {
|
|
|
59
59
|
default: false
|
|
60
60
|
},
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
flannelIpv6Masq: {
|
|
63
63
|
type: Boolean,
|
|
64
64
|
default: false
|
|
65
65
|
}
|
|
66
66
|
},
|
|
67
67
|
|
|
68
|
-
watch: {
|
|
69
|
-
isProbablyIPv6(neu) {
|
|
70
|
-
if (this.mode === _CREATE && this.showFlannelMasq) {
|
|
71
|
-
this.$emit('enable-flannel-masq-changed', neu);
|
|
72
|
-
}
|
|
73
|
-
},
|
|
74
|
-
|
|
75
|
-
isK3s(neu) {
|
|
76
|
-
if (!neu) {
|
|
77
|
-
this.$emit('enable-flannel-masq-changed', null);
|
|
78
|
-
} else if (this.isProbablyIPv6) {
|
|
79
|
-
this.$emit('enable-flannel-masq-changed', true);
|
|
80
|
-
}
|
|
81
|
-
},
|
|
82
|
-
|
|
83
|
-
},
|
|
84
|
-
|
|
85
68
|
data() {
|
|
86
69
|
return { STACK_PREFS };
|
|
87
70
|
},
|
|
@@ -157,11 +140,11 @@ export default {
|
|
|
157
140
|
return this.isK3s && flannelEnabled;
|
|
158
141
|
},
|
|
159
142
|
|
|
160
|
-
|
|
143
|
+
isIpv6StackPref() {
|
|
161
144
|
return [STACK_PREFS.IPV6, STACK_PREFS.DUAL].includes(this.value?.spec?.rkeConfig?.networking?.stackPreference);
|
|
162
145
|
},
|
|
163
146
|
|
|
164
|
-
|
|
147
|
+
isIpv6ServerConfig() {
|
|
165
148
|
const clusterCIDR = this.serverConfig['cluster-cidr'] || '';
|
|
166
149
|
const serviceCIDR = this.serverConfig['service-cidr'] || '';
|
|
167
150
|
|
|
@@ -169,25 +152,7 @@ export default {
|
|
|
169
152
|
},
|
|
170
153
|
|
|
171
154
|
isProbablyIPv6() {
|
|
172
|
-
return this.hasSomeIpv6Pools || this.
|
|
173
|
-
},
|
|
174
|
-
},
|
|
175
|
-
|
|
176
|
-
methods: {
|
|
177
|
-
// if ipv6 pools are detected, we enforce dual-stack or ipv6 stack prefs.
|
|
178
|
-
// If ipv6 pools are not detected we don't know for sure they aren't there so we don't validate the input
|
|
179
|
-
// also not validating the input when editing existing clusters to ensure we don't prevent editing clusters using dual-stack VPCs provisioned before the ipv6 feature was added
|
|
180
|
-
stackPreferenceValidator(val) {
|
|
181
|
-
const value = val?.value || val;
|
|
182
|
-
let isValid;
|
|
183
|
-
|
|
184
|
-
if (this.hasSomeIpv6Pools && this.mode === _CREATE) {
|
|
185
|
-
isValid = value !== STACK_PREFS.IPV4;
|
|
186
|
-
|
|
187
|
-
return isValid ? null : this.t('cluster.rke2.stackPreference.errorNeedsIpv6');
|
|
188
|
-
} else {
|
|
189
|
-
return null;
|
|
190
|
-
}
|
|
155
|
+
return this.hasSomeIpv6Pools || this.isIpv6StackPref || this.isIpv6ServerConfig;
|
|
191
156
|
},
|
|
192
157
|
}
|
|
193
158
|
};
|
|
@@ -212,6 +177,7 @@ export default {
|
|
|
212
177
|
:mode="mode"
|
|
213
178
|
:disabled="isEdit"
|
|
214
179
|
:label="t('cluster.rke2.address.clusterCidr.label')"
|
|
180
|
+
data-testid="cluster-cidr"
|
|
215
181
|
@update:value="$emit('cluster-cidr-changed', $event)"
|
|
216
182
|
/>
|
|
217
183
|
</div>
|
|
@@ -224,6 +190,7 @@ export default {
|
|
|
224
190
|
:mode="mode"
|
|
225
191
|
:disabled="isEdit"
|
|
226
192
|
:label="t('cluster.rke2.address.serviceCidr.label')"
|
|
193
|
+
data-testid="service-cidr"
|
|
227
194
|
@update:value="$emit('service-cidr-changed', $event)"
|
|
228
195
|
/>
|
|
229
196
|
</div>
|
|
@@ -331,7 +298,6 @@ export default {
|
|
|
331
298
|
:value="localValue?.spec?.rkeConfig?.networking?.stackPreference || STACK_PREFS.IPV4"
|
|
332
299
|
:mode="mode"
|
|
333
300
|
:options="stackPreferenceOptions"
|
|
334
|
-
:rules="[stackPreferenceValidator]"
|
|
335
301
|
data-testid="network-tab-stackpreferences"
|
|
336
302
|
:require-dirty="false"
|
|
337
303
|
@selecting="e=>$emit('stack-preference-changed', e)"
|
|
@@ -357,11 +323,11 @@ export default {
|
|
|
357
323
|
class="col"
|
|
358
324
|
>
|
|
359
325
|
<Checkbox
|
|
360
|
-
:value="
|
|
326
|
+
:value="flannelIpv6Masq"
|
|
361
327
|
data-testid="cluster-rke2-flannel-masq-checkbox"
|
|
362
328
|
:mode="mode"
|
|
363
329
|
:label="t('cluster.k3s.flannelMasq.label')"
|
|
364
|
-
@update:value="e=>$emit('
|
|
330
|
+
@update:value="e=>$emit('flannel-ipv6-masq-changed', e)"
|
|
365
331
|
/>
|
|
366
332
|
</div>
|
|
367
333
|
</div>
|
package/edit/secret/index.vue
CHANGED
|
@@ -72,7 +72,7 @@ export default {
|
|
|
72
72
|
this.value.metadata.namespace = this.filteredProjects[0].status.backingNamespace;
|
|
73
73
|
this.value.metadata.labels[UI_PROJECT_SECRET] = this.filteredProjects[0].metadata.name;
|
|
74
74
|
} else {
|
|
75
|
-
this.projectName = this.filteredProjects.find((p) => p.metadata.name === projectScopedLabel)
|
|
75
|
+
this.projectName = this.filteredProjects.find((p) => p.metadata.name === projectScopedLabel)?.metadata.name;
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
78
|
},
|
package/edit/token.vue
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { mapGetters } from 'vuex';
|
|
3
3
|
import day from 'dayjs';
|
|
4
4
|
import sortBy from 'lodash/sortBy';
|
|
5
|
-
import { MANAGEMENT,
|
|
5
|
+
import { MANAGEMENT, EXT } from '@shell/config/types';
|
|
6
6
|
import { Banner } from '@components/Banner';
|
|
7
7
|
import DetailText from '@shell/components/DetailText';
|
|
8
8
|
import Footer from '@shell/components/form/Footer';
|
|
@@ -14,6 +14,7 @@ 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
16
|
import { SETTING } from '@shell/config/settings';
|
|
17
|
+
import Checkbox from '@components/Form/Checkbox/Checkbox.vue';
|
|
17
18
|
|
|
18
19
|
export default {
|
|
19
20
|
components: {
|
|
@@ -24,6 +25,7 @@ export default {
|
|
|
24
25
|
LabeledSelect,
|
|
25
26
|
RadioGroup,
|
|
26
27
|
Select,
|
|
28
|
+
Checkbox,
|
|
27
29
|
},
|
|
28
30
|
|
|
29
31
|
mixins: [CreateEditView],
|
|
@@ -41,7 +43,11 @@ export default {
|
|
|
41
43
|
|
|
42
44
|
return {
|
|
43
45
|
errors: null,
|
|
46
|
+
user: null,
|
|
44
47
|
form: {
|
|
48
|
+
enabled: true,
|
|
49
|
+
description: '',
|
|
50
|
+
clusterName: '',
|
|
45
51
|
expiryType: 'never',
|
|
46
52
|
customExpiry: 0,
|
|
47
53
|
customExpiryUnits: 'minute',
|
|
@@ -51,11 +57,13 @@ export default {
|
|
|
51
57
|
accessKey: '',
|
|
52
58
|
secretKey: '',
|
|
53
59
|
maxTTL,
|
|
60
|
+
ttl: ''
|
|
54
61
|
};
|
|
55
62
|
},
|
|
56
63
|
|
|
57
64
|
computed: {
|
|
58
65
|
...mapGetters({ t: 'i18n/t' }),
|
|
66
|
+
|
|
59
67
|
scopes() {
|
|
60
68
|
const all = this.$store.getters['management/all'](MANAGEMENT.CLUSTER);
|
|
61
69
|
const kubeClusters = filterHiddenLocalCluster(filterOnlyKubernetesClusters(all, this.$store), this.$store);
|
|
@@ -71,16 +79,20 @@ export default {
|
|
|
71
79
|
const options = ['never', 'day', 'month', 'year', 'custom'];
|
|
72
80
|
let opts = options.map((opt) => ({ value: opt, label: this.t(`accountAndKeys.apiKeys.add.expiry.options.${ opt }`) }));
|
|
73
81
|
|
|
74
|
-
// When the TTL is
|
|
82
|
+
// When the TTL is greater than 0, present only two options
|
|
75
83
|
// (1) The maximum allowed
|
|
76
84
|
// (2) Custom
|
|
77
|
-
if (this.maxTTL
|
|
85
|
+
if (this.maxTTL > 0 ) {
|
|
78
86
|
const now = day();
|
|
79
87
|
const expiry = now.add(this.maxTTL, 'minute');
|
|
80
88
|
const max = diffFrom(expiry, now, this.t);
|
|
81
89
|
|
|
82
90
|
opts = opts.filter((opt) => opt.value === 'custom');
|
|
83
91
|
opts.unshift({ value: 'max', label: this.t('accountAndKeys.apiKeys.add.expiry.options.maximum', { value: max.string }) });
|
|
92
|
+
} else {
|
|
93
|
+
// maxTTL <= 0 means there is no maximum, so we can show the 'never' option which results in an infinite TTL
|
|
94
|
+
// OR if we set a positive TTL, then it assumes that value
|
|
95
|
+
opts = opts.filter((opt) => opt.value === 'never' || opt.value === 'custom');
|
|
84
96
|
}
|
|
85
97
|
|
|
86
98
|
return opts;
|
|
@@ -91,6 +103,9 @@ export default {
|
|
|
91
103
|
|
|
92
104
|
return filtered.map((opt) => ({ value: opt, label: this.t(`accountAndKeys.apiKeys.add.customExpiry.options.${ opt }`) }));
|
|
93
105
|
},
|
|
106
|
+
hasNeverOption() {
|
|
107
|
+
return this.expiryOptions?.filter((opt) => opt.value === 'never')?.length === 1;
|
|
108
|
+
}
|
|
94
109
|
},
|
|
95
110
|
|
|
96
111
|
mounted() {
|
|
@@ -115,31 +130,33 @@ export default {
|
|
|
115
130
|
});
|
|
116
131
|
},
|
|
117
132
|
|
|
118
|
-
async actuallySave(
|
|
133
|
+
async actuallySave() {
|
|
134
|
+
// update expiration value before save
|
|
119
135
|
this.updateExpiry();
|
|
120
|
-
if ( this.isCreate ) {
|
|
121
|
-
// Description is a bit weird, so need to clone and set this
|
|
122
|
-
// rather than use this.value - need to find a way to set this if we ever
|
|
123
|
-
// want to allow edit (which I don't think we do)
|
|
124
|
-
const res = await this.value.save();
|
|
125
136
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
137
|
+
if ( this.isCreate ) {
|
|
138
|
+
const steveToken = await this.$store.dispatch('management/create', {
|
|
139
|
+
type: EXT.TOKEN,
|
|
140
|
+
spec: {
|
|
141
|
+
description: this.form.description,
|
|
142
|
+
kind: '',
|
|
143
|
+
userPrincipal: null, // will be set by the backend to the current user
|
|
144
|
+
clusterName: this.form.clusterName,
|
|
145
|
+
enabled: this.form.enabled,
|
|
146
|
+
ttl: this.ttl
|
|
147
|
+
// userID: not needed as it will be set by the backend to the current user
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
const steveTokenSaved = await steveToken.save();
|
|
152
|
+
|
|
153
|
+
this.created = steveTokenSaved;
|
|
154
|
+
this.ttlLimited = this.created?.spec?.ttl !== this.ttl;
|
|
155
|
+
const token = this.created?.status?.bearerToken?.split(':');
|
|
129
156
|
|
|
130
157
|
this.accessKey = token[0];
|
|
131
158
|
this.secretKey = (token.length > 1) ? token[1] : '';
|
|
132
|
-
this.token = this.created
|
|
133
|
-
|
|
134
|
-
// Force a refresh of the token so we get the expiry date correctly
|
|
135
|
-
await this.$store.dispatch('rancher/find', {
|
|
136
|
-
type: NORMAN.TOKEN,
|
|
137
|
-
id: res.id,
|
|
138
|
-
opt: { force: true }
|
|
139
|
-
}, { root: true });
|
|
140
|
-
} else {
|
|
141
|
-
// Note: update of existing key not supported currently
|
|
142
|
-
await this.value.save();
|
|
159
|
+
this.token = this.created?.status?.bearerToken;
|
|
143
160
|
}
|
|
144
161
|
},
|
|
145
162
|
|
|
@@ -159,7 +176,9 @@ export default {
|
|
|
159
176
|
const units = (v === 'custom') ? this.form.customExpiryUnits : v;
|
|
160
177
|
let ttl = 0;
|
|
161
178
|
|
|
162
|
-
if (
|
|
179
|
+
if (v === 'never') {
|
|
180
|
+
ttl = -1;
|
|
181
|
+
} else if (units === 'max') {
|
|
163
182
|
ttl = this.maxTTL * 60 * 1000;
|
|
164
183
|
} else if ( units !== 'never' ) {
|
|
165
184
|
const now = day();
|
|
@@ -167,7 +186,8 @@ export default {
|
|
|
167
186
|
|
|
168
187
|
ttl = expiry.diff(now);
|
|
169
188
|
}
|
|
170
|
-
|
|
189
|
+
|
|
190
|
+
this.ttl = ttl;
|
|
171
191
|
}
|
|
172
192
|
}
|
|
173
193
|
};
|
|
@@ -178,7 +198,7 @@ export default {
|
|
|
178
198
|
<div class="pl-10 pr-10">
|
|
179
199
|
<LabeledInput
|
|
180
200
|
key="description"
|
|
181
|
-
v-model:value="
|
|
201
|
+
v-model:value="form.description"
|
|
182
202
|
:placeholder="t('accountAndKeys.apiKeys.add.description.placeholder')"
|
|
183
203
|
label-key="accountAndKeys.apiKeys.add.description.label"
|
|
184
204
|
mode="edit"
|
|
@@ -186,13 +206,30 @@ export default {
|
|
|
186
206
|
/>
|
|
187
207
|
|
|
188
208
|
<LabeledSelect
|
|
189
|
-
v-model:value="
|
|
209
|
+
v-model:value="form.clusterName"
|
|
190
210
|
class="mt-20 scope-select"
|
|
191
211
|
label-key="accountAndKeys.apiKeys.add.scope"
|
|
192
212
|
:options="scopes"
|
|
193
213
|
/>
|
|
194
214
|
|
|
195
|
-
<
|
|
215
|
+
<Checkbox
|
|
216
|
+
v-model:value="form.enabled"
|
|
217
|
+
class="mt-20 mb-20"
|
|
218
|
+
:mode="mode"
|
|
219
|
+
label-key="accountAndKeys.apiKeys.add.enabled"
|
|
220
|
+
/>
|
|
221
|
+
|
|
222
|
+
<Banner
|
|
223
|
+
v-if="hasNeverOption"
|
|
224
|
+
color="warning"
|
|
225
|
+
class="mt-20"
|
|
226
|
+
>
|
|
227
|
+
<div>
|
|
228
|
+
{{ t('accountAndKeys.apiKeys.info.expiryOptionsWithNever') }}
|
|
229
|
+
</div>
|
|
230
|
+
</Banner>
|
|
231
|
+
|
|
232
|
+
<h5 class="mb-20">
|
|
196
233
|
{{ t('accountAndKeys.apiKeys.add.expiry.label') }}
|
|
197
234
|
</h5>
|
|
198
235
|
|
|
@@ -204,7 +241,9 @@ export default {
|
|
|
204
241
|
class="mr-20"
|
|
205
242
|
name="expiryGroup"
|
|
206
243
|
/>
|
|
207
|
-
<div
|
|
244
|
+
<div
|
|
245
|
+
class="ml-20 mt-10 expiry"
|
|
246
|
+
>
|
|
208
247
|
<input
|
|
209
248
|
v-model="form.customExpiry"
|
|
210
249
|
:disabled="form.expiryType !== 'custom'"
|