From ee915249ceebf4af8d1d8bc4f5eee7055b1112d9 Mon Sep 17 00:00:00 2001 From: Marcelo Fukumoto Date: Tue, 10 Nov 2026 11:21:30 +0000 Subject: [PATCH] fix: keep the ignore rule name valid when the check has spaces (#1234) Backup schedule checks are named after the schedule's condition reason, such as "Proactive Schedule Suspend". The rule name used it lowercased, spaces included, so Kubernetes refused it and the check could not be ignored. Turn whatever a name can't have into hyphens. The rule keeps the check as it is in its spec, which is what the controller matches. Signed-off-by: Marcelo Fukumoto --- .../health/__tests__/health.test.js | 19 +++++++++++++++++-- pkg/harvester/utils/component-health.js | 7 +++++-- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/pkg/harvester/components/health/__tests__/health.test.js b/pkg/harvester/components/health/__tests__/health.test.js index 450ff136..7a406fff 100644 --- a/pkg/harvester/components/health/__tests__/health.test.js +++ b/pkg/harvester/components/health/__tests__/health.test.js @@ -156,7 +156,9 @@ const actionsOf = (props = {}, storeProps = {}) => { }; }; -const mountRuleModal = ({ rule, mode, save = () => {} } = {}) => { +const mountRuleModal = ({ + rule, mode, save = () => {}, row = CHECK +} = {}) => { createRule.mockClear(); cloneRule.mockClear(); saveRule.mockReset().mockImplementation(save); @@ -167,7 +169,7 @@ const mountRuleModal = ({ rule, mode, save = () => {} } = {}) => { const w = vtuMount(IgnoreRuleModal, { ...opts(), props: { - row: CHECK, rule, mode, modal: { show: true, onClose } + row, rule, mode, modal: { show: true, onClose } }, }); @@ -408,6 +410,10 @@ describe('health components', () => { it('names the rule of a check after it', () => { expect(ignoreRuleName('harvester-controller-vmimage', 'ImageRetryLimitExceeded')).toBe('harvester-controller-vmimage.imageretrylimitexceeded'); }); + it('turns what a name can not have in a check into hyphens', () => { + expect(ignoreRuleName('harvester-controller-schedulevmbackup', 'Proactive Schedule Suspend')).toBe('harvester-controller-schedulevmbackup.proactive-schedule-suspend'); + expect(ignoreRuleName('h', ' Reach_Max Failure! ')).toBe('h.reach-max-failure'); + }); it('tells the ignored resources apart, and all of them when the check is ignored', () => { const table = { props: ['rows'], template: '
' }; const row = { @@ -634,6 +640,15 @@ describe('health components', () => { expect(createRule.mock.calls[0][1].metadata).toStrictEqual({ name: 'h.a', labels: { 'health.harvesterhci.io/source': 'user' } }); }); + it('gives a check with spaces a valid name, and keeps the check as it is', async() => { + const { confirm } = mountRuleModal({ row: { ...CHECK, checkName: 'Proactive Schedule Suspend' } }); + + await confirm().trigger('click'); + await flushPromises(); + + expect(createRule.mock.calls[0][1].metadata.name).toBe('h.proactive-schedule-suspend'); + expect(newRule.spec.checkKey).toBe('Proactive Schedule Suspend'); + }); it('says when the rule will expire, from 7 days on', async() => { const { confirm, duration, currentExpiry, newExpiry diff --git a/pkg/harvester/utils/component-health.js b/pkg/harvester/utils/component-health.js index 030a9d46..0b5e4a43 100644 --- a/pkg/harvester/utils/component-health.js +++ b/pkg/harvester/utils/component-health.js @@ -86,9 +86,12 @@ export function ignoredCounts(checks) { }, { checks: 0, resources: 0 }); } -// the UI keeps one rule per check under this name, so Kubernetes refuses a second one for the same check +// the UI keeps one rule per check under this name, so Kubernetes refuses a second one for the same check. +// Some checks are named after a condition reason with spaces, like "Proactive Schedule Suspend", which a name can't have export function ignoreRuleName(componentHealthName, checkName) { - return `${ componentHealthName }.${ checkName.toLowerCase() }`; + const check = checkName.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, ''); + + return `${ componentHealthName }.${ check }`; } export function affectedResourceKind(row) {