Skip to content

Commit 0eeeb44

Browse files
authored
add webhook config to Terminator (#641)
1 parent 75d27af commit 0eeeb44

File tree

21 files changed

+706
-25
lines changed

21 files changed

+706
-25
lines changed

src/api/v1alpha1/terminator_logging.go

+19
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,22 @@ func (e EventsSpec) MarshalLogObject(enc zapcore.ObjectEncoder) error {
5757
enc.AddString("stateChange", e.StateChange)
5858
return nil
5959
}
60+
61+
func (w WebhookSpec) MarshalLogObject(enc zapcore.ObjectEncoder) error {
62+
enc.AddString("url", w.URL)
63+
enc.AddString("proxyURL", w.ProxyURL)
64+
65+
enc.AddArray("headers", zapcore.ArrayMarshalerFunc(func(enc zapcore.ArrayEncoder) error {
66+
for _, header := range w.Headers {
67+
enc.AppendObject(zapcore.ObjectMarshalerFunc(func(enc zapcore.ObjectEncoder) error {
68+
enc.AddString("name", header.Name)
69+
enc.AddString("value", header.Value)
70+
return nil
71+
}))
72+
}
73+
return nil
74+
}))
75+
76+
enc.AddString("template", w.Template)
77+
return nil
78+
}

src/api/v1alpha1/terminator_types.go

+15
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ type TerminatorSpec struct {
3434
SQS SQSSpec `json:"sqs,omitempty"`
3535
Drain DrainSpec `json:"drain,omitempty"`
3636
Events EventsSpec `json:"events,omitempty"`
37+
Webhook WebhookSpec `json:"webhook,omitempty"`
3738
}
3839

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

77+
// HeaderSpec defines the HTTP headers to include with webhook notifications.
78+
type HeaderSpec struct {
79+
Name string `json:"name,omitempty"`
80+
Value string `json:"value,omitempty"`
81+
}
82+
83+
// WebhookSpec defines the configuration of webhook notifications to send when events are handled.
84+
type WebhookSpec struct {
85+
URL string `json:"url,omitempty"`
86+
ProxyURL string `json:"proxyURL,omitempty"`
87+
Headers []HeaderSpec `json:"headers,omitempty"`
88+
Template string `json:"template,omitempty"`
89+
}
90+
7691
// TerminatorStatus defines the observed state of Terminator
7792
type TerminatorStatus struct {
7893
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster

src/api/v1alpha1/terminator_validation.go

+27
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"context"
2121
"fmt"
2222
"net/url"
23+
"text/template"
2324

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

@@ -44,6 +45,7 @@ func (t *TerminatorSpec) validate() (errs *apis.FieldError) {
4445
t.validateMatchLabels().ViaField("matchLabels"),
4546
t.SQS.validate().ViaField("sqs"),
4647
t.Events.validate().ViaField("events"),
48+
t.Webhook.validate().ViaField("webhook"),
4749
)
4850
}
4951

@@ -78,3 +80,28 @@ func (e *EventsSpec) validate() (errs *apis.FieldError) {
7880
}
7981
return errs
8082
}
83+
84+
func (w *WebhookSpec) validate() (errs *apis.FieldError) {
85+
if _, err := url.Parse(w.URL); err != nil {
86+
errs = errs.Also(apis.ErrInvalidValue(w.URL, "url", err.Error()))
87+
}
88+
89+
if _, err := url.Parse(w.ProxyURL); err != nil && w.ProxyURL != "" {
90+
errs = errs.Also(apis.ErrInvalidValue(w.ProxyURL, "proxyURL", "must be a valid URL"))
91+
}
92+
93+
for i, h := range w.Headers {
94+
if h.Name != "" {
95+
continue
96+
}
97+
errs = errs.Also(apis.ErrInvalidValue(h.Name, "name", "must not be empty").ViaFieldIndex("headers", i))
98+
}
99+
100+
if w.Template == "" {
101+
errs = errs.Also(apis.ErrInvalidValue(w.Template, "template", "must not be empty"))
102+
} else if _, err := template.New("Validate").Parse(w.Template); err != nil {
103+
errs = errs.Also(apis.ErrInvalidValue(w.Template, "template", err.Error()))
104+
}
105+
106+
return errs
107+
}

src/charts/aws-node-termination-handler-2/templates/node.k8s.aws_terminators.yaml

