Skip to content

add webhook config to Terminator #641

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

Merged
merged 6 commits into from
May 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions src/api/v1alpha1/terminator_logging.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,22 @@ func (e EventsSpec) MarshalLogObject(enc zapcore.ObjectEncoder) error {
enc.AddString("stateChange", e.StateChange)
return nil
}

func (w WebhookSpec) MarshalLogObject(enc zapcore.ObjectEncoder) error {
enc.AddString("url", w.URL)
enc.AddString("proxyURL", w.ProxyURL)

enc.AddArray("headers", zapcore.ArrayMarshalerFunc(func(enc zapcore.ArrayEncoder) error {
for _, header := range w.Headers {
enc.AppendObject(zapcore.ObjectMarshalerFunc(func(enc zapcore.ObjectEncoder) error {
enc.AddString("name", header.Name)
enc.AddString("value", header.Value)
return nil
}))
}
return nil
}))

enc.AddString("template", w.Template)
return nil
}
15 changes: 15 additions & 0 deletions src/api/v1alpha1/terminator_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ type TerminatorSpec struct {
SQS SQSSpec `json:"sqs,omitempty"`
Drain DrainSpec `json:"drain,omitempty"`
Events EventsSpec `json:"events,omitempty"`
Webhook WebhookSpec `json:"webhook,omitempty"`
}

// SQSSpec defines inputs to SQS "receive messages" requests.
Expand Down Expand Up @@ -73,6 +74,20 @@ type EventsSpec struct {
StateChange Action `json:"stateChange,omitempty"`
}

// HeaderSpec defines the HTTP headers to include with webhook notifications.
type HeaderSpec struct {
Name string `json:"name,omitempty"`
Value string `json:"value,omitempty"`
}

// WebhookSpec defines the configuration of webhook notifications to send when events are handled.
type WebhookSpec struct {
URL string `json:"url,omitempty"`
ProxyURL string `json:"proxyURL,omitempty"`
Headers []HeaderSpec `json:"headers,omitempty"`
Template string `json:"template,omitempty"`
}

// TerminatorStatus defines the observed state of Terminator
type TerminatorStatus struct {
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
Expand Down
27 changes: 27 additions & 0 deletions src/api/v1alpha1/terminator_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"context"
"fmt"
"net/url"
"text/template"

"k8s.io/apimachinery/pkg/util/sets"

Expand All @@ -44,6 +45,7 @@ func (t *TerminatorSpec) validate() (errs *apis.FieldError) {
t.validateMatchLabels().ViaField("matchLabels"),
t.SQS.validate().ViaField("sqs"),
t.Events.validate().ViaField("events"),
t.Webhook.validate().ViaField("webhook"),
)
}

Expand Down Expand Up @@ -78,3 +80,28 @@ func (e *EventsSpec) validate() (errs *apis.FieldError) {
}
return errs
}

func (w *WebhookSpec) validate() (errs *apis.FieldError) {
if _, err := url.Parse(w.URL); err != nil {
errs = errs.Also(apis.ErrInvalidValue(w.URL, "url", err.Error()))
}

if _, err := url.Parse(w.ProxyURL); err != nil && w.ProxyURL != "" {
errs = errs.Also(apis.ErrInvalidValue(w.ProxyURL, "proxyURL", "must be a valid URL"))
}

for i, h := range w.Headers {
if h.Name != "" {
continue
}
errs = errs.Also(apis.ErrInvalidValue(h.Name, "name", "must not be empty").ViaFieldIndex("headers", i))
}

if w.Template == "" {
errs = errs.Also(apis.ErrInvalidValue(w.Template, "template", "must not be empty"))
} else if _, err := template.New("Validate").Parse(w.Template); err != nil {
errs = errs.Also(apis.ErrInvalidValue(w.Template, "template", err.Error()))
}

return errs
}
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,42 @@ spec:
- Cordon
- NoAction
default: CordonAndDrain
webhook:
description: Send notification of handled events.
type: object
properties:
url:
description: URL to send notifications.
type: string
proxyURL:
description: Proxy URL to use to send notifications.
type: string
headers:
description: HTTP headers to include when sending notifications.
type: array
items:
type: object
properties:
name:
description: Header name.
type: string
value:
description: Header value.
type: string
required:
- name
- value
{{- with .Values.terminator.defaults.webhook.headers }}
default:
{{- toYaml . | nindent 22 }}
{{- end }}
template:
description: Used to generate the request payload.
type: string
{{- with .Values.terminator.defaults.webhook.template }}
default: |
{{ . }}
{{- end }}
status:
description: TerminatorStatus defines the observed state of Terminator
type: object
Expand Down
5 changes: 5 additions & 0 deletions src/charts/aws-node-termination-handler-2/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ terminator:
ignoreAllDaemonSets: true
deleteEmptyDirData: true
timeoutSeconds: 120
webhook:
headers:
- name: Content-Type
value: application/json
template: '{"text":"[NTH][Instance Interruption] EventID: {{ .EventID }} - Kind: {{ .Kind }} - Instance: {{ .InstanceID }} - Node: {{ .NodeName }} - Start Time: {{ .StartTime }}"}'

