Files
Reloader/internal/pkg/webhook/webhook.go
T

96 lines
2.4 KiB
Go

// Package webhook handles sending reload notifications to external endpoints.
// When --webhook-url is set, Reloader sends HTTP POST requests instead of modifying workloads.
package webhook
import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"time"
"github.com/go-logr/logr"
httputil "github.com/stakater/Reloader/internal/pkg/http"
)
// Payload represents the data sent to the webhook endpoint.
type Payload struct {
Kind string `json:"kind"`
Namespace string `json:"namespace"`
ResourceName string `json:"resourceName"`
ResourceType string `json:"resourceType"`
Hash string `json:"hash"`
Timestamp time.Time `json:"timestamp"`
// Workloads contains the list of workloads that would be reloaded.
Workloads []WorkloadInfo `json:"workloads"`
}
// WorkloadInfo describes a workload that would be reloaded.
type WorkloadInfo struct {
Kind string `json:"kind"`
Name string `json:"name"`
Namespace string `json:"namespace"`
}
// Client sends reload notifications to webhook endpoints.
type Client struct {
httpClient *http.Client
url string
log logr.Logger
}
// NewClient creates a new webhook client.
func NewClient(url string, log logr.Logger) *Client {
return &Client{
httpClient: httputil.NewDefaultClient(),
url: url,
log: log,
}
}
// Send posts the payload to the configured webhook URL.
func (c *Client) Send(ctx context.Context, payload Payload) error {
if c.url == "" {
return nil
}
data, err := json.Marshal(payload)
if err != nil {
return fmt.Errorf("marshaling payload: %w", err)
}
req, err := http.NewRequestWithContext(ctx, http.MethodPost, c.url, bytes.NewReader(data))
if err != nil {
return fmt.Errorf("creating request: %w", err)
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("User-Agent", "Reloader/2.0")
resp, err := c.httpClient.Do(req)
if err != nil {
return fmt.Errorf("sending request: %w", err)
}
defer func() { _ = resp.Body.Close() }()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return fmt.Errorf("webhook returned status %d", resp.StatusCode)
}
c.log.V(1).Info("webhook notification sent",
"url", c.url,
"resourceType", payload.ResourceType,
"resourceName", payload.ResourceName,
"workloadCount", len(payload.Workloads),
)
return nil
}
// IsConfigured returns true if the webhook URL is set.
func (c *Client) IsConfigured() bool {
return c != nil && c.url != ""
}