diff --git a/plugins/kubernetes/src/components/CronJobsAccordions/CronJobsAccordions.tsx b/plugins/kubernetes/src/components/CronJobsAccordions/CronJobsAccordions.tsx index 2581678928..ce2dc340e7 100644 --- a/plugins/kubernetes/src/components/CronJobsAccordions/CronJobsAccordions.tsx +++ b/plugins/kubernetes/src/components/CronJobsAccordions/CronJobsAccordions.tsx @@ -29,7 +29,7 @@ import { CronJobDrawer } from './CronJobsDrawer'; import { getOwnedResources } from '../../utils/owner'; import { GroupedResponsesContext } from '../../hooks'; import { StatusError, StatusOK } from '@backstage/core-components'; -import cronstrue from 'cronstrue'; +import { humanizeCron } from '../../utils/crons'; type CronJobsAccordionsProps = { children?: React.ReactNode; @@ -79,7 +79,7 @@ const CronJobSummary = ({ cronJob }: CronJobSummaryProps) => { Schedule:{' '} {cronJob.spec?.schedule - ? `${cronJob.spec.schedule} (${cronstrue.toString( + ? `${cronJob.spec.schedule} (${humanizeCron( cronJob.spec.schedule, )})` : 'N/A'} diff --git a/plugins/kubernetes/src/utils/crons.test.ts b/plugins/kubernetes/src/utils/crons.test.ts new file mode 100644 index 0000000000..1617a20e1f --- /dev/null +++ b/plugins/kubernetes/src/utils/crons.test.ts @@ -0,0 +1,52 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { humanizeCron } from './crons'; + +describe('crons', () => { + describe('humanizeCron', () => { + it('should handle kubernetes aliases', () => { + expect(humanizeCron('@yearly')).toBe( + 'At 12:00 AM, on day 1 of the month, only in January', + ); + expect(humanizeCron('@annually')).toBe( + 'At 12:00 AM, on day 1 of the month, only in January', + ); + expect(humanizeCron('@monthly')).toBe( + 'At 12:00 AM, on day 1 of the month', + ); + expect(humanizeCron('@weekly')).toBe('At 12:00 AM, only on Sunday'); + expect(humanizeCron('@daily')).toBe('At 12:00 AM'); + expect(humanizeCron('@midnight')).toBe('At 12:00 AM'); + expect(humanizeCron('@hourly')).toBe('Every hour'); + }); + + it('should handle regular crons', () => { + expect(humanizeCron('0 23 * * *')).toBe('At 11:00 PM'); + expect(humanizeCron('0 0 13 * 5')).toBe( + 'At 12:00 AM, on day 13 of the month, and on Friday', + ); + }); + + it('should handle empty strings', () => { + expect(humanizeCron('')).toBe(''); + }); + + it('should return the original schedule rather than throwing an error', () => { + expect(humanizeCron('@invalid')).toBe('@invalid'); + }); + }); +}); diff --git a/plugins/kubernetes/src/utils/crons.ts b/plugins/kubernetes/src/utils/crons.ts new file mode 100644 index 0000000000..be383c41ca --- /dev/null +++ b/plugins/kubernetes/src/utils/crons.ts @@ -0,0 +1,40 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import cronstrue from 'cronstrue'; + +// Defined at https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/ +const k8sCronAliases = new Map([ + ['@yearly', '0 0 1 1 *'], + ['@annually', '0 0 1 1 *'], + ['@monthly', '0 0 1 * *'], + ['@weekly', '0 0 * * 0'], + ['@daily', '0 0 * * *'], + ['@midnight', '0 0 * * *'], + ['@hourly', '0 * * * *'], +]); + +// humanizeCron takes into account the aliases provided by kubernetes before +// calling cronstrue. In an effort to not throw an error, it will return the +// original cron formatted schedule if the cronstrue call fails. +export const humanizeCron = (schedule: string): string => { + const deAliasedSchedule = k8sCronAliases.get(schedule) || schedule; + try { + return cronstrue.toString(deAliasedSchedule); + } catch (e) { + return deAliasedSchedule; + } +};