Documentation
¶
Overview ¶
Package webhooks is the outgoing webhook (C-15): the request templates of the events mode with their Secrets, the Signing secret in the style of the Standard Webhooks specification, the version 1 body of an event with its Mentions as data, and the adapter that sends an event through internal/outbound under the outbound address policy and the Destination's proxy and maps the answer to a delivery outcome. Delivery queues and reconciles the events (ADR-0005); this package never decides when to send.
Index ¶
- Constants
- Variables
- func Mapping(status int, _ time.Duration, hasRetryAfter bool) outbound.Outcome
- func NewSigningSecret() logging.Secret
- func SecretRefs(src string) []string
- func Sign(secrets []logging.Secret, id string, at time.Time, body []byte) (string, error)
- func ValidSecretName(name string) bool
- func Validate(s *templates.Sandbox, base string, c *EventsConfig) error
- func ValidateTemplate(s *templates.Sandbox, base string, c *TemplateConfig, now time.Time) error
- func Verify(secret logging.Secret, id, timestamp string, body []byte, header string) bool
- type Actor
- type Adapter
- func (a *Adapter) Desired(ctx context.Context, db deliverydb.DBTX, destinationID int64, ...) ([]byte, error)
- func (a *Adapter) LengthLimit() int
- func (a *Adapter) PreviewRequest(src string, sample templates.Data) (string, error)
- func (a *Adapter) Publish(ctx context.Context, c delivery.Call, _ delivery.Message) delivery.Outcome
- func (a *Adapter) Reply(ctx context.Context, c delivery.Call, _ delivery.Root, _ delivery.Message) delivery.Outcome
- func (a *Adapter) SendEvent(ctx context.Context, c delivery.EventCall) delivery.Outcome
- func (a *Adapter) Update(ctx context.Context, c delivery.Call, _ string, _ delivery.Message) delivery.Outcome
- type Alert
- type AlertGroup
- type Body
- type Config
- type Data
- type EventsConfig
- type ExtractionRule
- type FieldError
- type Header
- type Mention
- type MentionTarget
- type Network
- type RequestData
- type RequestTemplate
- type Requester
- type RouteRef
- type Secret
- type SecretValues
- type Secrets
- type Service
- func (s *Service) DeleteSecret(ctx context.Context, r Requester, publicID string, version *int64, name string) error
- func (s *Service) EventBodies(ctx context.Context, tx groups.DBTX, in delivery.EventSource) (delivery.BodyFunc, error)
- func (s *Service) GenerateSigningSecret(ctx context.Context, r Requester, publicID string) (logging.Secret, SigningStatus, error)
- func (s *Service) ListSecrets(ctx context.Context, publicID string) (Secrets, error)
- func (s *Service) RetirePreviousSigningSecret(ctx context.Context, r Requester, publicID string) error
- func (s *Service) SetSecret(ctx context.Context, r Requester, publicID string, version *int64, name string, ...) (Secret, int64, error)
- func (s *Service) SigningStatus(ctx context.Context, publicID string) (SigningStatus, error)
- type SigningStatus
- type Store
- type TemplateConfig
- type TestData
- type Tester
- type TxQueries
- type User
- type Warning
- type Writer
Constants ¶
const ( ModeEvents = "events" ModeTemplate = "template" ModeBoth = "both" )
The modes of an outgoing webhook Destination (C-15.FR-1).
const ( WarningPreviousSigningSecret = "previous_signing_secret_active" WarningLiteralCredential = "literal_credential" //nolint:gosec // G101: the name of a warning, not a credential )
The kinds of DestinationWarning (C-15.FR-5, FR-10).
const ( CodeRequired = "required" CodeInvalidFormat = "invalid_format" CodeTooLong = "too_long" CodeReserved = "reserved" )
The codes of FieldError.
const ( ActionUpdated = "destination.updated" ResourceDestination = "destination" HintDestination = "destination" )
The Audit log action, resource type and live hint of a change of the Secrets of a Destination.
const ( HeaderID = "webhook-id" HeaderTimestamp = "webhook-timestamp" HeaderSignature = "webhook-signature" )
The headers of a signed request.
const ( FieldSigningSecret = "destinations.signing_secret" FieldPreviousSigningSecret = "destinations.previous_signing_secret" FieldSecret = "destination_secrets.value" FieldProxyPassword = "destinations.proxy_password" )
The keyring fields of the secrets of an outgoing webhook (encrypted_values).
const ( ActionSigningSecretGenerated = "destination.signing_secret_generated" ActionSigningSecretRetired = "destination.signing_secret_retired" )
The Audit log actions of the Signing secret.
const ( RequestCreate = "create" RequestUpdate = "update" RequestOpenThread = "open_thread" RequestReplyInThread = "reply_in_thread" RequestEvents = "events" )
The requests of the template mode (C-15.FR-3), and the request of the events mode as the logs name it.
const ( StepEvent = "event" StepCreate = RequestCreate )
The steps of a test and the items of a preview.
const BodyVersion = 1
BodyVersion is the version of the body of events-mode requests (C-15.FR-2): new event names and new fields are added within it; removing a field or changing its meaning needs a new version.
const EventTest = "test"
EventTest is the event of a Destination test.
const (
SigningSecretPrefix = "whsec_"
)
The Signing secret (C-15.FR-5) in the style of the Standard Webhooks specification: whsec_ and 32 random bytes in base64; the HMAC-SHA256 key is the decoded bytes. Each request carries webhook-id, webhook-timestamp (Unix seconds of the real clock) and webhook-signature, `v1,` and the base64 HMAC of "{id}.{timestamp}.{body}", one per secret that still signs, separated by a space.
const TypeWebhook = "webhook"
TypeWebhook is the Destination type of an outgoing webhook.
Variables ¶
var ( // ErrNotFound is a Destination that does not exist in the Organization or is deleted. ErrNotFound = errors.New("no such destination") // ErrNotWebhook is a Destination that is not an outgoing webhook (not_webhook_destination). ErrNotWebhook = errors.New("the destination is not an outgoing webhook") // ErrNoSecret is a Secret the Destination does not have. ErrNoSecret = errors.New("no such secret") // ErrVersionMismatch is an If-Match that names another version of the Destination's Secrets. ErrVersionMismatch = errors.New("the secrets changed since they were read") )
Functions ¶
func Mapping ¶
Mapping is the response mapping of outgoing webhooks (C-15.FR-6): 2xx is delivered; 429 or 503 with Retry-After is a RetryAfter; 408 and every other 5xx are Transient; 401, 403, 404 and 410 are Fatal; 400, 413, 422 and every other answer are unknown.
func NewSigningSecret ¶
NewSigningSecret is a new random Signing secret.
func SecretRefs ¶
SecretRefs are the names of the Secrets src reads as `.Secrets.<name>`, `$.Secrets.<name>` or `index .Secrets "<name>"`, sorted and without duplicates; a template that does not parse reads none.
func Sign ¶
Sign is the value of webhook-signature for the request id sent at the Unix time at with body: a v1 signature with each secret, in order, separated by a space.
func ValidSecretName ¶
ValidSecretName reports whether name can name a Secret.
func Validate ¶
func Validate(s *templates.Sandbox, base string, c *EventsConfig) error
Validate checks the request of the events mode on save (C-15.FR-1): the URL and every header parse in the sandbox and run on a dry run in which every Secret they read has a placeholder value, the URL then is an absolute http or https URL, and every header name is a valid name that Muster does not set itself. The URL and header values are trimmed of surrounding space; base is the JSON Pointer of the request in the body, such as /events.
func ValidateTemplate ¶
ValidateTemplate checks the requests of the template mode on save (C-15.FR-1, FR-3): "create" and "update" are set, each request has a method of RequestTemplate, a URL, at most maxHeaders headers with valid names that Muster does not set itself, and extraction rules only on "create" and "open thread", each with a template identifier as its name, unique among them, and a valid JSONPath; every template parses and runs on a dry run with the example, its Secrets and the values of the extraction rules it may read filled with example-<name>, after which the URL is an absolute http or https URL. The fields are trimmed of surrounding space; base is the JSON Pointer of the requests in the body, such as /template, and now the business time the example's Alerts started ten minutes before.
func Verify ¶
Verify reports whether one of the signatures of header, the value of webhook-signature, is the v1 signature of the request id sent at timestamp with body under secret; the comparison takes constant time. A receiving endpoint also refuses a timestamp far from their clock, which this does not check.
Types ¶
type Actor ¶
type Actor struct {
Kind string `json:"kind"`
ID *string `json:"id,omitempty"`
Name *string `json:"name,omitempty"`
Login *string `json:"login,omitempty"`
TokenName *string `json:"token_name,omitempty"`
Transport string `json:"transport"`
Reason *string `json:"reason,omitempty"`
}
Actor is who made the change of an event (WebhookActor): a User or a Service account with the token and the Transport, or Muster itself (`system`) with the reason.
type Adapter ¶
type Adapter struct {
Service *Service
Network Network
Sandbox *templates.Sandbox
// contains filtered or unexported fields
}
Adapter sends the requests of outgoing webhooks for delivery (C-15.FR-2, FR-3, FR-5, FR-6, FR-8): the events-mode requests, and in the template mode "create", "update", "open thread" and "reply in thread" as the adapter of the Destination type webhook, which delivery reconciles like a messenger's (ADR-0005). It renders the request templates with the Destination's Secrets in the sandbox, signs the body with every Signing secret that still signs, sends it through internal/outbound with the Destination's proxy in the client class of the call, maps the answer to a delivery outcome and extracts the values of the response. Every secret of the Destination is registered for redaction, so no error it returns carries one. It also renders the Desired state of the template mode and previews request templates.
func (*Adapter) Desired ¶
func (a *Adapter) Desired(ctx context.Context, db deliverydb.DBTX, destinationID int64, st delivery.RequestState) ( []byte, error)
Desired renders, reading the Destination destinationID through db, the "update" request of its template mode for the Desired state st and returns its hash (delivery.Requests): the Secrets as [redacted] and the extracted values as example-<name>, so that neither a new Secret nor a new value changes it. A request that does not render hashes the state and the error, so that each new state makes the call that fails; one without request templates, the state.
func (*Adapter) LengthLimit ¶
LengthLimit is the longest output of a template: the receiver's own limits are unknown.
func (*Adapter) PreviewRequest ¶
PreviewRequest renders one request template src with the template data of a sample (previewTemplate of the kind webhook_request, C-12.FR-4): as a Loud message with a Mention of each kind, its Secrets as [redacted] and the extracted values it reads as example-<name>. A template that fails is the *templates.Error of its position.
func (*Adapter) Publish ¶
func (a *Adapter) Publish(ctx context.Context, c delivery.Call, _ delivery.Message) delivery.Outcome
Publish sends "create" of the template mode for a new Root message or Storm summary (C-15.FR-3): Loud with its Mention targets as delivery decided; the values its extraction rules find are returned, the first rule's as the message id. The Message is not used: the request templates render the Desired state of the call.
func (*Adapter) Reply ¶
func (a *Adapter) Reply(ctx context.Context, c delivery.Call, _ delivery.Root, _ delivery.Message) delivery.Outcome
Reply sends "reply in thread" of the template mode for a lifecycle event whose messenger form is a Thread reply, with the latest Desired state; "open thread", when the Destination has one, runs first once per Root message and adds the values it extracts, which the reply reads. A failure of "open thread" is the outcome and sends no reply; once it ran, the outcome says so whatever the reply came to. Without "reply in thread" nothing is sent.
func (*Adapter) SendEvent ¶
SendEvent sends one events-mode request (C-15.FR-2): POST with the body of the event as application/json, the headers of the Destination and webhook-id, webhook-timestamp and webhook-signature. A URL or header template that fails, or reads a Secret the Destination does not have, sends nothing and is a template error.
func (*Adapter) Update ¶
func (a *Adapter) Update(ctx context.Context, c delivery.Call, _ string, _ delivery.Message) delivery.Outcome
Update sends "update" of the template mode with the latest Desired state, so that changes made while a request was pending collapse into it; the final edit carries `.Final`.
type Alert ¶
type Alert struct {
Fingerprint string `json:"fingerprint"`
Status string `json:"status"`
Labels map[string]string `json:"labels"`
Annotations map[string]string `json:"annotations"`
StartsAt time.Time `json:"starts_at"`
ResolvedAt *time.Time `json:"resolved_at"`
ResolveReason *string `json:"resolve_reason"`
GeneratorURL *string `json:"generator_url"`
}
Alert is one Alert of the Alert Group, by fingerprint (WebhookAlert).
type AlertGroup ¶
type AlertGroup struct {
Number int64 `json:"number"`
ID string `json:"id"`
Title string `json:"title"`
Summary *string `json:"summary"`
Status string `json:"status"`
Owner *User `json:"owner,omitempty"`
Route RouteRef `json:"route"`
Urgent bool `json:"urgent"`
SeverityLevel string `json:"severity_level"`
StartedAt time.Time `json:"started_at"`
ResolvedAt *time.Time `json:"resolved_at"`
SnoozeUntil *time.Time `json:"snooze_until"`
ReopenCount int64 `json:"reopen_count"`
URL string `json:"url"`
}
AlertGroup is the state of the Alert Group when the event was recorded (WebhookAlertGroup).
type Body ¶
type Body struct {
Version int `json:"version"`
Event string `json:"event"`
Notify bool `json:"notify"`
Mentions []MentionTarget `json:"mentions"`
Sequence int64 `json:"sequence"`
OccurredAt time.Time `json:"occurred_at"`
Actor Actor `json:"actor"`
AlertGroup AlertGroup `json:"alert_group"`
Alerts []Alert `json:"alerts"`
// Test is set only on the test event of a Destination test (C-16.FR-1), which belongs to no Alert Group's order.
Test bool `json:"test,omitempty"`
}
Body is the body of an events-mode request (OutgoingWebhookEvent), rendered when the event is queued, so that a retry hours later still carries the state the event described.
type Config ¶
type Config struct {
Store Store
Writer Writer
Keyring *keyring.Keyring
Audit *audit.Writer
Business clock.Clock
PublicURL string
}
Config is what a Service needs: its queries, the Keyring that encrypts and opens the secrets, the Audit log, the business clock that dates the changes, and for the adapter and the bodies of events, MUSTER_PUBLIC_URL and the network of its outbound clients.
type Data ¶
type Data struct {
Secrets SecretValues
}
Data is what the request templates of the events mode read: the Destination's Secrets, by name.
type EventsConfig ¶
EventsConfig is the request of the events mode as stored in webhook_events_config (WebhookEventsConfig): its URL and its headers, Go templates rendered with `.Secrets`.
func ParseEventsConfig ¶
func ParseEventsConfig(raw []byte) (EventsConfig, error)
ParseEventsConfig reads the stored request of the events mode.
type ExtractionRule ¶
ExtractionRule picks a value from the JSON response of "create" or "open thread" (ExtractionRule): stored by Name per Alert Group and Destination and read by later requests as `.Response.<name>`; Path is a JSONPath (RFC 9535) such as `$.data.id`.
type FieldError ¶
FieldError is a field of the events request or of a Secret that is not valid, at a JSON pointer of the request body, with the 1-based line and column of a template error when known.
func (*FieldError) Error ¶
func (e *FieldError) Error() string
type Header ¶
Header is a header of a request: its name and its value, a Go template that may read `.Secrets`.
type Mention ¶
Mention is a resolved Mention as the request templates read it (`.Mentions`, C-15.FR-11): everyone with the choice of the Mention settings — all, channel or here — a group by its name, or a User by public_id, display name and login.
type MentionTarget ¶
type MentionTarget struct {
Type string `json:"type"`
ID *string `json:"id,omitempty"`
Name *string `json:"name,omitempty"`
Login *string `json:"login,omitempty"`
}
MentionTarget is a resolved Mention as data (WebhookMentionTarget, C-15.FR-11): Muster does not know the markup of the receiving endpoint, so everyone has only its type, a group its name, and a User their public_id, display name and login.
func MentionTargets ¶
func MentionTargets(ts []mentions.Target) []MentionTarget
MentionTargets are the targets of a Loud event as the body carries them, in the order Resolve gave them; none is the empty list, never null.
type Network ¶
type Network struct {
Policy outbound.PolicySource
Log *logging.Logger
Real clock.Clock
Resolver outbound.Resolver
}
Network is what the outbound clients of the adapter need: the outbound address policy, the logger, the real clock of the signatures and the outbound metrics, and a resolver (nil: the system resolver).
type RequestData ¶
type RequestData struct {
Status string
Alerts templates.Alerts
GroupLabels templates.KV
CommonLabels templates.KV
CommonAnnotations templates.KV
ExternalURL string
AlertGroup *templates.AlertGroup
Storm *delivery.StormState
Response map[string]string
Event string
Notify bool
Mentions []Mention
Secrets SecretValues `json:"-"`
Final string
}
RequestData is what the request templates of the template mode read (C-15.FR-3, FR-11): Alertmanager's template data and the Alert Group (the data of the Root message templates, alert data unescaped, since the receiver's format is unknown), or for a Storm summary the Storm and no Alert Group; the values extracted from earlier responses; the lifecycle event a Thread reply carries; whether the message is Loud; its Mention targets; the Destination's Secrets; and the text of the final edit, empty for another request.
type RequestTemplate ¶
type RequestTemplate struct {
Method string `json:"method"`
URL string `json:"url"`
Headers []Header `json:"headers"`
Body *string `json:"body"`
Extract []ExtractionRule `json:"extract"`
}
RequestTemplate is one request of the template mode (RequestTemplate): its method, and its URL, headers and body, Go templates, with the rules that extract values from its response.
type Requester ¶
Requester is who asks for a change and how: the actor and the Transport of the Audit log entry, and the client address.
type Secret ¶
Secret is the status of a named Secret: its name and the time of its last change; the value is never read back.
type SecretValues ¶
SecretValues are the Destination's Secrets as the request templates read them, by name; printed or encoded whole, as {{ toJson . }} or {{ printf "%v" . }} would, they show as [redacted], so that only a Secret read by its name reaches a request.
func (SecretValues) MarshalJSON ¶
func (SecretValues) MarshalJSON() ([]byte, error)
MarshalJSON hides the values.
func (SecretValues) String ¶
func (SecretValues) String() string
type Secrets ¶
Secrets are the Secrets of a Destination by name, with the version of the Destination, their ETag.
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
Service holds the secrets of the outgoing webhooks of an Organization and reads what their events need.
func (*Service) DeleteSecret ¶
func (s *Service) DeleteSecret(ctx context.Context, r Requester, publicID string, version *int64, name string) error
DeleteSecret deletes the Secret name of the outgoing webhook publicID; a non-nil version must be the current version of its Secrets. A Secret it does not have is ErrNoSecret. It is recorded as destination.updated.
func (*Service) EventBodies ¶
func (s *Service) EventBodies(ctx context.Context, tx groups.DBTX, in delivery.EventSource) (delivery.BodyFunc, error)
EventBodies reads, through the transaction tx of a change, the Alert Group of in as its events carry it — its state after the change, its Owner, its Route and its Alerts — and the actor of the change, and returns the function that renders the body of each of its events (C-15.FR-2).
func (*Service) GenerateSigningSecret ¶
func (s *Service) GenerateSigningSecret(ctx context.Context, r Requester, publicID string) (logging.Secret, SigningStatus, error)
GenerateSigningSecret makes a new Signing secret for the outgoing webhook publicID (C-15.FR-5): the current one becomes the previous one, which still signs until it is retired, and the new one is returned once. It is recorded as destination.signing_secret_generated.
func (*Service) ListSecrets ¶
ListSecrets lists the Secrets of the outgoing webhook publicID (C-15.FR-10); another type is ErrNotWebhook.
func (*Service) RetirePreviousSigningSecret ¶
func (s *Service) RetirePreviousSigningSecret(ctx context.Context, r Requester, publicID string) error
RetirePreviousSigningSecret wipes the previous Signing secret of the outgoing webhook publicID, which signs no more (C-15.FR-5); it is recorded as destination.signing_secret_retired. Without a previous one it changes nothing.
func (*Service) SetSecret ¶
func (s *Service) SetSecret(ctx context.Context, r Requester, publicID string, version *int64, name string, value logging.Secret) (Secret, int64, error)
SetSecret creates or replaces the Secret name of the outgoing webhook publicID with value (C-15.FR-10); a non-nil version must be the current version of its Secrets. It is recorded as destination.updated with the Secret marked changed, never its value, and returns the Secret's status and the new version.
func (*Service) SigningStatus ¶
SigningStatus reads the status of the Signing secrets of the outgoing webhook publicID; another type is ErrNotWebhook.
type SigningStatus ¶
SigningStatus is the status of the Signing secrets of a Destination (SigningSecretStatus).
type Store ¶
type Store interface {
GetWebhookDestination(ctx context.Context, arg dbgen.GetWebhookDestinationParams) (
dbgen.GetWebhookDestinationRow, error)
ListSecrets(ctx context.Context, arg dbgen.ListSecretsParams) ([]dbgen.ListSecretsRow, error)
GetTarget(ctx context.Context, arg dbgen.GetTargetParams) (dbgen.GetTargetRow, error)
ListSecretValues(ctx context.Context, arg dbgen.ListSecretValuesParams) ([]dbgen.ListSecretValuesRow, error)
}
Store are the queries the package runs outside a change.
type TemplateConfig ¶
type TemplateConfig struct {
Create RequestTemplate `json:"create"`
Update RequestTemplate `json:"update"`
OpenThread *RequestTemplate `json:"open_thread"`
ReplyInThread *RequestTemplate `json:"reply_in_thread"`
}
TemplateConfig is the requests of the template mode as stored in webhook_template_config (WebhookTemplateConfig): "create" and "update", and the optional "open thread" and "reply in thread".
func ParseTemplateConfig ¶
func ParseTemplateConfig(raw []byte) (TemplateConfig, error)
ParseTemplateConfig reads the stored requests of the template mode.
func (TemplateConfig) JSON ¶
func (c TemplateConfig) JSON() []byte
JSON is the requests as stored, with empty lists rather than null.
type TestData ¶
type TestData interface {
Data(src *messages.Source) templates.Data
GroupURL(publicID string) string
}
TestData is what a test and a preview read of the renderer of messages, declared by their consumer; *messages.Renderer implements it: the template data of the source and the link to an Alert Group page.
type Tester ¶
type Tester struct {
Adapter *Adapter
Path interface {
Do(ctx context.Context, s delivery.Subject, op delivery.Op) (delivery.Outcome, error)
}
Data TestData
DB dbgen.DBTX
Budget time.Duration
}
Tester is the Destination test and the preview of outgoing webhooks: Adapter sends, on the interactive path Path, each step within Budget (delivery.interactive_budget; zero is delivery.InteractiveBudget); Data renders the source; DB, the main pool, reads the name and login of the person who started a test, which the test event names.
func (*Tester) Preview ¶
func (t *Tester) Preview(ctx context.Context, in delivery.TestInput) ([]delivery.PreviewItem, error)
Preview renders, without sending anything, the requests of the outgoing webhook of in for its source (C-16.FR-4, C-15.FR-10): in the events mode the item event, the body of the first event of an Alert Group; in the template mode the items create, update, open_thread and reply_in_thread of the requests it has, with the values extracted from responses as example-<name>. Every Secret shows as [redacted]; a request whose template fails shows its error as the item's text.
type TxQueries ¶
type TxQueries interface {
LockWebhookDestination(ctx context.Context, arg dbgen.LockWebhookDestinationParams) (
dbgen.LockWebhookDestinationRow, error)
BumpDestinationVersion(ctx context.Context, arg dbgen.BumpDestinationVersionParams) (int64, error)
UpsertSecret(ctx context.Context, arg dbgen.UpsertSecretParams) error
DeleteSecret(ctx context.Context, arg dbgen.DeleteSecretParams) (int64, error)
RotateSigningSecret(ctx context.Context, arg dbgen.RotateSigningSecretParams) (int64, error)
RetirePreviousSigningSecret(ctx context.Context, arg dbgen.RetirePreviousSigningSecretParams) (int64, error)
audit.Store
Notify(ctx context.Context, h db.Hint) error
}
TxQueries are the queries of a change of the secrets of a Destination, with the insert of the Audit log and the live hint, in one transaction.
type Warning ¶
Warning is a DestinationWarning of an outgoing webhook that its request templates give: a literal credential at the JSON Pointer Field.
func TemplateWarnings ¶
func TemplateWarnings(base string, c TemplateConfig) []Warning
TemplateWarnings are the literal_credential warnings of the requests of the template mode (C-15.FR-10), as Warnings gives them for the events mode; base is the JSON Pointer of the requests, such as /template.
func Warnings ¶
func Warnings(base string, c EventsConfig) []Warning
Warnings are the literal_credential warnings of the request of the events mode (C-15.FR-10): a header named Authorization, or a URL whose user information or query holds a literal value, that reads no Secret. base is the JSON Pointer of the request, such as /events.