-
Notifications
You must be signed in to change notification settings - Fork 12.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Alerting: Adds support for timezones in mute timings #68813
Conversation
@@ -631,6 +631,7 @@ muteTimes: | |||
- times: | |||
- start_time: '06:00' | |||
end_time: '23:59' | |||
location: 'UTC' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps this needs some comment about what timezone syntax is acceptable?
} | ||
|
||
const useDefaultValues = (muteTiming?: MuteTimeInterval): MuteTimingFields => { | ||
return useMemo(() => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Diff here looks a bit weird but removed the unnecessary useMemo
}; | ||
|
||
const defaultPageNav: Partial<NavModelItem> = { | ||
icon: 'sitemap', | ||
}; | ||
|
||
const MuteTimingForm = ({ muteTiming, showError, provenance }: Props) => { | ||
const MuteTimingForm = ({ muteTiming, showError, loading, provenance }: Props) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
loading
is now being passed in to the form because for some reason the form contains the AlertingPageWrapper
. We were showing multiple loaders before and one of them was even rendered entirely outside of the application.
}) | ||
); | ||
|
||
setUpdating(true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We're disabling the form when the AM is still updating with changes – previously we immediately redirected and hoped it would have saved the changes.
</div> | ||
); | ||
})} | ||
<Stack direction="column" gap={2}> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another weird diff; this time because I'm wrapping the intervals
in a <Stack />
instead of applying bottom margins. There are some important changes in here though so don't skip past this part of the review.
})} | ||
width={50} | ||
placeholder="Example: 1:3, may:august, december" | ||
// @ts-ignore react-hook-form doesn't handle nested field arrays well |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You'll see // @ts-ignore
in a few places – this basically comes down to react-hook-form
being sort of clueless about the types of nested fields when using useFieldArray<>()
return DAYS_OF_THE_WEEK.slice(startIndex, endIndex + 1); | ||
} | ||
|
||
const DaysOfTheWeek = ({ defaultValue = '', onChange }: DaysOfTheWeekProps) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These changes are for parsing the Alertmanager config for weekdays in to our visual weekday selector.
Valid config is for example monday, wednesday:friday sunday
which translates to monday, wednesday, thursday, friday, sunday
if (!timeString) { | ||
return true; | ||
} | ||
const [hour, minutes] = timeString.split(':').map((x) => parseInt(x, 10)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed all of this in favor of a regular expression I've copied from the Alertmanager source. Previous implementation contained small bugs and would allow invalid syntax.
@@ -186,26 +183,6 @@ function useColumns(alertManagerSourceName: string, hideActions = false, setMute | |||
}, [alertManagerSourceName, setMuteTimingName, showActions, permissions]); | |||
} | |||
|
|||
function renderTimeIntervals(timeIntervals: TimeInterval[]) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
moved to utils
refetch?: boolean; // refetch config on success | ||
} | ||
|
||
export const updateAlertManagerConfigAction = createAsyncThunk<void, UpdateAlertManagerConfigActionOptions, {}>( | ||
'unifiedalerting/updateAMConfig', | ||
({ alertManagerSourceName, oldConfig, newConfig, successMessage, redirectPath, refetch }, thunkAPI): Promise<void> => | ||
({ alertManagerSourceName, oldConfig, newConfig, successMessage, redirectPath, redirectSearch, refetch }, thunkAPI): Promise<void> => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added support for redirectSearch
here so we don't lose the original search params when redirecting
Hi there-so we'd need to ask Deyan perhaps--but as a user I prefer the text to tell me what I have to enter in a field-so more actionable really - so "Enter a time inclusive xxxx" also I think we need full stops - it looks odd to me without - but again - not sure of Grafana rules on this exactly. |
@@ -315,6 +315,8 @@ export interface TimeInterval { | |||
days_of_month?: string[]; | |||
months?: string[]; | |||
years?: string[]; | |||
/** IANA TZ identifier like "Europe/Brussels", also supports "Local" or "UTC" */ | |||
location?: string; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, location is a feature of Alertmanager?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return ( | ||
'Times: ' + | ||
(times ? times?.map(({ start_time, end_time }) => `${start_time} - ${end_time} UTC`).join(' and ') : 'All') | ||
(times |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we slightly refactor this method so it's more readable? Does it return the format required by Alertmanager?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nay, this is just our presentation of a time string so we can do whatever we want here.
I had initially wanted to refactor the way we render mute timing, making it more visual and showing both the next mute time start and duration but decided it's probably best if I don't make this PR too large.
I'll refactor this function a bit though because it's turning in to quite the monstrosity
@@ -92,8 +92,10 @@ export function recordToArray(record: Record<string, string>): Array<{ key: stri | |||
return Object.entries(record).map(([key, value]) => ({ key, value })); | |||
} | |||
|
|||
export function makeAMLink(path: string, alertManagerName?: string, options?: Record<string, string>): string { | |||
type URLParamsLike = ConstructorParameters<typeof URLSearchParams>[0]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice!
// @ts-ignore typescript types here incorrect, sigh | ||
const startDate = moment().startOf('day').add(startTime, timeUnit); | ||
// @ts-ignore typescript types here incorrect, sigh | ||
const endDate = moment().startOf('day').add(endTime, timeUnit); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe date-fns
provides better types?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately I didn't find a function in date-fns
that can work with the HH:mm
notation – I checked that library first before settling on moment
:(
import { SelectableValue } from '@grafana/data'; | ||
import { Select, SelectCommonProps } from '@grafana/ui'; | ||
|
||
const TIMEZONES = [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can take a look if Intl.supportedValuesOf('timeZone')
could give you this list
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great idea! I was looking for it in the Inl
namespace but didn't find that one – thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM! Great work! 🎉
Is this live already? if yes, which version of Grafana is this pls? |
Hi! 👋 It will be in Grafana 10.1, scheduled to be released next week! |
@gillesdemey Thanks a lot for this. Isn't an issue that it is still written in "The time inclusive of the starting time and exclusive of the end time in UTC" ? Also is it possible to write a time range with a midnight overlap? To avoid having writing this. |
What is this feature?
This PR adds several improvements to mute timings:
Which issue(s) does this PR fix?:
Fixes #48508
Special notes for your reviewer:
Still needs to write a few tests for this one, but it's ready for a functionality and code review.
Future improvements
While working on this I'm thinking the following improvements might be interesting;