@@ -125,7 +125,7 @@ func cronCalendarEventsForTeam(
125
125
for _ , policy := range policies {
126
126
policyIDs = append (policyIDs , policy .ID )
127
127
}
128
- hosts , err := ds .GetHostsPolicyMemberships (ctx , domain , policyIDs )
128
+ hosts , err := ds .GetTeamHostsPolicyMemberships (ctx , domain , team . ID , policyIDs )
129
129
if err != nil {
130
130
return fmt .Errorf ("get team hosts failing policies: %w" , err )
131
131
}
@@ -150,22 +150,28 @@ func cronCalendarEventsForTeam(
150
150
}
151
151
level .Debug (logger ).Log (
152
152
"msg" , "summary" ,
153
+ "team_id" , team .ID ,
153
154
"passing_hosts" , len (passingHosts ),
154
155
"failing_hosts" , len (failingHosts ),
155
156
"failing_hosts_without_associated_email" , len (failingHostsWithoutAssociatedEmail ),
156
157
)
157
158
159
+ // Remove calendar events from hosts that are passing the calendar policies.
160
+ //
161
+ // We execute this first to remove any calendar events for a user that is now passing
162
+ // policies on one of its hosts, and possibly create a new calendar event if they have
163
+ // another failing host on the same team.
164
+ if err := removeCalendarEventsFromPassingHosts (ctx , ds , calendar , passingHosts ); err != nil {
165
+ level .Info (logger ).Log ("msg" , "removing calendar events from passing hosts" , "err" , err )
166
+ }
167
+
168
+ // Process hosts that are failing calendar policies.
158
169
if err := processCalendarFailingHosts (
159
170
ctx , ds , calendar , orgName , failingHosts , logger ,
160
171
); err != nil {
161
172
level .Info (logger ).Log ("msg" , "processing failing hosts" , "err" , err )
162
173
}
163
174
164
- // Remove calendar events from hosts that are passing the policies.
165
- if err := removeCalendarEventsFromPassingHosts (ctx , ds , calendar , passingHosts ); err != nil {
166
- level .Info (logger ).Log ("msg" , "removing calendar events from passing hosts" , "err" , err )
167
- }
168
-
169
175
// At last we want to log the hosts that are failing and don't have an associated email.
170
176
logHostsWithoutAssociatedEmail (
171
177
domain ,
@@ -184,14 +190,26 @@ func processCalendarFailingHosts(
184
190
hosts []fleet.HostPolicyMembershipData ,
185
191
logger kitlog.Logger ,
186
192
) error {
193
+ hosts = filterHostsWithSameEmail (hosts )
194
+
187
195
for _ , host := range hosts {
188
196
logger := log .With (logger , "host_id" , host .HostID )
189
197
190
- hostCalendarEvent , calendarEvent , err := ds .GetHostCalendarEvent (ctx , host .HostID )
198
+ hostCalendarEvent , calendarEvent , err := ds .GetHostCalendarEventByEmail (ctx , host .Email )
191
199
192
200
expiredEvent := false
193
201
webhookAlreadyFiredThisMonth := false
194
202
if err == nil {
203
+ if hostCalendarEvent .HostID != host .HostID {
204
+ // This calendar event belongs to another host with this associated email,
205
+ // thus we skip this entry.
206
+ continue // continue with next host
207
+ }
208
+ if hostCalendarEvent .WebhookStatus == fleet .CalendarWebhookStatusPending {
209
+ // This can happen if the host went offline (and never returned results)
210
+ // after setting the webhook as pending.
211
+ continue // continue with next host
212
+ }
195
213
now := time .Now ()
196
214
webhookAlreadyFired := hostCalendarEvent .WebhookStatus == fleet .CalendarWebhookStatusSent
197
215
if webhookAlreadyFired && sameDate (now , calendarEvent .StartTime ) {
@@ -200,7 +218,7 @@ func processCalendarFailingHosts(
200
218
continue // continue with next host
201
219
}
202
220
webhookAlreadyFiredThisMonth = webhookAlreadyFired && sameMonth (now , calendarEvent .StartTime )
203
- if calendarEvent .EndTime .Before (time . Now () ) {
221
+ if calendarEvent .EndTime .Before (now ) {
204
222
expiredEvent = true
205
223
}
206
224
}
@@ -232,6 +250,25 @@ func processCalendarFailingHosts(
232
250
return nil
233
251
}
234
252
253
+ func filterHostsWithSameEmail (hosts []fleet.HostPolicyMembershipData ) []fleet.HostPolicyMembershipData {
254
+ minHostPerEmail := make (map [string ]fleet.HostPolicyMembershipData )
255
+ for _ , host := range hosts {
256
+ minHost , ok := minHostPerEmail [host .Email ]
257
+ if ! ok {
258
+ minHostPerEmail [host .Email ] = host
259
+ continue
260
+ }
261
+ if host .HostID < minHost .HostID {
262
+ minHostPerEmail [host .Email ] = host
263
+ }
264
+ }
265
+ filtered := make ([]fleet.HostPolicyMembershipData , 0 , len (minHostPerEmail ))
266
+ for _ , host := range minHostPerEmail {
267
+ filtered = append (filtered , host )
268
+ }
269
+ return filtered
270
+ }
271
+
235
272
func processFailingHostExistingCalendarEvent (
236
273
ctx context.Context ,
237
274
ds fleet.Datastore ,
@@ -416,10 +453,13 @@ func removeCalendarEventsFromPassingHosts(
416
453
hosts []fleet.HostPolicyMembershipData ,
417
454
) error {
418
455
for _ , host := range hosts {
419
- calendarEvent , err := ds .GetCalendarEvent (ctx , host .Email )
456
+ hostCalendarEvent , calendarEvent , err := ds .GetHostCalendarEventByEmail (ctx , host .Email )
420
457
switch {
421
458
case err == nil :
422
- // OK
459
+ if hostCalendarEvent .HostID != host .HostID {
460
+ // This calendar event belongs to another host, thus we skip this entry.
461
+ continue
462
+ }
423
463
case fleet .IsNotFound (err ):
424
464
continue
425
465
default :
0 commit comments