Merge pull request #10522 from scures/9810/incorrect-abbreviation

9810/incorrect-cluster-name-abbreviation
This commit is contained in:
Sorin 2024-03-12 14:44:30 +01:00 committed by GitHub
commit a54edec8b6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
24 changed files with 530 additions and 316 deletions

View File

@ -30,7 +30,7 @@ export default class CustomBadgeDialogPo extends ComponentPo {
}
badgeCustomDescription(): LabeledInputPo {
return LabeledInputPo.byLabel(this.self(), 'Custom description');
return LabeledInputPo.byLabel(this.self(), 'Custom comment');
}
colorPicker(): ColorInputPo {

View File

@ -45,13 +45,10 @@ describe('Cluster Dashboard', { testIsolation: 'off', tags: ['@explorer', '@admi
it('can add cluster badge', () => {
const settings = {
description: {
original: 'Example Text',
original: '',
new: 'E2E Test'
},
iconText: {
original: 'EX',
new: 'E2'
},
iconText: 'E2E',
backgroundColor: {
original: '#ff0000',
new: '#f80dd8',
@ -69,7 +66,7 @@ describe('Cluster Dashboard', { testIsolation: 'off', tags: ['@explorer', '@admi
customClusterCard.getTitle().contains('Custom Cluster Badge');
// update badge
clusterDashboard.customBadge().selectCheckbox('Show badge for this cluster').set();
clusterDashboard.customBadge().selectCheckbox('Show cluster comment').set();
clusterDashboard.customBadge().badgeCustomDescription().set(settings.description.new);
// update color
@ -80,10 +77,8 @@ describe('Cluster Dashboard', { testIsolation: 'off', tags: ['@explorer', '@admi
// update icon
clusterDashboard.customBadge().clusterIcon().children().should('have.class', 'cluster-local-logo');
clusterDashboard.customBadge().selectCheckbox('Customize cluster icon').set();
clusterDashboard.customBadge().clusterIcon().children().should('not.have.class', 'cluster-local-logo');
clusterDashboard.customBadge().clusterIcon().contains(settings.iconText.original);
clusterDashboard.customBadge().iconText().set(settings.iconText.new);
clusterDashboard.customBadge().clusterIcon().contains(settings.iconText.new);
clusterDashboard.customBadge().iconText().set(settings.iconText);
clusterDashboard.customBadge().clusterIcon().contains(settings.iconText);
// Apply Changes
clusterDashboard.customBadge().applyAndWait('/v3/clusters/local');
@ -94,12 +89,12 @@ describe('Cluster Dashboard', { testIsolation: 'off', tags: ['@explorer', '@admi
header.customBadge().should('contain', settings.description.new);
const burgerMenu = new BurgerMenuPo();
burgerMenu.clusters().first().find('span').should('contain', settings.iconText.new);
burgerMenu.clusters().first().find('span').should('contain', settings.iconText);
// Reset
clusterDashboard.addCustomBadge('Edit Cluster Badge').click();
clusterDashboard.customBadge().selectCheckbox('Customize cluster icon').set();
clusterDashboard.customBadge().selectCheckbox('Show badge for this cluster').set();
clusterDashboard.customBadge().selectCheckbox('Show cluster comment').set();
// Apply Changes
clusterDashboard.customBadge().applyAndWait('/v3/clusters/local');

View File

@ -98,7 +98,7 @@
"marked": "4.0.17",
"papaparse": "5.3.0",
"portal-vue": "2.1.7",
"rancher-icons": "rancher/icons#v2.0.19",
"rancher-icons": "rancher/icons#v2.0.21",
"sass": "1.51.0",
"sass-loader": "10.2.1",
"set-cookie-parser": "2.4.6",

View File

@ -2387,14 +2387,14 @@ clusterBadge:
editLabel: Edit Cluster Badge
modal:
title: Custom Cluster Badge
checkbox: Show badge for this cluster
description: Custom description
checkbox: Show cluster comment
comment: Custom comment
iconText: Icon Text
buttonAction: Apply
badgeBgColor: Badge background color
badgeTextColor: Badge text color
badgeAsIcon: Customize cluster icon
maxCharsTooltip: Maximum two characters
maxCharsTooltip: Maximum three characters
previewTitle: "Cluster icon and name presentation:"
grafanaDashboard:

View File

@ -9,7 +9,7 @@ export default {
computed: {
hasBadge() {
return !!this.cluster?.badge;
return !!this.cluster?.badge?.text;
}
}
};

View File

@ -107,7 +107,7 @@ export default {
position: relative;
align-items: center;
display: flex;
height: 28px;
height: 32px;
justify-content: center;
width: 42px;
}
@ -126,7 +126,7 @@ export default {
.cluster-badge-logo {
width: 42px;
height: 28px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;

View File

@ -98,6 +98,7 @@ export default {
.cluster-badge-logo {
width: 32px;
height: 32px;
padding: 0px 5px;
display: flex;
align-items: center;
justify-content: center;

View File

@ -24,8 +24,9 @@ describe('component: RoleDetailEdit', () => {
$fetchState: { pending: false },
$route: { name: 'anything' },
$store: {
getters: {
currentStore: () => 'store', 'i18n/t': jest.fn(), 'store/schemaFor': jest.fn()
dispatch: jest.fn(),
getters: {
currentStore: () => 'store', 'i18n/t': jest.fn(), 'store/schemaFor': jest.fn(), 'store/customisation/': jest.fn()
}
}
},

View File

@ -9,12 +9,14 @@ import { _VIEW, _EDIT, _CREATE } from '@shell/config/query-params';
import { LabeledInput } from '@components/Form/LabeledInput';
import LabeledSelect from '@shell/components/form/LabeledSelect';
import { normalizeName } from '@shell/utils/kube';
import ClusterIconMenu from '@shell/components/ClusterIconMenu';
export default {
name: 'NameNsDescription',
components: {
LabeledInput,
LabeledSelect
LabeledSelect,
ClusterIconMenu
},
props: {
@ -220,6 +222,12 @@ export default {
!!this.forceNamespace || this.namespaceDisabled || this.mode === _EDIT
); // namespace is never editable
},
clusterPreview() {
return this.$store.getters['customisation/getPreviewCluster'] || {
label: this.name,
badge: { iconText: null }
};
},
nameReallyDisabled() {
return this.nameDisabled || (this.mode === _EDIT && !this.nameEditable);
@ -289,6 +297,10 @@ export default {
return this.mode === _CREATE;
},
showCustomize() {
return this.mode === _CREATE && this.name && this.name.length > 0;
},
colSpan() {
if (!this.horizontal) {
return `span-8`;
@ -310,7 +322,14 @@ export default {
watch: {
name(val) {
if ( this.normalizeName ) {
// Reset the badge preview when the name changes
if (!this.clusterPreview) {
return;
}
this.$set(this.clusterPreview?.badge, 'iconText', null);
this.$set(this.clusterPreview, 'label', val);
if (this.normalizeName) {
val = normalizeName(val);
}
@ -345,6 +364,10 @@ export default {
});
},
created() {
this.$store.dispatch('customisation/setDefaultPreviewCluster');
},
methods: {
updateNamespace(val) {
if (this.forceNamespace) {
@ -370,8 +393,8 @@ export default {
cancelCreateNamespace(e) {
this.createNamespace = false;
this.$parent.$emit('createNamespace', false);
// In practise we should always have a defaultNamespace... unless we're in non-kube extension world, so fall back on options
this.namespace = this.$store.getters['defaultNamespace'] || this.options.find((o) => !!o.value)?.value ;
// In practice we should always have a defaultNamespace... unless we're in non-kube extension world, so fall back on options
this.namespace = this.$store.getters['defaultNamespace'] || this.options.find((o) => !!o.value)?.value;
},
selectNamespace(e) {
@ -385,7 +408,18 @@ export default {
this.$parent.$emit('createNamespace', false);
this.$emit('isNamespaceNew', false);
}
}
},
customBadgeDialog() {
this.$store.dispatch('cluster/promptModal', {
component: 'AddCustomBadgeDialog',
componentProps: {
isCreate: true,
clusterName: this.name,
clusterPreview: this.clusterPreview
},
});
},
},
};
</script>
@ -459,6 +493,24 @@ export default {
/>
</div>
<!-- // TODO: here goes the custom component -->
<div class="cluster-appearance">
<label for="name">Cluster Appearance</label>
<div class="cluster-appearance-preview">
<span>
<ClusterIconMenu :cluster="clusterPreview" />
</span>
<button
:disabled="!showCustomize"
@click="customBadgeDialog"
>
<i class="icon icon-brush-icon" />
<span>Customize</span>
</button>
</div>
</div>
<div
v-show="!descriptionHidden"
:data-testid="componentTestid + '-description'"
@ -505,13 +557,16 @@ button {
padding-top: 7px;
}
}
.row {
&.name-ns-description {
max-height: $input-height;
}
.namespace-select ::v-deep {
.labeled-select {
min-width: 40%;
.v-select.inline {
&.vs--single {
padding-bottom: 2px;
@ -527,9 +582,48 @@ button {
max-height: initial;
}
& > div > * {
&>div>* {
margin-bottom: 20px;
}
}
.cluster-appearance {
display: flex;
flex-direction: column;
margin: 0px 35px 0px 0px;
&-preview {
display: flex;
justify-content: center;
align-self: start;
gap: 10px;
justify-content: space-between;
span {
display: flex;
align-self: center;
height: auto;
}
button {
display: flex;
align-self: center;
height: auto;
margin: 0;
padding: 0;
top: 0;
color: var(--link);
i {
margin-right: 2px;
}
&:disabled {
color: var(--disabled-text);
cursor: not-allowed;
}
}
}
}
}
</style>

View File

@ -5,26 +5,30 @@ describe('component: NameNsDescription', () => {
// Accessing to computed value due code complexity
it('should map namespaces to options', () => {
const namespaceName = 'test';
const result = [{
label: namespaceName,
value: namespaceName
}];
const result = [
{
label: namespaceName,
value: namespaceName,
},
];
const wrapper = mount(NameNsDescription, {
propsData: {
value: {},
mode: 'create',
value: {},
mode: 'create',
cluster: {},
},
mocks: {
$store: {
getters: {
dispatch: jest.fn(),
getters: {
namespaces: jest.fn(),
allowedNamespaces: () => ({ [namespaceName]: true }),
currentStore: () => 'cluster',
'cluster/schemaFor': jest.fn(),
'i18n/t': jest.fn()
'i18n/t': jest.fn(),
},
},
}
},
});
expect((wrapper.vm as any).options).toStrictEqual(result);
@ -40,16 +44,22 @@ describe('component: NameNsDescription', () => {
},
mocks: {
$store: {
getters: {
namespaces: jest.fn(),
allowedNamespaces: () => ({ [namespaceName]: true }),
dispatch: jest.fn(),
getters: {
namespaces: jest.fn(),
allowedNamespaces: () => ({ [namespaceName]: true }),
'customizations/getPreviewCluster': {
ready: true,
isLocal: false,
badge: {},
},
currentStore: () => 'cluster',
'cluster/schemaFor': jest.fn(),
'i18n/t': jest.fn()
'i18n/t': jest.fn(),
},
},
$refs: { name: { focus: jest.fn() } }
}
$refs: { name: { focus: jest.fn() } },
},
});
(wrapper.vm as any).updateNamespace(newNamespaceName);

View File

@ -152,7 +152,7 @@ export const CLUSTER_BADGE = {
TEXT: 'ui.rancher/badge-text',
// Badge color - as a hex color - e.g. #ff00ff
COLOR: 'ui.rancher/badge-color',
// Custom icon text - max 2 characters
// Custom icon text - max 3 characters
ICON_TEXT: 'ui.rancher/badge-icon-text',
};

View File

@ -37,6 +37,7 @@ let store = {};
resolveStoreModules(require('../store/type-map.js'), 'type-map.js');
resolveStoreModules(require('../store/uiplugins.ts'), 'uiplugins.ts');
resolveStoreModules(require('../store/wm.js'), 'wm.js');
resolveStoreModules(require('../store/customisation.js'), 'customisation.js');
// If the environment supports hot reloading...
@ -62,6 +63,7 @@ let store = {};
'../store/type-map.js',
'../store/uiplugins.ts',
'../store/wm.js',
'../store/customisation.js'
], () => {
// Update `root.modules` with the latest definitions.
updateModules();

View File

@ -12,6 +12,7 @@ import { LabeledInput } from '@components/Form/LabeledInput';
import ColorInput from '@shell/components/form/ColorInput';
import { parseColor, textColor } from '@shell/utils/color';
import { NORMAN } from '@shell/config/types';
import { abbreviateClusterName } from '@shell/utils/cluster';
export default {
name: 'AddCustomBadgeDialog',
@ -25,57 +26,92 @@ export default {
ClusterBadge,
ClusterProviderIcon,
},
props: {
isCreate: { type: Boolean, default: false },
clusterName: { type: String, default: '' }
},
data() {
return {
useCustomBadge: null,
useCustomComment: null,
errors: [],
badgeBgColor: '',
badgeDescription: '',
badgeComment: '',
badgeAsIcon: null,
letter: '',
cluster: {},
};
},
mounted() {
// Generates a fake cluster object for use with badge component on cluster provisioning.
if (this.isCreate) {
this.cluster = this.getPreviewCluster;
this.badgeAsIcon = true;
this.letter = this.cluster?.badge?.iconText || abbreviateClusterName(this.clusterName);
this.useCustomComment = false;
this.badgeBgColor = this.cluster?.badge?.color || '#f1f1f1';
this.badgeComment = this.cluster?.badge?.text || null;
}
},
fetch() {
if (this.isCreate) {
return;
}
if (this.currentCluster.metadata?.annotations) {
this.badgeDescription = this.currentCluster.metadata?.annotations[CLUSTER_BADGE.TEXT];
this.useCustomBadge = this.badgeDescription?.length > 0;
this.badgeDescription = this.badgeDescription || 'Example Text';
this.badgeComment = this.currentCluster.metadata?.annotations[CLUSTER_BADGE.TEXT];
this.useCustomComment = this.badgeComment?.length > 0;
this.badgeBgColor = this.currentCluster.metadata?.annotations[CLUSTER_BADGE.COLOR] || '#ff0000';
this.badgeAsIcon = !!this.currentCluster.metadata?.annotations[CLUSTER_BADGE.ICON_TEXT] || false;
this.letter = this.currentCluster.metadata?.annotations[CLUSTER_BADGE.ICON_TEXT] || this.badgeDescription.substring(0, 2);
// TODO: Hardcode the annotations for creating cluster
this.letter = this.currentCluster.metadata?.annotations[CLUSTER_BADGE.ICON_TEXT] || abbreviateClusterName(this.currentCluster.nameDisplay);
}
},
computed: {
...mapGetters(['currentCluster']),
previewColor() {
return textColor(parseColor(this.badgeBgColor)) || '#ffffff';
},
...mapGetters('customisation', ['getPreviewCluster']),
canSubmit() {
return this.badgeDescription.length >= 1;
if (this.isCreate) {
return true;
}
if (this.badgeAsIcon && this.useCustomComment) {
return true;
} else {
return this.badgeAsIcon !== this.currentCluster.metadata?.annotations[CLUSTER_BADGE.ICON_TEXT] || this.useCustomComment !== !!this.currentCluster.metadata?.annotations[CLUSTER_BADGE.TEXT];
}
},
// Fake cluster object for use with badge component
previewCluster() {
// Make cluster object that is enough for the badge component to work
return {
return !this.isCreate ? {
isLocal: this.currentCluster.isLocal,
providerNavLogo: this.currentCluster.providerNavLogo,
badge: {
text: this.badgeDescription,
text: this.badgeComment,
color: this.badgeBgColor,
textColor: textColor(parseColor(this.badgeBgColor)),
iconText: this.badgeAsIcon ? this.letter.toUpperCase() : '',
}
} : {
isLocal: false,
ready: true,
providerNavLogo: '',
badge: {
text: this.badgeComment,
color: this.badgeBgColor,
textColor: textColor(parseColor(this.badgeBgColor || 'white')),
iconText: this.badgeAsIcon ? this.letter.toUpperCase() : '',
}
};
},
mode() {
return !!this.useCustomBadge ? 'edit' : 'view';
previewName() {
return this.isCreate ? this.clusterName : this.currentCluster.nameDisplay;
},
},
@ -86,25 +122,37 @@ export default {
async apply(buttonDone) {
try {
// Fetch the Norman cluster object
const norman = await this.$store.dispatch('rancher/find', { type: NORMAN.CLUSTER, id: this.currentCluster.id });
if (!this.isCreate) {
// Fetch the Norman cluster object
const norman = await this.$store.dispatch('rancher/find', { type: NORMAN.CLUSTER, id: this.currentCluster.id });
delete norman.annotations[CLUSTER_BADGE.TEXT];
delete norman.annotations[CLUSTER_BADGE.COLOR];
delete norman.annotations[CLUSTER_BADGE.ICON_TEXT];
delete norman.annotations[CLUSTER_BADGE.COLOR];
delete norman.annotations[CLUSTER_BADGE.ICON_TEXT];
delete norman.annotations[CLUSTER_BADGE.TEXT];
if (this.useCustomBadge) {
this.$set(norman.annotations, CLUSTER_BADGE.TEXT, this.badgeDescription);
this.$set(norman.annotations, CLUSTER_BADGE.COLOR, this.badgeBgColor);
if (this.badgeAsIcon) {
this.$set(norman.annotations, CLUSTER_BADGE.COLOR, this.badgeBgColor);
this.$set(norman.annotations, CLUSTER_BADGE.ICON_TEXT, this.letter.toUpperCase());
// If the user has a custom comment, set it as the badge text
if (this.useCustomComment) {
this.$set(norman.annotations, CLUSTER_BADGE.TEXT, this.badgeComment);
}
}
await norman.save();
buttonDone(true);
this.close();
} else {
if (!this.badgeComment) {
delete this.previewCluster.badge.text;
}
this.$store.commit('customisation/setPreviewCluster', { ...this.previewCluster });
buttonDone(true);
this.close();
}
await norman.save();
buttonDone(true);
this.close();
} catch (err) {
this.errors = exceptionToErrorsArray(err);
buttonDone(false);
@ -135,18 +183,18 @@ export default {
<div class="mt-10 pl-20 row preview-row">
<div class="badge-preview col span-12">
<ClusterProviderIcon
v-if="useCustomBadge"
v-if="isCreate"
:cluster="previewCluster"
/>
<ClusterProviderIcon
v-else
:cluster="currentCluster"
:cluster="previewCluster"
/>
<div class="cluster-name">
{{ currentCluster.nameDisplay }}
{{ previewName }}
</div>
<ClusterBadge
v-if="useCustomBadge"
v-if="useCustomComment"
:cluster="previewCluster"
/>
</div>
@ -155,7 +203,37 @@ export default {
<div class="row mt-10">
<div class="col">
<Checkbox
v-model="useCustomBadge"
v-model="badgeAsIcon"
:label="t('clusterBadge.modal.badgeAsIcon')"
class="mt-10"
:tooltip="t('clusterBadge.modal.maxCharsTooltip')"
/>
</div>
</div>
<div class="row mt-10">
<div class="col">
<LabeledInput
v-model.trim="letter"
:disabled="!badgeAsIcon"
class="badge-icon-text"
:label="t('clusterBadge.modal.iconText')"
:maxlength="3"
/>
</div>
<div class="col">
<ColorInput
v-model="badgeBgColor"
:disabled="!badgeAsIcon"
:default-value="badgeBgColor"
:label="t('clusterBadge.modal.badgeBgColor')"
/>
</div>
</div>
<div class="row mt-10">
<div class="col">
<Checkbox
v-model="useCustomComment"
:label="t('clusterBadge.modal.checkbox')"
class="mt-10"
/>
@ -166,46 +244,10 @@ export default {
<div class="row mt-10">
<div class="col span-12">
<LabeledInput
v-model.trim="badgeDescription"
:mode="mode"
:label="t('clusterBadge.modal.description')"
v-model.trim="badgeComment"
:disabled="!useCustomComment"
:label="t('clusterBadge.modal.comment')"
:maxlength="32"
:required="true"
/>
</div>
</div>
<div class="row mt-10">
<div class="col span-12">
<ColorInput
v-model="badgeBgColor"
:mode="mode"
:default-value="badgeBgColor"
:label="t('clusterBadge.modal.badgeBgColor')"
/>
</div>
</div>
<div class="row mt-10">
<div class="col">
<Checkbox
v-model="badgeAsIcon"
:mode="mode"
:label="t('clusterBadge.modal.badgeAsIcon')"
class="mt-10"
:tooltip="t('clusterBadge.modal.maxCharsTooltip')"
/>
</div>
</div>
<div class="row mt-10">
<div class="col">
<LabeledInput
v-model.trim="letter"
:disabled="!badgeAsIcon"
class="badge-icon-text"
:mode="mode"
:label="t('clusterBadge.modal.iconText')"
:maxlength="2"
/>
</div>
</div>
@ -239,67 +281,70 @@ export default {
</Card>
</template>
<style lang='scss' scoped>
.prompt-badge {
margin: 0;
.cluster-icon {
border: 1px solid var(--default-border);
}
.cluster-badge-body {
min-height: 50px;
display: flex;
flex-direction: column;
.prompt-badge {
margin: 0;
.preview-row {
height: 32px;
.badge-preview {
align-items: center;
display: flex;
height: 32px;
white-space: nowrap;
.cluster-name {
margin: 0 10px;
font-size: 16px;
}
.cluster-badge-icon-preview {
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 5px;
font-weight: bold;
}
.cluster-badge-preview {
cursor: default;
border-radius: 10px;
font-size: 12px;
padding: 2px 10px;
}
}
}
::v-deep .badge-icon-text input {
text-transform: uppercase;
}
}
}
.bottom {
.cluster-badge-body {
min-height: 50px;
display: flex;
flex-direction: column;
flex: 1;
.banner {
margin-top: 0;
.preview-row {
height: 32px;
.badge-preview {
align-items: center;
display: flex;
height: 32px;
white-space: nowrap;
.cluster-name {
margin: 0 10px;
font-size: 16px;
}
.cluster-badge-icon-preview {
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 5px;
font-weight: bold;
}
.cluster-badge-preview {
cursor: default;
border-radius: 10px;
font-size: 12px;
padding: 2px 10px;
}
}
}
.buttons {
display: flex;
justify-content: flex-end;
width: 100%;
::v-deep .badge-icon-text input {
text-transform: uppercase;
}
}
}
.bottom {
display: flex;
flex-direction: column;
flex: 1;
.banner {
margin-top: 0;
}
.buttons {
display: flex;
justify-content: flex-end;
width: 100%;
}
}
</style>

View File

@ -8,7 +8,8 @@ localVue.use(Vuex);
describe('view: fleet.cattle.io.gitrepo should', () => {
const mockStore = {
getters: {
dispatch: jest.fn(),
getters: {
'i18n/t': (text: string) => text,
t: (text: string) => text,
currentStore: () => 'current_store',
@ -27,7 +28,7 @@ describe('view: fleet.cattle.io.gitrepo should', () => {
return false;
}
}
}
},
};
const values = {
metadata: { namespace: 'test' }, spec: { template: {}, correctDrift: { enabled: false } }, targetInfo: { mode: 'all' },

View File

@ -11,7 +11,8 @@ describe('edit: management.cattle.io.setting should', () => {
// Remove all these mocks after migration to Vue 2.7/3 due mixin logic
mocks: {
$store: {
getters: {
dispatch: jest.fn(),
getters: {
currentStore: () => 'current_store',
'current_store/schemaFor': jest.fn(),
'current_store/all': jest.fn(),

View File

@ -13,12 +13,14 @@ describe('view: ui.cattle.io.navlink should', () => {
// Remove all these mocks after migration to Vue 2.7/3 due mixin logic
mocks: {
$store: {
getters: {
dispatch: jest.fn(),
getters: {
currentStore: () => 'current_store',
'current_store/schemaFor': jest.fn(),
'current_store/all': jest.fn(),
'i18n/t': (val) => val,
'i18n/exists': jest.fn(),
'store/customisation/': jest.fn()
}
},
$route: { query: { AS: '' } },

View File

@ -16,7 +16,8 @@ describe('view: logging.banzaicloud.io.output', () => {
},
mocks: {
$store: {
getters: {
dispatch: jest.fn(),
getters: {
currentStore: () => 'current_store',
'management/schemaFor': jest.fn(),
'current_store/all': jest.fn(),

View File

@ -76,12 +76,13 @@ const defaultComputed = {
};
const defaultGetters = {
currentStore: () => 'current_store',
'management/schemaFor': jest.fn(),
'current_store/all': jest.fn(),
'i18n/t': jest.fn(),
'i18n/withFallback': jest.fn(),
'plugins/cloudProviderForDriver': jest.fn()
currentStore: () => 'current_store',
'management/schemaFor': jest.fn(),
'current_store/all': jest.fn(),
'i18n/t': jest.fn(),
'i18n/withFallback': jest.fn(),
'plugins/cloudProviderForDriver': jest.fn(),
'customization/getPreviewCluster': jest.fn(),
};
const defaultMocks = {

View File

@ -43,7 +43,7 @@ import semver from 'semver';
import { SETTING } from '@shell/config/settings';
import { base64Encode } from '@shell/utils/crypto';
import { CAPI as CAPI_ANNOTATIONS } from '@shell/config/labels-annotations';
import { CAPI as CAPI_ANNOTATIONS, CLUSTER_BADGE } from '@shell/config/labels-annotations';
import AgentEnv from '@shell/edit/provisioning.cattle.io.cluster/AgentEnv';
import Labels from '@shell/edit/provisioning.cattle.io.cluster/Labels';
import MachinePool from '@shell/edit/provisioning.cattle.io.cluster/tabs/MachinePool';
@ -149,15 +149,15 @@ export default {
},
data() {
if ( !this.value.spec.rkeConfig ) {
if (!this.value.spec.rkeConfig) {
set(this.value.spec, 'rkeConfig', {});
}
if ( !this.value.spec.rkeConfig.chartValues ) {
if (!this.value.spec.rkeConfig.chartValues) {
set(this.value.spec.rkeConfig, 'chartValues', {});
}
if ( !this.value.spec.rkeConfig.upgradeStrategy ) {
if (!this.value.spec.rkeConfig.upgradeStrategy) {
set(this.value.spec.rkeConfig, 'upgradeStrategy', {
controlPlaneConcurrency: '1',
controlPlaneDrainOptions: {},
@ -166,11 +166,11 @@ export default {
});
}
if ( !this.value.spec.rkeConfig.machineGlobalConfig ) {
if (!this.value.spec.rkeConfig.machineGlobalConfig) {
set(this.value.spec, 'rkeConfig.machineGlobalConfig', {});
}
if ( !this.value.spec.rkeConfig.machineSelectorConfig?.length ) {
if (!this.value.spec.rkeConfig.machineSelectorConfig?.length) {
set(this.value.spec, 'rkeConfig.machineSelectorConfig', [{ config: {} }]);
}
@ -221,6 +221,9 @@ export default {
},
computed: {
clusterBadgeAbbreviation() {
return this.$store.getters['customisation/getPreviewCluster'];
},
rkeConfig() {
return this.value.spec.rkeConfig;
},
@ -251,11 +254,11 @@ export default {
// If there are any other properties set, or multiple configs with no selector
// show a warning that you're editing only part of the config in the UI.
for ( const conf of this.value.spec?.rkeConfig?.machineSelectorConfig ) {
if ( conf.machineLabelSelector ) {
for (const conf of this.value.spec?.rkeConfig?.machineSelectorConfig) {
if (conf.machineLabelSelector) {
const keys = Object.keys(conf.config || {});
if ( keys.length === 0 || (keys.length === 1 && keys[0] === 'kubelet-arg') ) {
if (keys.length === 0 || (keys.length === 1 && keys[0] === 'kubelet-arg')) {
kubeletOnly++;
} else {
other++;
@ -268,7 +271,7 @@ export default {
// eslint-disable-next-line no-console
console.log(`Global: ${ global }, Kubelet Only: ${ kubeletOnly }, Other: ${ other }`);
return ( global > 1 || other > 0 );
return (global > 1 || other > 0);
},
versionOptions() {
@ -291,26 +294,26 @@ export default {
const showK3s = allValidK3sVersions.length && !existingRke2;
const out = [];
if ( showRke2 ) {
if ( showK3s ) {
if (showRke2) {
if (showK3s) {
out.push({ kind: 'group', label: this.t('cluster.provider.rke2') });
}
out.push(...allValidRke2Versions);
}
if ( showK3s ) {
if ( showRke2 ) {
if (showK3s) {
if (showRke2) {
out.push({ kind: 'group', label: this.t('cluster.provider.k3s') });
}
out.push(...allValidK3sVersions);
}
if ( cur ) {
if (cur) {
const existing = out.find((x) => x.value === cur);
if ( existing ) {
if (existing) {
existing.disabled = false;
}
}
@ -324,7 +327,7 @@ export default {
selectedVersion() {
const str = this.value.spec.kubernetesVersion;
if ( !str ) {
if (!str) {
return;
}
@ -363,7 +366,7 @@ export default {
},
needCredential() {
if ( this.provider === 'custom' || this.provider === 'import' || this.isElementalCluster || this.mode === _VIEW || (this.providerConfig?.spec?.builtin === false && this.providerConfig?.spec?.addCloudCredential === false) ) {
if (this.provider === 'custom' || this.provider === 'import' || this.isElementalCluster || this.mode === _VIEW || (this.providerConfig?.spec?.builtin === false && this.providerConfig?.spec?.addCloudCredential === false)) {
return false;
}
@ -382,7 +385,7 @@ export default {
},
hasMachinePools() {
if ( this.provider === 'custom' || this.provider === 'import' ) {
if (this.provider === 'custom' || this.provider === 'import') {
return false;
}
@ -423,7 +426,7 @@ export default {
machineConfigSchema() {
let schema;
if ( !this.hasMachinePools ) {
if (!this.hasMachinePools) {
return null;
} else if (this.isElementalCluster) {
schema = ELEMENTAL_SCHEMA_IDS.MACHINE_INV_SELECTOR_TEMPLATES;
@ -457,53 +460,53 @@ export default {
tooltip: {},
};
for ( const role of roles ) {
for (const role of roles) {
counts[role] = 0;
out.color[role] = NODE_TOTAL.success.color;
out.icon[role] = NODE_TOTAL.success.icon;
}
for ( const row of this.machinePools || [] ) {
if ( row.remove ) {
for (const row of this.machinePools || []) {
if (row.remove) {
continue;
}
const qty = parseInt(row.pool.quantity, 10);
if ( isNaN(qty) ) {
if (isNaN(qty)) {
continue;
}
for ( const role of roles ) {
for (const role of roles) {
counts[role] = counts[role] + (row.pool[`${ role }Role`] ? qty : 0);
}
}
for ( const role of roles ) {
for (const role of roles) {
out.label[role] = this.t(`cluster.machinePool.nodeTotals.label.${ role }`, { count: counts[role] });
out.tooltip[role] = this.t(`cluster.machinePool.nodeTotals.tooltip.${ role }`, { count: counts[role] });
}
if ( counts.etcd === 0 ) {
if (counts.etcd === 0) {
out.color.etcd = NODE_TOTAL.error.color;
out.icon.etcd = NODE_TOTAL.error.icon;
} else if ( counts.etcd === 1 || counts.etcd % 2 === 0 || counts.etcd > 7 ) {
} else if (counts.etcd === 1 || counts.etcd % 2 === 0 || counts.etcd > 7) {
out.color.etcd = NODE_TOTAL.warning.color;
out.icon.etcd = NODE_TOTAL.warning.icon;
}
if ( counts.controlPlane === 0 ) {
if (counts.controlPlane === 0) {
out.color.controlPlane = NODE_TOTAL.error.color;
out.icon.controlPlane = NODE_TOTAL.error.icon;
} else if ( counts.controlPlane === 1 ) {
} else if (counts.controlPlane === 1) {
out.color.controlPlane = NODE_TOTAL.warning.color;
out.icon.controlPlane = NODE_TOTAL.warning.icon;
}
if ( counts.worker === 0 ) {
if (counts.worker === 0) {
out.color.worker = NODE_TOTAL.error.color;
out.icon.worker = NODE_TOTAL.error.icon;
} else if ( counts.worker === 1 ) {
} else if (counts.worker === 1) {
out.color.worker = NODE_TOTAL.warning.color;
out.icon.worker = NODE_TOTAL.warning.icon;
}
@ -533,11 +536,11 @@ export default {
}
if (this.showCloudProvider) { // Shouldn't be removed such that changes to it will re-trigger this watch
if ( this.agentConfig?.['cloud-provider-name'] === 'rancher-vsphere' ) {
if (this.agentConfig?.['cloud-provider-name'] === 'rancher-vsphere') {
names.push('rancher-vsphere-cpi', 'rancher-vsphere-csi');
}
if ( this.agentConfig?.['cloud-provider-name'] === HARVESTER ) {
if (this.agentConfig?.['cloud-provider-name'] === HARVESTER) {
names.push(HARVESTER_CLOUD_PROVIDER);
}
}
@ -562,11 +565,11 @@ export default {
value: '',
}];
if ( !!this.agentArgs['cloud-provider-name']?.options ) {
if (!!this.agentArgs['cloud-provider-name']?.options) {
const preferred = this.$store.getters['plugins/cloudProviderForDriver'](this.provider);
for ( const opt of this.agentArgs['cloud-provider-name']?.options ) {
// If we don't have a preferred provider... show all options
for (const opt of this.agentArgs['cloud-provider-name']?.options) {
// If we don't have a preferred provider... show all options
const showAllOptions = preferred === undefined;
// If we have a preferred provider... only show default, preferred and external
const isPreferred = opt === preferred;
@ -589,7 +592,7 @@ export default {
const cur = this.agentConfig?.['cloud-provider-name'];
if ( cur && !out.find((x) => x.value === cur) ) {
if (cur && !out.find((x) => x.value === cur)) {
out.unshift({ label: `${ cur } (Current)`, value: cur });
}
@ -623,7 +626,7 @@ export default {
}
}
if ( !out ) {
if (!out) {
out = preferred || first;
}
@ -631,7 +634,7 @@ export default {
},
appsOSWarning() {
if (this.mode !== _EDIT ) {
if (this.mode !== _EDIT) {
return null;
}
const { linuxWorkerCount, windowsWorkerCount } = this.value?.mgmt?.status || {};
@ -701,9 +704,26 @@ export default {
},
watch: {
clusterBadgeAbbreviation: {
immediate: true,
handler(neu) {
if (!neu) {
return;
}
const obj = {
[CLUSTER_BADGE.ICON_TEXT]: neu.badge.iconText, [CLUSTER_BADGE.COLOR]: neu.badge.color, [CLUSTER_BADGE.TEXT]: neu.badge.text
};
this.value.metadata.annotations = {
...this.value.metadata.annotations,
...obj
};
}
},
credentialId(val) {
if ( val ) {
if (val) {
this.credential = this.$store.getters['rancher/byId'](NORMAN.CLOUD_CREDENTIAL, this.credentialId);
if (this.isHarvesterDriver) {
@ -718,7 +738,7 @@ export default {
addonNames(neu, old) {
// To catch the 'some addons' --> 'no addons' case also check array length (`difference([], [1,2,3]) === []`)
const diff = old.length !== neu.length || difference(neu, old).length ;
const diff = old.length !== neu.length || difference(neu, old).length;
if (diff) {
// Allow time for addonNames to update... then fetch any missing addons
@ -780,32 +800,32 @@ export default {
* Initialize all the cluster specs
*/
async initSpecs() {
if ( !this.value.spec ) {
if (!this.value.spec) {
set(this.value, 'spec', {});
}
if ( !this.value.spec.machineSelectorConfig ) {
if (!this.value.spec.machineSelectorConfig) {
set(this.value.spec, 'machineSelectorConfig', []);
}
if ( !this.value.spec.machineSelectorConfig.find((x) => !x.machineLabelSelector) ) {
if (!this.value.spec.machineSelectorConfig.find((x) => !x.machineLabelSelector)) {
this.value.spec.machineSelectorConfig.unshift({ config: {} });
}
if ( this.value.spec.cloudCredentialSecretName ) {
if (this.value.spec.cloudCredentialSecretName) {
await this.$store.dispatch('rancher/findAll', { type: NORMAN.CLOUD_CREDENTIAL });
this.credentialId = `${ this.value.spec.cloudCredentialSecretName }`;
}
if ( !this.value.spec.kubernetesVersion ) {
if (!this.value.spec.kubernetesVersion) {
set(this.value.spec, 'kubernetesVersion', this.defaultVersion);
}
if ( this.rkeConfig.etcd?.s3?.bucket ) {
if (this.rkeConfig.etcd?.s3?.bucket) {
this.s3Backup = true;
}
if ( !this.rkeConfig.etcd ) {
if (!this.rkeConfig.etcd) {
set(this.rkeConfig, 'etcd', {
disableSnapshots: false,
s3: null,
@ -824,14 +844,14 @@ export default {
this.allNamespaces = await this.$store.dispatch('management/findAll', { type: NAMESPACE });
}
if ( !this.machinePools ) {
if (!this.machinePools) {
await this.initMachinePools(this.value.spec.rkeConfig.machinePools);
if ( this.mode === _CREATE && !this.machinePools.length ) {
if (this.mode === _CREATE && !this.machinePools.length) {
await this.addMachinePool();
}
}
if ( this.value.spec.defaultPodSecurityAdmissionConfigurationTemplateName === undefined ) {
if (this.value.spec.defaultPodSecurityAdmissionConfigurationTemplateName === undefined) {
set(this.value.spec, 'defaultPodSecurityAdmissionConfigurationTemplateName', '');
}
},
@ -840,7 +860,7 @@ export default {
* Fetch RKE versions and their configurations to be mapped to the form
*/
async fetchRke2Versions() {
if ( !this.rke2Versions ) {
if (!this.rke2Versions) {
const hash = {
rke2Versions: this.$store.dispatch('management/request', { url: '/v1-rke2-release/releases' }),
k3sVersions: this.$store.dispatch('management/request', { url: '/v1-k3s-release/releases' }),
@ -886,7 +906,7 @@ export default {
defaultK3s = k3sChannels.find((x) => x.id === 'default')?.latest;
}
if ( !this.rke2Versions.length && !this.k3sVersions.length ) {
if (!this.rke2Versions.length && !this.k3sVersions.length) {
throw new Error('No version info found in KDM');
}
@ -938,12 +958,12 @@ export default {
*/
setAgentConfiguration() {
// Cluster Agent Configuration
if ( !this.value.spec[CLUSTER_AGENT_CUSTOMIZATION]) {
if (!this.value.spec[CLUSTER_AGENT_CUSTOMIZATION]) {
set(this.value.spec, CLUSTER_AGENT_CUSTOMIZATION, {});
}
// Fleet Agent Configuration
if ( !this.value.spec[FLEET_AGENT_CUSTOMIZATION] ) {
if (!this.value.spec[FLEET_AGENT_CUSTOMIZATION]) {
set(this.value.spec, FLEET_AGENT_CUSTOMIZATION, {});
}
},
@ -968,8 +988,8 @@ export default {
async initMachinePools(existing) {
const out = [];
if ( existing?.length ) {
for ( const pool of existing ) {
if (existing?.length) {
for (const pool of existing) {
let type;
if (this.isElementalCluster) {
@ -981,7 +1001,7 @@ export default {
let config;
let configMissing = false;
if ( this.$store.getters['management/canList'](type) ) {
if (this.$store.getters['management/canList'](type)) {
try {
config = await this.$store.dispatch('management/find', {
type,
@ -1018,7 +1038,7 @@ export default {
async addMachinePool(idx) {
// this.machineConfigSchema is the schema for the Machine Pool's machine configuration for the given provider
if ( !this.machineConfigSchema ) {
if (!this.machineConfigSchema) {
return;
}
@ -1076,7 +1096,7 @@ export default {
this.machinePools.push(pool);
this.$nextTick(() => {
if ( this.$refs.pools?.select ) {
if (this.$refs.pools?.select) {
this.$refs.pools.select(name);
}
});
@ -1085,11 +1105,11 @@ export default {
removeMachinePool(idx) {
const entry = this.machinePools[idx];
if ( !entry ) {
if (!entry) {
return;
}
if ( entry.create ) {
if (entry.create) {
// If this is a new pool that isn't saved yet, it can just be dropped
removeObject(this.machinePools, entry);
} else {
@ -1148,8 +1168,8 @@ export default {
return await this.extensionProvider.saveMachinePoolConfigs(this.machinePools, this.value);
}
for ( const entry of this.machinePools ) {
if ( entry.remove ) {
for (const entry of this.machinePools) {
if (entry.remove) {
continue;
}
@ -1160,8 +1180,8 @@ export default {
const prefix = `${ this.value.metadata.name }-${ entry.pool.name }`.substr(0, 50).toLowerCase();
if ( entry.create ) {
if ( !entry.config.metadata?.name ) {
if (entry.create) {
if (!entry.config.metadata?.name) {
entry.config.metadata.generateName = `nc-${ prefix }-`;
}
@ -1171,12 +1191,12 @@ export default {
entry.pool.machineConfigRef.name = neu.metadata.name;
entry.create = false;
entry.update = true;
} else if ( entry.update ) {
} else if (entry.update) {
entry.config = await entry.config.save();
}
// Ensure Elemental clusters have a hostname prefix
if (this.isElementalCluster && !entry.pool.hostnamePrefix ) {
if (this.isElementalCluster && !entry.pool.hostnamePrefix) {
entry.pool.hostnamePrefix = `${ prefix }-`;
}
@ -1187,11 +1207,11 @@ export default {
},
async cleanupMachinePools() {
for ( const entry of this.machinePools ) {
if ( entry.remove && entry.config ) {
for (const entry of this.machinePools) {
if (entry.remove && entry.config) {
try {
await entry.config.remove();
} catch (e) {}
} catch (e) { }
}
}
},
@ -1212,7 +1232,7 @@ export default {
},
cancelCredential() {
if ( this.$refs.cruresource ) {
if (this.$refs.cruresource) {
this.$refs.cruresource.emitOrRoute();
}
},
@ -1220,7 +1240,7 @@ export default {
done() {
let routeName = 'c-cluster-product-resource';
if ( this.mode === _CREATE && (this.provider === 'import' || this.provider === 'custom') ) {
if (this.mode === _CREATE && (this.provider === 'import' || this.provider === 'custom')) {
// Go show the registration command
routeName = 'c-cluster-product-resource-namespace-id';
}
@ -1365,7 +1385,7 @@ export default {
return await this.extensionProvider?.saveCluster(this.value, this.schema);
}
if ( this.isCreate ) {
if (this.isCreate) {
url = url || this.schema.linkFor('collection');
const res = await this.value.save({ url });
@ -1410,12 +1430,12 @@ export default {
* 2) We're ready to cache any values the user provides for each addon
*/
async initAddons() {
for ( const chartName of this.addonNames ) {
for (const chartName of this.addonNames) {
const entry = this.chartVersions[chartName];
// prevent fetching of addon config for 'none' CNI option
// https://github.com/rancher/dashboard/issues/10338
if ( this.versionInfo[chartName] || chartName.includes('none')) {
if (this.versionInfo[chartName] || chartName.includes('none')) {
continue;
}
@ -1451,7 +1471,7 @@ export default {
refreshComponentWithYamls(key) {
const component = this.$refs[key];
if ( component ) {
if (component) {
this.refreshYamls(component.$refs);
}
},
@ -1459,11 +1479,11 @@ export default {
refreshYamls(refs) {
const keys = Object.keys(refs).filter((x) => x.startsWith('yaml'));
for ( const k of keys ) {
for (const k of keys) {
const entry = refs[k];
const list = isArray(entry) ? entry : [entry];
for ( const component of list ) {
for (const component of list) {
component?.refresh(); // `yaml` ref can be undefined on switching from Basic to Addon tab (Azure --> Amazon --> addon)
}
}
@ -1490,23 +1510,23 @@ export default {
},
initServerAgentArgs() {
for ( const k in this.serverArgs ) {
if ( this.serverConfig[k] === undefined ) {
for (const k in this.serverArgs) {
if (this.serverConfig[k] === undefined) {
const def = this.serverArgs[k].default;
set(this.serverConfig, k, (def !== undefined ? def : undefined));
}
}
for ( const k in this.agentArgs ) {
if ( this.agentConfig?.[k] === undefined ) {
for (const k in this.agentArgs) {
if (this.agentConfig?.[k] === undefined) {
const def = this.agentArgs[k].default;
set(this.agentConfig, k, (def !== undefined ? def : undefined));
}
}
if ( !this.serverConfig?.profile ) {
if (!this.serverConfig?.profile) {
set(this.serverConfig, 'profile', null);
}
},
@ -1539,22 +1559,22 @@ export default {
let registrySecret = null;
let regs = this.rkeConfig.registries;
if ( !regs ) {
if (!regs) {
regs = {};
set(this.rkeConfig, 'registries', regs);
}
if ( !regs.configs ) {
if (!regs.configs) {
set(regs, 'configs', {});
}
if ( !regs.mirrors ) {
if (!regs.mirrors) {
set(regs, 'mirrors', {});
}
const config = regs.configs[this.registryHost];
if ( config ) {
if (config) {
registrySecret = config.authConfigSecretName;
}
@ -1574,21 +1594,21 @@ export default {
setRegistryConfig() {
const hostname = (this.registryHost || '').trim();
if ( this.systemRegistry ) {
if (this.systemRegistry) {
// Empty string overrides the system default to nothing
set(this.agentConfig, 'system-default-registry', '');
} else {
// No need to set anything
set(this.agentConfig, 'system-default-registry', undefined);
}
if ( !hostname || hostname === this.systemRegistry ) {
if (!hostname || hostname === this.systemRegistry) {
// Undefined removes the key which uses the global setting without hardcoding it into the config
set(this.agentConfig, 'system-default-registry', undefined);
} else {
set(this.agentConfig, 'system-default-registry', hostname);
}
if ( hostname && this.registrySecret ) {
if (hostname && this.registrySecret) {
// For a registry with basic auth, but no mirrors,
// add a single registry config with the basic auth secret.
const basicAuthConfig = {
@ -1637,12 +1657,12 @@ export default {
let isCurrentVersion = false;
let label = obj.id;
if ( currentVersion ) {
if (currentVersion) {
disabled = compare(obj.id, currentVersion) < 0;
isCurrentVersion = compare(obj.id, currentVersion) === 0;
}
if ( defaultVersion ) {
if (defaultVersion) {
experimental = compare(defaultVersion, obj.id) < 0;
}
@ -1914,7 +1934,7 @@ export default {
return this.t('cluster.banner.machinePoolError', {
count: x[1].length, pool_name: x[0], fields: formattedFields
}, true);
} )
})
.filter((x) => x);
if (!errors) {
@ -1925,7 +1945,7 @@ export default {
},
handleS3BackupChanged(neu) {
this.s3Backup = neu;
if ( neu ) {
if (neu) {
// We need to make sure that s3 doesn't already have an existing value otherwise when editing a cluster with s3 defined this will clear s3.
if (isEmpty(this.rkeConfig.etcd?.s3)) {
set(this.rkeConfig.etcd, 's3', {});
@ -1961,7 +1981,7 @@ export default {
}
for (const [index] of this.machinePools.entries()) { // validator machine config
if ( typeof this.$refs.pool[index]?.test === 'function' ) {
if (typeof this.$refs.pool[index]?.test === 'function') {
try {
const res = await this.$refs.pool[index].test();
@ -2001,7 +2021,7 @@ export default {
@done="done"
@finish="saveOverride"
@cancel="cancel"
@error="e=>errors = e"
@error="e => errors = e"
>
<div class="header-warnings">
<Banner
@ -2038,7 +2058,7 @@ export default {
name-placeholder="cluster.name.placeholder"
description-label="cluster.description.label"
description-placeholder="cluster.description.placeholder"
:rules="{name:fvGetAndReportPathRules('metadata.name')}"
:rules="{ name: fvGetAndReportPathRules('metadata.name') }"
/>
<Banner
@ -2112,7 +2132,7 @@ export default {
:pool-id="obj.id"
:pool-create-mode="obj.create"
@error="handleMachinePoolError"
@validationChanged="v=>machinePoolValidationChanged(obj.id, v)"
@validationChanged="v => machinePoolValidationChanged(obj.id, v)"
/>
</Tab>
</template>
@ -2352,13 +2372,15 @@ export default {
</template>
<style lang="scss" scoped>
.min-height {
min-height: 40em;
}
.patch-version {
margin-top: 5px;
}
.header-warnings .banner {
margin-bottom: 0;
}
.min-height {
min-height: 40em;
}
.patch-version {
margin-top: 5px;
}
.header-warnings .banner {
margin-bottom: 0;
}
</style>

View File

@ -299,9 +299,10 @@ export default class MgmtCluster extends HybridModel {
// Custom badge to show for the Cluster (if the appropriate annotations are set)
get badge() {
const text = this.metadata?.annotations?.[CLUSTER_BADGE.TEXT];
const icon = this.metadata?.annotations?.[CLUSTER_BADGE.ICON_TEXT];
const comment = this.metadata?.annotations?.[CLUSTER_BADGE.TEXT];
if (!text) {
if (!icon) {
return undefined;
}
@ -309,10 +310,10 @@ export default class MgmtCluster extends HybridModel {
const iconText = this.metadata?.annotations[CLUSTER_BADGE.ICON_TEXT] || '';
return {
text,
text: comment || undefined,
color,
textColor: textColor(parseColor(color)),
iconText: iconText.substr(0, 2)
iconText: iconText.substr(0, 3)
};
}

View File

@ -0,0 +1,35 @@
const defaultState = {
previewCluster: {
ready: true,
isLocal: false,
badge: { }
}
};
export const state = function() {
return { previewCluster: defaultState.previewCluster };
};
export const getters = {
getPreviewCluster(state) {
return state.previewCluster;
},
};
export const mutations = {
setPreviewCluster(state, previewCluster) {
state.previewCluster = previewCluster;
},
setDefaultPreviewCluster(state) {
state.previewCluster = defaultState.previewCluster;
}
};
export const actions = {
setPreviewCluster({ commit }, previewCluster) {
commit('setPreviewCluster', previewCluster);
},
setDefaultPreviewCluster({ commit }) {
commit('setDefaultPreviewCluster');
}
};

View File

@ -3,7 +3,7 @@ import { abbreviateClusterName } from '@shell/utils/cluster';
describe('fx: abbreviateClusterName', () => {
it.each([
['local', 'lcl'],
['world-wide-web', 'www'],
['world-wide-web', 'wwb'],
['a', 'a'],
['ab', 'ab'],
['abc', 'abc'],
@ -17,32 +17,34 @@ describe('fx: abbreviateClusterName', () => {
['a-b', 'a-b'],
['1-2', '1-2'],
['a-b-c', 'abc'],
['abc-def-ghi', 'adg'],
['abc-def-ghi', 'adi'],
['abcd-efgh', 'adh'],
['a-bc', 'abc'],
['ab-c', 'abc'],
['ab-cd', 'abd'],
['a-bcd', 'abd'],
['abc-d', 'acd'],
['0123-4567-89ab-cdef', '048'],
['0123-4567-89ab-cdef-ghij', '048'],
['0123-4567-8901-2345', '048'],
['0123-4567-8901-2345-6789', '048'],
['0123-4567-89ab-cdef', '04f'],
['0123-4567-89ab-cdef-ghij', '04j'],
['0123-4567-8901-2345', '045'],
['0123-4567-8901-2345-6789', '049'],
['a1b', 'a1b'],
['a1b2', 'a1b'],
['ab12cd34', 'a1c'],
['test-cluster-one', 'tco'],
['a1b2', 'a12'],
['ab12cd34', 'a14'],
['test-cluster-one', 'tce'],
['test-cluster-1', 'tc1'],
['customer-support-team-1', 'cst'],
['customer-support-team-one', 'cst'],
['customer-support-team-prod', 'cst'],
['customer-support-team-dev', 'cst'],
['customer-support-team-prod-1', 'cst'],
['customer-support-team-dev-1', 'cst'],
['customer-support-team-prod-2', 'cst'],
['customer-support-team-dev-2', 'cst'],
['customer-support-team-1', 'cs1'],
['customer-support-team-one', 'cse'],
['customer-support-team-prod', 'csd'],
['customer-support-team-dev', 'csv'],
['customer-support-team-prod-1', 'cs1'],
['customer-support-team-prod-f1xz', 'csz'],
['customer-support-team-dev-1', 'cs1'],
['customer-support-team-prod-2', 'cs2'],
['customer-support-team-dev-2', 'cs2'],
['s322', 's32'],
['mew-test10', 'mt0'],
['mew-test11', 'mt1'],
['sowmya2', 'sa2'],
['sowmya4', 'sa4'],
['sowmya', 'sma'],

View File

@ -85,7 +85,7 @@ export function abbreviateClusterName(input) {
break;
}
default:
result = segments.slice(0, 3).map((segment) => segment[0]).join('');
result = segments.slice(0, 2).map((segment) => segment[0]).join('') + segments.slice(-1)[0].slice(-1);
}
return result;

View File

@ -13074,9 +13074,9 @@ queue-microtask@^1.2.2:
resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
rancher-icons@rancher/icons#v2.0.19:
version "2.0.19"
resolved "https://codeload.github.com/rancher/icons/tar.gz/811e4800dd41dd9925b8ac9ebb0b1774f2f90143"
rancher-icons@rancher/icons#v2.0.21:
version "2.0.21"
resolved "https://codeload.github.com/rancher/icons/tar.gz/a5b583d46c268282c2753b955cfafc383241b55f"
randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0:
version "2.1.0"