+36
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,42 @@ spec:
136136
- Cordon
137137
- NoAction
138138
default: CordonAndDrain
139+
webhook:
140+
description: Send notification of handled events.
141+
type: object
142+
properties:
143+
url:
144+
description: URL to send notifications.
145+
type: string
146+
proxyURL:
147+
description: Proxy URL to use to send notifications.
148+
type: string
149+
headers:
150+
description: HTTP headers to include when sending notifications.
151+
type: array
152+
items:
153+
type: object
154+
properties:
155+
name:
156+
description: Header name.
157+
type: string
158+
value:
159+
description: Header value.
160+
type: string
161+
required:
162+
- name
163+
- value
164+
{{- with .Values.terminator.defaults.webhook.headers }}
165+
default:
166+
{{- toYaml . | nindent 22 }}
167+
{{- end }}
168+
template:
169+
description: Used to generate the request payload.
170+
type: string
171+
{{- with .Values.terminator.defaults.webhook.template }}
172+
default: |
173+
{{ . }}
174+
{{- end }}
139175
status:
140176
description: TerminatorStatus defines the observed state of Terminator
141177
type: object

src/charts/aws-node-termination-handler-2/values.yaml

+5
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ terminator:
1818
ignoreAllDaemonSets: true
1919
deleteEmptyDirData: true
2020
timeoutSeconds: 120
21+
webhook:
22+
headers:
23+
- name: Content-Type
24+
value: application/json
25+
template: '{"text":"[NTH][Instance Interruption] EventID: {{ .EventID }} - Kind: {{ .Kind }} - Instance: {{ .InstanceID }} - Node: {{ .NodeName }} - Start Time: {{ .StartTime }}"}'
2126

2227
aws:
2328
# AWS region to use in API calls.

src/cmd/controller/main.go