aws:
# AWS region to use in API calls.
Expand Down
4 changes: 4 additions & 0 deletions src/cmd/controller/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ import (
"github.com/aws/aws-node-termination-handler/pkg/sqsmessage"
"github.com/aws/aws-node-termination-handler/pkg/terminator"
terminatoradapter "github.com/aws/aws-node-termination-handler/pkg/terminator/adapter"
"github.com/aws/aws-node-termination-handler/pkg/webhook"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/ec2metadata"
Expand Down Expand Up @@ -172,6 +173,9 @@ func main() {
Drainer: kubectlcordondrainer.DefaultDrainer,
},
},
WebhookClientBuilder: terminatoradapter.WebhookClientBuilder(
webhook.ClientBuilder(webhook.NewHttpClientDo).NewClient,
),
}
if err = rec.BuildController(
ctrl.NewControllerManagedBy(mgr).
Expand Down
13 changes: 11 additions & 2 deletions src/pkg/event/asgterminate/v1/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package v1

import (
"context"
"time"

"github.com/aws/aws-node-termination-handler/pkg/event/asgterminate/lifecycleaction"
"github.com/aws/aws-node-termination-handler/pkg/terminator"
Expand All @@ -31,8 +32,8 @@ type EC2InstanceTerminateLifecycleAction struct {
AWSEvent
}

func (EC2InstanceTerminateLifecycleAction) Kind() terminator.EventKind {
return terminator.EventKinds.AutoScalingTermination
func (e EC2InstanceTerminateLifecycleAction) EventID() string {
return e.AWSEvent.ID
}

func (e EC2InstanceTerminateLifecycleAction) EC2InstanceIDs() []string {
Expand All @@ -48,7 +49,15 @@ func (e EC2InstanceTerminateLifecycleAction) Done(ctx context.Context) (bool, er
})
}

func (EC2InstanceTerminateLifecycleAction) Kind() terminator.EventKind {
return terminator.EventKinds.AutoScalingTermination
}

func (e EC2InstanceTerminateLifecycleAction) MarshalLogObject(enc zapcore.ObjectEncoder) error {
zap.Inline(e.AWSEvent).AddTo(enc)
return nil
}

func (e EC2InstanceTerminateLifecycleAction) StartTime() time.Time {
return e.AWSEvent.Time
}
13 changes: 11 additions & 2 deletions src/pkg/event/asgterminate/v2/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package v2

import (
"context"
"time"

"github.com/aws/aws-node-termination-handler/pkg/event/asgterminate/lifecycleaction"
"github.com/aws/aws-node-termination-handler/pkg/terminator"
Expand All @@ -31,8 +32,8 @@ type EC2InstanceTerminateLifecycleAction struct {
AWSEvent
}

func (EC2InstanceTerminateLifecycleAction) Kind() terminator.EventKind {
return terminator.EventKinds.AutoScalingTermination
func (e EC2InstanceTerminateLifecycleAction) EventID() string {
return e.AWSEvent.ID
}

func (e EC2InstanceTerminateLifecycleAction) EC2InstanceIDs() []string {
Expand All @@ -48,7 +49,15 @@ func (e EC2InstanceTerminateLifecycleAction) Done(ctx context.Context) (bool, er
})
}

func (EC2InstanceTerminateLifecycleAction) Kind() terminator.EventKind {
return terminator.EventKinds.AutoScalingTermination
}

func (e EC2InstanceTerminateLifecycleAction) MarshalLogObject(enc zapcore.ObjectEncoder) error {
zap.Inline(e.AWSEvent).AddTo(enc)
return nil
}

func (e EC2InstanceTerminateLifecycleAction) StartTime() time.Time {
return e.AWSEvent.Time
}
13 changes: 11 additions & 2 deletions src/pkg/event/noop.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package event

import (
"context"
"time"

"github.com/aws/aws-node-termination-handler/pkg/terminator"

Expand All @@ -27,8 +28,8 @@ import (

type noop AWSMetadata

func (noop) Kind() terminator.EventKind {
return terminator.EventKinds.Noop
func (noop) EventID() string {
return ""
}

func (noop) EC2InstanceIDs() []string {
Expand All @@ -39,7 +40,15 @@ func (noop) Done(_ context.Context) (bool, error) {
return true, nil
}

func (noop) Kind() terminator.EventKind {
return terminator.EventKinds.Noop
}

func (n noop) MarshalLogObject(enc zapcore.ObjectEncoder) error {
zap.Inline(AWSMetadata(n)).AddTo(enc)
return nil
}

func (noop) StartTime() time.Time {
return time.Now()
}
13 changes: 11 additions & 2 deletions src/pkg/event/rebalancerecommendation/v0/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package v0

import (
"context"
"time"

"github.com/aws/aws-node-termination-handler/pkg/terminator"

Expand All @@ -27,8 +28,8 @@ import (

type EC2InstanceRebalanceRecommendation AWSEvent

func (EC2InstanceRebalanceRecommendation) Kind() terminator.EventKind {
return terminator.EventKinds.RebalanceRecommendation
func (e EC2InstanceRebalanceRecommendation) EventID() string {
return e.ID
}

func (e EC2InstanceRebalanceRecommendation) EC2InstanceIDs() []string {
Expand All @@ -39,7 +40,15 @@ func (EC2InstanceRebalanceRecommendation) Done(_ context.Context) (bool, error)
return false, nil
}

func (EC2InstanceRebalanceRecommendation) Kind() terminator.EventKind {
return terminator.EventKinds.RebalanceRecommendation
}

func (e EC2InstanceRebalanceRecommendation) MarshalLogObject(enc zapcore.ObjectEncoder) error {
zap.Inline(AWSEvent(e)).AddTo(enc)
return nil
}

func (e EC2InstanceRebalanceRecommendation) StartTime() time.Time {
return e.Time
}
13 changes: 11 additions & 2 deletions src/pkg/event/scheduledchange/v1/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package v1

import (
"context"
"time"

"github.com/aws/aws-node-termination-handler/pkg/terminator"

Expand All @@ -27,8 +28,8 @@ import (

type AWSHealthEvent AWSEvent

func (AWSHealthEvent) Kind() terminator.EventKind {
return terminator.EventKinds.ScheduledChange
func (e AWSHealthEvent) EventID() string {
return e.ID
}

func (e AWSHealthEvent) EC2InstanceIDs() []string {
Expand All @@ -43,7 +44,15 @@ func (AWSHealthEvent) Done(_ context.Context) (bool, error) {
return false, nil
}

func (AWSHealthEvent) Kind() terminator.EventKind {
return terminator.EventKinds.ScheduledChange
}

func (e AWSHealthEvent) MarshalLogObject(enc zapcore.ObjectEncoder) error {
zap.Inline(AWSEvent(e)).AddTo(enc)
return nil
}

func (e AWSHealthEvent) StartTime() time.Time {
return e.Time
}
13 changes: 11 additions & 2 deletions src/pkg/event/spotinterruption/v1/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package v1

import (
"context"
"time"

"github.com/aws/aws-node-termination-handler/pkg/terminator"

Expand All @@ -27,8 +28,8 @@ import (

type EC2SpotInstanceInterruptionWarning AWSEvent

func (EC2SpotInstanceInterruptionWarning) Kind() terminator.EventKind {
return terminator.EventKinds.SpotInterruption
func (e EC2SpotInstanceInterruptionWarning) EventID() string {
return e.ID
}

func (e EC2SpotInstanceInterruptionWarning) EC2InstanceIDs() []string {
Expand All @@ -39,7 +40,15 @@ func (EC2SpotInstanceInterruptionWarning) Done(_ context.Context) (bool, error)
return false, nil
}

func (EC2SpotInstanceInterruptionWarning) Kind() terminator.EventKind {
return terminator.EventKinds.SpotInterruption
}

func (e EC2SpotInstanceInterruptionWarning) MarshalLogObject(enc zapcore.ObjectEncoder) error {
zap.Inline(AWSEvent(e)).AddTo(enc)
return nil
}

func (e EC2SpotInstanceInterruptionWarning) StartTime() time.Time {
return e.Time
}
Loading