Skip to content
This repository was archived by the owner on Jan 19, 2024. It is now read-only.

Commit 4d93045

Browse files
feat: Use cloudevent for alerting endpoint (#270)
* feat: Use cloudevent for alerting endpoint Signed-off-by: TannerGabriel <[email protected]> * Use NewHTTPReceiveHandler to handle cloudevents and Prometheus alerts Signed-off-by: TannerGabriel <[email protected]>
1 parent 827a4cc commit 4d93045

File tree

3 files changed

+53
-74
lines changed

3 files changed

+53
-74
lines changed

chart/templates/deployment.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,8 @@ spec:
107107
value: 'sh.keptn.event.monitoring.configure,sh.keptn.event.configure-monitoring.triggered,sh.keptn.event.get-sli.triggered'
108108
- name: PUBSUB_RECIPIENT
109109
value: '127.0.0.1'
110+
- name: PUBSUB_RECIPIENT_PATH
111+
value: '/events'
110112
- name: STAGE_FILTER
111113
value: "{{ .Values.distributor.stageFilter }}"
112114
- name: PROJECT_FILTER

main.go

Lines changed: 49 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
package main
22

33
import (
4-
"bytes"
54
"context"
65
"encoding/json"
76
"fmt"
7+
"github.com/google/uuid"
88
"github.com/keptn-contrib/prometheus-service/eventhandling"
99
"github.com/keptn-contrib/prometheus-service/utils"
1010
keptn "github.com/keptn/go-utils/pkg/lib"
@@ -14,27 +14,16 @@ import (
1414
"os"
1515

1616
cloudevents "github.com/cloudevents/sdk-go/v2"
17-
"github.com/google/uuid"
1817
"github.com/kelseyhightower/envconfig"
1918
keptncommon "github.com/keptn/go-utils/pkg/lib/keptn"
2019
keptnv2 "github.com/keptn/go-utils/pkg/lib/v0_2_0"
2120
)
2221

23-
type ceTest struct {
24-
Specversion string `json:"specversion" yaml:"specversion"`
25-
}
26-
2722
var (
2823
env utils.EnvConfig
2924
)
3025

3126
func main() {
32-
/**
33-
Note that prometheus-service requires to open multiple ports:
34-
* 8080 (default port; exposed) - acts as the ingest for prometheus alerts, and also proxies CloudEvents to port 8082
35-
* 8081 (Keptn distributor) - Port that keptn/distributor is listening too (default port for Keptn distributor)
36-
* 8082 (CloudEvents; env.Port) - Port that the CloudEvents sdk is listening to; this port is not exposed, but will be used for internal communication
37-
*/
3827
logger := keptncommon.NewLogger("", "", utils.ServiceName)
3928

4029
env = utils.EnvConfig{}
@@ -46,12 +35,6 @@ func main() {
4635
logger.Debug(fmt.Sprintf("Configuration service: %s", env.ConfigurationServiceURL))
4736
logger.Debug(fmt.Sprintf("Port: %d, Path: %s", env.Port, env.Path))
4837

49-
// listen on port 8080 for any HTTP request (cloudevents are also handled, but forwarded to env.Port internally)
50-
logger.Debug("Starting server on port 8080...")
51-
http.HandleFunc("/", Handler)
52-
http.HandleFunc("/health", HealthHandler)
53-
go http.ListenAndServe(":8080", nil)
54-
5538
// start internal CloudEvents handler (on port env.Port)
5639
os.Exit(_main(env))
5740
}
@@ -60,17 +43,19 @@ func _main(env utils.EnvConfig) int {
6043
ctx := context.Background()
6144
ctx = cloudevents.WithEncodingStructured(ctx)
6245

63-
p, err := cloudevents.NewHTTP(cloudevents.WithPath(env.Path), cloudevents.WithPort(env.Port))
46+
p, err := cloudevents.NewHTTP()
6447
if err != nil {
65-
log.Fatalf("failed to create client, %v", err)
48+
log.Fatalf("failed to create protocol: %s", err.Error())
6649
}
67-
// Create CloudEvents client
68-
c, err := cloudevents.NewClient(p)
50+
51+
ceHandler, err := cloudevents.NewHTTPReceiveHandler(ctx, p, gotEvent)
6952
if err != nil {
70-
log.Fatalf("failed to create client, %v", err)
53+
log.Fatalf("failed to create handler: %s", err.Error())
7154
}
72-
// Start CloudEvents receiver
73-
log.Fatal(c.StartReceiver(ctx, gotEvent))
55+
56+
http.HandleFunc("/", HTTPGetHandler)
57+
http.Handle(env.Path, ceHandler)
58+
http.ListenAndServe(":8080", nil)
7459

7560
return 0
7661
}
@@ -80,6 +65,8 @@ func gotEvent(event cloudevents.Event) error {
8065
var shkeptncontext string
8166
_ = event.Context.ExtensionAs("shkeptncontext", &shkeptncontext)
8267

68+
logger := keptncommon.NewLogger(shkeptncontext, "", utils.ServiceName)
69+
8370
// convert v0.1.4 spec monitoring.configure CloudEvent into a v0.2.0 spec configure-monitoring.triggered CloudEvent
8471
if event.Type() == keptn.ConfigureMonitoringEventType {
8572
event.SetType(keptnv2.GetTriggeredEventType(keptnv2.ConfigureMonitoringTaskName))
@@ -91,13 +78,34 @@ func gotEvent(event cloudevents.Event) error {
9178
return fmt.Errorf("could not create Keptn handler: %v", err)
9279
}
9380

94-
logger := keptncommon.NewLogger(shkeptncontext, event.Context.GetID(), utils.ServiceName)
95-
9681
return eventhandling.NewEventHandler(event, logger, keptnHandler).HandleEvent()
9782
}
9883

99-
// HealthHandler provides a basic health check
100-
func HealthHandler(w http.ResponseWriter, r *http.Request) {
84+
// HTTPGetHandler will handle all requests for '/health' and '/ready'
85+
func HTTPGetHandler(w http.ResponseWriter, r *http.Request) {
86+
switch r.URL.Path {
87+
case "/":
88+
shkeptncontext := uuid.New().String()
89+
logger := keptncommon.NewLogger(shkeptncontext, "", utils.ServiceName)
90+
91+
body, err := ioutil.ReadAll(r.Body)
92+
if err != nil {
93+
logger.Error(fmt.Sprintf("Failed to read body from requst: %s", err))
94+
return
95+
}
96+
97+
eventhandling.ProcessAndForwardAlertEvent(w, body, logger, shkeptncontext)
98+
case "/health":
99+
healthEndpointHandler(w, r)
100+
case "/ready":
101+
healthEndpointHandler(w, r)
102+
default:
103+
endpointNotFoundHandler(w, r)
104+
}
105+
}
106+
107+
// HealthHandler rerts a basic health check back
108+
func healthEndpointHandler(w http.ResponseWriter, r *http.Request) {
101109
type StatusBody struct {
102110
Status string `json:"status"`
103111
}
@@ -114,51 +122,20 @@ func HealthHandler(w http.ResponseWriter, r *http.Request) {
114122
}
115123
}
116124

117-
// Handler takes all http request and forwards it to the corresponding event handler (e.g., prometheus alert);
118-
// Note: cloudevents are also forwarded
119-
func Handler(rw http.ResponseWriter, req *http.Request) {
120-
shkeptncontext := uuid.New().String()
121-
logger := keptncommon.NewLogger(shkeptncontext, "", utils.ServiceName)
122-
logger.Debug(fmt.Sprintf("%s %s", req.Method, req.URL))
123-
logger.Debug("Receiving event which will be dispatched")
124-
125-
body, err := ioutil.ReadAll(req.Body)
126-
if err != nil {
127-
logger.Error(fmt.Sprintf("Failed to read body from requst: %s", err))
128-
return
125+
// endpointNotFoundHandler will return 404 for requests
126+
func endpointNotFoundHandler(w http.ResponseWriter, r *http.Request) {
127+
type StatusBody struct {
128+
Status string `json:"status"`
129129
}
130130

131-
// try to deserialize the event to check if it contains specversion
132-
event := ceTest{}
133-
if err = json.Unmarshal(body, &event); err != nil {
134-
logger.Debug("Failed to read body: " + err.Error() + "; content=" + string(body))
135-
return
136-
}
131+
status := StatusBody{Status: "NOT FOUND"}
137132

138-
// check event whether event contains specversion to forward it to 8081; otherwise process it as prometheus alert
139-
if event.Specversion == "" {
140-
// this is a prometheus alert
141-
eventhandling.ProcessAndForwardAlertEvent(rw, body, logger, shkeptncontext)
142-
} else {
143-
// this is a CloudEvent retrieved on port 8080 that needs to be forwarded to 8082 (env.Port)
144-
forwardPath := fmt.Sprintf("http://localhost:%d%s", env.Port, env.Path)
145-
logger.Debug("Forwarding cloudevent to " + forwardPath)
146-
// forward cloudevent to cloudevents lister on env.Port (see main())
147-
proxyReq, err := http.NewRequest(req.Method, forwardPath, bytes.NewReader(body))
148-
proxyReq.Header.Set("Content-Type", "application/cloudevents+json")
149-
resp, err := http.DefaultClient.Do(proxyReq)
150-
if err != nil {
151-
logger.Error("Could not send cloud event: " + err.Error())
152-
return
153-
}
154-
defer resp.Body.Close()
155-
156-
if resp.StatusCode < 200 || resp.StatusCode > 299 {
157-
logger.Error(fmt.Sprintf("Could not process cloud event: Handler returned status %s", resp.Status))
158-
rw.WriteHeader(500)
159-
} else {
160-
logger.Debug("event successfully sent to port 8081")
161-
rw.WriteHeader(201)
162-
}
133+
body, _ := json.Marshal(status)
134+
135+
w.Header().Set("content-type", "application/json")
136+
137+
_, err := w.Write(body)
138+
if err != nil {
139+
log.Println(err)
163140
}
164141
}

utils/env.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ const SliResourceURI = "prometheus/sli.yaml"
99
// EnvConfig holds the configuration of environment variables that this service uses
1010
type EnvConfig struct {
1111
// Port on which to listen for cloudevents
12-
Port int `envconfig:"RCV_PORT" default:"8082"` // Note: must not be 8080 and not 8081
13-
Path string `envconfig:"RCV_PATH" default:"/"`
12+
Port int `envconfig:"RCV_PORT" default:"8080"`
13+
Path string `envconfig:"RCV_PATH" default:"/events"`
1414
ConfigurationServiceURL string `envconfig:"CONFIGURATION_SERVICE" default:""`
1515
PrometheusNamespace string `envconfig:"PROMETHEUS_NS" default:""`
1616
PrometheusConfigMap string `envconfig:"PROMETHEUS_CM" default:""`

0 commit comments

Comments
 (0)