+4
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ import (
5757
"github.com/aws/aws-node-termination-handler/pkg/sqsmessage"
5858
"github.com/aws/aws-node-termination-handler/pkg/terminator"
5959
terminatoradapter "github.com/aws/aws-node-termination-handler/pkg/terminator/adapter"
60+
"github.com/aws/aws-node-termination-handler/pkg/webhook"
6061

6162
"github.com/aws/aws-sdk-go/aws"
6263
"github.com/aws/aws-sdk-go/aws/ec2metadata"
@@ -172,6 +173,9 @@ func main() {
172173
Drainer: kubectlcordondrainer.DefaultDrainer,
173174
},
174175
},
176+
WebhookClientBuilder: terminatoradapter.WebhookClientBuilder(
177+
webhook.ClientBuilder(webhook.NewHttpClientDo).NewClient,
178+
),
175179
}
176180
if err = rec.BuildController(
177181
ctrl.NewControllerManagedBy(mgr).

src/pkg/event/asgterminate/v1/handler.go

+11-2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package v1
1818

1919
import (
2020
"context"
21+
"time"
2122

2223
"github.com/aws/aws-node-termination-handler/pkg/event/asgterminate/lifecycleaction"
2324
"github.com/aws/aws-node-termination-handler/pkg/terminator"
@@ -31,8 +32,8 @@ type EC2InstanceTerminateLifecycleAction struct {
3132
AWSEvent
3233
}
3334

34-
func (EC2InstanceTerminateLifecycleAction) Kind() terminator.EventKind {
35-
return terminator.EventKinds.AutoScalingTermination
35+
func (e EC2InstanceTerminateLifecycleAction) EventID() string {
36+
return e.AWSEvent.ID
3637
}
3738

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

52+
func (EC2InstanceTerminateLifecycleAction) Kind() terminator.EventKind {
53+
return terminator.EventKinds.AutoScalingTermination
54+
}
55+
5156
func (e EC2InstanceTerminateLifecycleAction) MarshalLogObject(enc zapcore.ObjectEncoder) error {
5257
zap.Inline(e.AWSEvent).AddTo(enc)
5358
return nil
5459
}
60+
61+
func (e EC2InstanceTerminateLifecycleAction) StartTime() time.Time {
62+
return e.AWSEvent.Time
63+
}

src/pkg/event/asgterminate/v2/handler.go

+11-2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package v2
1818

1919
import (
2020
"context"
21+
"time"
2122

2223
"github.com/aws/aws-node-termination-handler/pkg/event/asgterminate/lifecycleaction"
2324
"github.com/aws/aws-node-termination-handler/pkg/terminator"
@@ -31,8 +32,8 @@ type EC2InstanceTerminateLifecycleAction struct {
3132
AWSEvent
3233
}
3334

34-
func (EC2InstanceTerminateLifecycleAction) Kind() terminator.EventKind {
35-
return terminator.EventKinds.AutoScalingTermination
35+
func (e EC2InstanceTerminateLifecycleAction) EventID() string {
36+
return e.AWSEvent.ID
3637
}
3738

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

52+
func (EC2InstanceTerminateLifecycleAction) Kind() terminator.EventKind {
53+
return terminator.EventKinds.AutoScalingTermination
54+
}
55+
5156
func (e EC2InstanceTerminateLifecycleAction) MarshalLogObject(enc zapcore.ObjectEncoder) error {
5257
zap.Inline(e.AWSEvent).AddTo(enc)
5358
return nil
5459
}
60+
61+
func (e EC2InstanceTerminateLifecycleAction) StartTime() time.Time {
62+
return e.AWSEvent.Time
63+
}

src/pkg/event/noop.go

+11-2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package event
1818

1919
import (
2020
"context"
21+
"time"
2122

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

@@ -27,8 +28,8 @@ import (
2728

2829
type noop AWSMetadata
2930

30-
func (noop) Kind() terminator.EventKind {
31-
return terminator.EventKinds.Noop
31+
func (noop) EventID() string {
32+
return ""
3233
}
3334

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

43+
func (noop) Kind() terminator.EventKind {
44+
return terminator.EventKinds.Noop
45+
}
46+
4247
func (n noop) MarshalLogObject(enc zapcore.ObjectEncoder) error {
4348
zap.Inline(AWSMetadata(n)).AddTo(enc)
4449
return nil
4550
}
51+
52+
func (noop) StartTime() time.Time {
53+
return time.Now()
54+
}

src/pkg/event/rebalancerecommendation/v0/handler.go

+11-2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package v0
1818

1919
import (
2020
"context"
21+
"time"
2122

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

@@ -27,8 +28,8 @@ import (
2728

2829
type EC2InstanceRebalanceRecommendation AWSEvent
2930

30-
func (EC2InstanceRebalanceRecommendation) Kind() terminator.EventKind {
31-
return terminator.EventKinds.RebalanceRecommendation
31+
func (e EC2InstanceRebalanceRecommendation) EventID() string {
32+
return e.ID
3233
}
3334

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

43+
func (EC2InstanceRebalanceRecommendation) Kind() terminator.EventKind {
44+
return terminator.EventKinds.RebalanceRecommendation
45+
}
46+
4247
func (e EC2InstanceRebalanceRecommendation) MarshalLogObject(enc zapcore.ObjectEncoder) error {
4348
zap.Inline(AWSEvent(e)).AddTo(enc)
4449
return nil
4550
}
51+
52+
func (e EC2InstanceRebalanceRecommendation) StartTime() time.Time {
53+
return e.Time
54+
}

src/pkg/event/scheduledchange/v1/handler.go

+11-2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package v1
1818

1919
import (
2020
"context"
21+
"time"
2122

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

@@ -27,8 +28,8 @@ import (
2728

2829
type AWSHealthEvent AWSEvent
2930

30-
func (AWSHealthEvent) Kind() terminator.EventKind {
31-
return terminator.EventKinds.ScheduledChange
31+
func (e AWSHealthEvent) EventID() string {
32+
return e.ID
3233
}
3334

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

47+
func (AWSHealthEvent) Kind() terminator.EventKind {
48+
return terminator.EventKinds.ScheduledChange
49+
}
50+
4651
func (e AWSHealthEvent) MarshalLogObject(enc zapcore.ObjectEncoder) error {
4752
zap.Inline(AWSEvent(e)).AddTo(enc)
4853
return nil
4954
}
55+
56+
func (e AWSHealthEvent) StartTime() time.Time {
57+
return e.Time
58+
}

src/pkg/event/spotinterruption/v1/handler.go

+11-2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package v1
1818

1919
import (
2020
"context"
21+
"time"
2122

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

@@ -27,8 +28,8 @@ import (
2728

2829
type EC2SpotInstanceInterruptionWarning AWSEvent
2930

30-
func (EC2SpotInstanceInterruptionWarning) Kind() terminator.EventKind {
31-
return terminator.EventKinds.SpotInterruption
31+
func (e EC2SpotInstanceInterruptionWarning) EventID() string {
32+
return e.ID
3233
}
3334

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

43+
func (EC2SpotInstanceInterruptionWarning) Kind() terminator.EventKind {
44+
return terminator.EventKinds.SpotInterruption
45+
}
46+
4247
func (e EC2SpotInstanceInterruptionWarning) MarshalLogObject(enc zapcore.ObjectEncoder) error {
4348
zap.Inline(AWSEvent(e)).AddTo(enc)
4449
return nil
4550
}
51+
52+
func (e EC2SpotInstanceInterruptionWarning) StartTime() time.Time {
53+
return e.Time
54+
}

0 commit comments

Comments
 (0)