drone/internal/webhook/events.go
Johannes Batzill 1f1118f624 [Webhook] Add Event Processing and Retrigger (#148)
This change includes:
- webhook event processor (includes actual webhook execution)
- webhook retrigger API allowing to retrigger previous webhook executions
2023-01-04 14:43:36 -08:00

89 lines
3.1 KiB
Go

// Copyright 2022 Harness Inc. All rights reserved.
// Use of this source code is governed by the Polyform Free Trial License
// that can be found in the LICENSE.md file for this repository.
package webhook
import (
"context"
"errors"
"fmt"
"github.com/harness/gitness/internal/store"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
"github.com/rs/zerolog/log"
"go.uber.org/multierr"
)
func generateTriggerIDFromEventID(eventID string) string {
return fmt.Sprintf("event-%s", eventID)
}
func triggerForEventWithGitUID(ctx context.Context, server *Server, repoStore store.RepoStore, eventID string,
repoGitUID string, triggerType enum.WebhookTrigger, createBody func(*types.Repository) interface{}) error {
// TODO: can we avoid this DB call? would need the gitrpc to know the repo id though ...
repo, err := repoStore.FindByGitUID(ctx, repoGitUID)
// not found error is unrecoverable - most likely a racing condition of repo being deleted by now
if err != nil && errors.Is(err, store.ErrResourceNotFound) {
log.Ctx(ctx).Warn().Err(err).
Msgf("discard event since repo with gitUID '%s' doesn't exist anymore", repoGitUID)
return nil
}
// all other errors we return and force the event to be reprocessed
if err != nil {
return fmt.Errorf("failed to get repo for gitUID '%s': %w", repoGitUID, err)
}
body := createBody(repo)
return triggerForEvent(ctx, server, eventID, enum.WebhookParentRepo, repo.ID, triggerType, body)
}
func triggerForEvent(ctx context.Context, server *Server, eventID string,
parentType enum.WebhookParent, parentID int64, triggerType enum.WebhookTrigger, body interface{}) error {
triggerID := generateTriggerIDFromEventID(eventID)
results, err := server.triggerWebhooksFor(ctx, parentType, parentID, triggerID, triggerType, body)
// return all errors and force the event to be reprocessed (it's not webhook execution specific!)
if err != nil {
return fmt.Errorf("failed to trigger %s (id: '%s') for webhooks of %s %d: %w",
triggerType, triggerID, parentType, parentID, err)
}
// go through all events and figure out if we need to retry the event.
// Combine all errors into a single error to log (to reduce number of logs)
retryRequired := false
var errs error
for _, result := range results {
if result.Skipped() {
continue
}
// combine errors of non-successful executions
if result.Execution.Result != enum.WebhookExecutionResultSuccess {
errs = multierr.Append(errs, fmt.Errorf("execution %d of webhook %d resulted in %s: %w",
result.Execution.ID, result.Webhook.ID, result.Execution.Result, result.Err))
}
if result.Execution.Result == enum.WebhookExecutionResultRetriableError {
retryRequired = true
}
}
// in case there was at least one error, log error details in single log to reduce log flooding
if errs != nil {
log.Ctx(ctx).Warn().Err(errs).Msgf("webhook execution for %s %d had errors", parentType, parentID)
}
// in case at least one webhook has to be retried, return an error to the event framework to have it reprocessed
if retryRequired {
return fmt.Errorf("at least one webhook execution resulted in a retry for %s %d", parentType, parentID)
}
return nil
}