Skip to content

Commit 63ab3a9

Browse files
authored
Add python support in integration tests (#9)
add python support
1 parent f9ad6c9 commit 63ab3a9

29 files changed

+514
-1204
lines changed

.github/workflows/integration_tests.yml

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ on:
66
push:
77
paths:
88
- 'integration_tests/**'
9+
- '.github/**'
910

1011
jobs:
1112

@@ -47,18 +48,14 @@ jobs:
4748
uses: actions/checkout@v2
4849
with:
4950
repository: DataDog/datadog-agent
50-
#ref: refs/heads/release/lambda-extension-v9
51-
ref: refs/heads/maxday/integration-test # todo : change after merge of current PRs
51+
ref: refs/heads/release/lambda-extension-v9 # todo : change after merge of current PRs
5252
path: "datadog-agent"
5353

5454
- name: Build the layer
5555
run: ./scripts/build_binary_and_layer.sh
5656

57-
5857
- name: Run tests
5958
env:
6059
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
6160
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
62-
# Todo : retrieve this layer version automatically from AWS
63-
NODE_LAYER_VERSION: 55
6461
run: cp -R ../.layers . && ./integration_tests/run.sh

integration_tests/recorder-extension/a_recorder

Lines changed: 0 additions & 3 deletions
This file was deleted.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
module datadog-lambda-extension/recorder-extension
2+
3+
go 1.14
4+
5+
require (
6+
github.com/DataDog/agent-payload v4.73.0+incompatible // indirect
7+
github.com/gogo/protobuf v1.3.2 // indirect
8+
github.com/gorilla/mux v1.8.0 // indirect
9+
)
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
github.com/DataDog/agent-payload v4.73.0+incompatible h1:SnHWa/x6fkyEw0ZnlWZOeEfE6caBQtviwoaJx2Rvuy4=
2+
github.com/DataDog/agent-payload v4.73.0+incompatible/go.mod h1:/2RW4IC/2z54jtB6RLgq5UtVI1TsX0joDRjKbkLT+mk=
3+
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
4+
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
5+
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
6+
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
7+
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
8+
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
9+
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
10+
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
11+
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
12+
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
13+
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
14+
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
15+
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
16+
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
17+
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
18+
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
19+
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
20+
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
21+
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
22+
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
23+
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
24+
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
25+
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
26+
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
27+
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
28+
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
29+
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
30+
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
31+
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
32+
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
33+
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
34+
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
35+
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
Lines changed: 264 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,264 @@
1+
// Unless explicitly stated otherwise all files in this repository are licensed
2+
// under the Apache License Version 2.0.
3+
// This product includes software developed at Datadog (https://www.datadoghq.com/).
4+
// Copyright 2016-present Datadog, Inc.
5+
6+
// Some parts of this file are taken from : https://github.com/aws-samples/aws-lambda-extensions/tree/main/go-example-extension
7+
8+
package main
9+
10+
import (
11+
"bytes"
12+
"compress/gzip"
13+
"context"
14+
"encoding/json"
15+
"fmt"
16+
"io/ioutil"
17+
"net/http"
18+
"os"
19+
"os/signal"
20+
"sort"
21+
"strings"
22+
"syscall"
23+
"time"
24+
25+
"github.com/DataDog/agent-payload/gogen"
26+
)
27+
28+
const extensionName = "recorder-extension" // extension name has to match the filename
29+
var extensionClient = NewClient(os.Getenv("AWS_LAMBDA_RUNTIME_API"))
30+
31+
func main() {
32+
ctx, cancel := context.WithCancel(context.Background())
33+
34+
sigs := make(chan os.Signal, 1)
35+
signal.Notify(sigs, syscall.SIGTERM, syscall.SIGINT)
36+
go func() {
37+
<-sigs
38+
cancel()
39+
}()
40+
41+
err := extensionClient.Register(ctx, extensionName)
42+
if err != nil {
43+
panic(err)
44+
}
45+
46+
// port 8080 is used by the Lambda Invoke API
47+
port := "3333"
48+
Start(port)
49+
50+
// Will block until shutdown event is received or cancelled via the context.
51+
processEvents(ctx)
52+
}
53+
54+
func processEvents(ctx context.Context) {
55+
for {
56+
select {
57+
case <-ctx.Done():
58+
return
59+
default:
60+
res, err := extensionClient.NextEvent(ctx)
61+
if err != nil {
62+
return
63+
}
64+
if res.EventType == Shutdown {
65+
time.Sleep(1900 * time.Millisecond)
66+
return
67+
}
68+
}
69+
}
70+
}
71+
72+
// JSON representation of a message.
73+
type jsonServerlessPayload struct {
74+
Message jsonServerlessMessage `json:"message"`
75+
Status string `json:"status"`
76+
Timestamp int64 `json:"timestamp"`
77+
Hostname string `json:"hostname"`
78+
Service string `json:"service"`
79+
Source string `json:"ddsource"`
80+
Tags string `json:"ddtags"`
81+
}
82+
83+
type jsonServerlessMessage struct {
84+
Message string `json:"message"`
85+
Lambda *jsonServerlessLambda `json:"lambda,omitempty"`
86+
}
87+
88+
type jsonServerlessLambda struct {
89+
ARN string `json:"arn"`
90+
RequestID string `json:"request_id,omitempty"`
91+
}
92+
93+
// NextEventResponse is the response for /event/next
94+
type NextEventResponse struct {
95+
EventType EventType `json:"eventType"`
96+
}
97+
98+
// EventType represents the type of events recieved from /event/next
99+
type EventType string
100+
101+
const (
102+
// Shutdown is a shutdown event for the environment
103+
Shutdown EventType = "SHUTDOWN"
104+
105+
extensionNameHeader = "Lambda-Extension-Name"
106+
extensionIdentiferHeader = "Lambda-Extension-Identifier"
107+
)
108+
109+
// Client is a simple client for the Lambda Extensions API
110+
type Client struct {
111+
baseURL string
112+
httpClient *http.Client
113+
extensionID string
114+
}
115+
116+
// NewClient returns a Lambda Extensions API client
117+
func NewClient(awsLambdaRuntimeAPI string) *Client {
118+
baseURL := fmt.Sprintf("http://%s/2020-01-01/extension", awsLambdaRuntimeAPI)
119+
return &Client{
120+
baseURL: baseURL,
121+
httpClient: &http.Client{},
122+
}
123+
}
124+
125+
// Register will register the extension with the Extensions API
126+
func (e *Client) Register(ctx context.Context, filename string) error {
127+
const action = "/register"
128+
url := e.baseURL + action
129+
130+
reqBody, err := json.Marshal(map[string]interface{}{
131+
"events": []EventType{Shutdown},
132+
})
133+
if err != nil {
134+
return err
135+
}
136+
httpReq, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewBuffer(reqBody))
137+
if err != nil {
138+
return err
139+
}
140+
httpReq.Header.Set(extensionNameHeader, filename)
141+
httpRes, err := e.httpClient.Do(httpReq)
142+
if err != nil {
143+
return err
144+
}
145+
if httpRes.StatusCode != 200 {
146+
return fmt.Errorf("request failed with status %s", httpRes.Status)
147+
}
148+
defer httpRes.Body.Close()
149+
e.extensionID = httpRes.Header.Get(extensionIdentiferHeader)
150+
return nil
151+
}
152+
153+
// NextEvent blocks while long polling for the next lambda invoke or shutdown
154+
func (e *Client) NextEvent(ctx context.Context) (*NextEventResponse, error) {
155+
const action = "/event/next"
156+
url := e.baseURL + action
157+
158+
httpReq, err := http.NewRequestWithContext(ctx, "GET", url, nil)
159+
if err != nil {
160+
return nil, err
161+
}
162+
httpReq.Header.Set(extensionIdentiferHeader, e.extensionID)
163+
httpRes, err := e.httpClient.Do(httpReq)
164+
if err != nil {
165+
return nil, err
166+
}
167+
if httpRes.StatusCode != 200 {
168+
return nil, fmt.Errorf("request failed with status %s", httpRes.Status)
169+
}
170+
defer httpRes.Body.Close()
171+
body, err := ioutil.ReadAll(httpRes.Body)
172+
if err != nil {
173+
return nil, err
174+
}
175+
res := NextEventResponse{}
176+
err = json.Unmarshal(body, &res)
177+
if err != nil {
178+
return nil, err
179+
}
180+
return &res, nil
181+
}
182+
183+
// Start is starting the http server to receive logs, traces and metrics
184+
func Start(port string) {
185+
go startHTTPServer(port)
186+
}
187+
188+
func startHTTPServer(port string) {
189+
http.HandleFunc("/api/beta/sketches", func(w http.ResponseWriter, r *http.Request) {
190+
body, err := ioutil.ReadAll(r.Body)
191+
if err != nil {
192+
fmt.Printf("Error while reading HTTP request body: %s \n", err)
193+
return
194+
}
195+
pl := new(gogen.SketchPayload)
196+
if err := pl.Unmarshal(body); err != nil {
197+
fmt.Printf("Error while unmarshalling sketches %s \n", err)
198+
return
199+
}
200+
201+
for _, sketch := range pl.Sketches {
202+
jsonSketch, err := json.Marshal(sketch)
203+
if err != nil {
204+
fmt.Printf("Error while JSON encoding the sketch")
205+
}
206+
fmt.Printf("[sketch] %s \n", string(jsonSketch))
207+
}
208+
})
209+
210+
http.HandleFunc("/v1/input", func(w http.ResponseWriter, r *http.Request) {
211+
body, err := ioutil.ReadAll(r.Body)
212+
if err != nil {
213+
return
214+
}
215+
decompressedBody, err := decompress(body)
216+
if err != nil {
217+
return
218+
}
219+
var messages []jsonServerlessPayload
220+
if err := json.Unmarshal(decompressedBody, &messages); err != nil {
221+
return
222+
}
223+
for _, log := range messages {
224+
sortedTags := strings.Split(log.Tags, ",")
225+
sort.Strings(sortedTags)
226+
log.Tags = strings.Join(sortedTags, ",")
227+
jsonLog, err := json.Marshal(log)
228+
if err != nil {
229+
fmt.Printf("Error while JSON encoding the Log")
230+
}
231+
stringJsonLog := string(jsonLog)
232+
// if we log an unwanted log, it will be available in the next log api payload -> infinite loop
233+
if !strings.Contains(stringJsonLog, "[log]") && !strings.Contains(stringJsonLog, "[metric]") {
234+
fmt.Printf("[log] %s\n", stringJsonLog)
235+
}
236+
}
237+
})
238+
239+
http.HandleFunc("/api/v1/series", func(w http.ResponseWriter, r *http.Request) {
240+
})
241+
242+
http.HandleFunc("/api/v1/check_run", func(w http.ResponseWriter, r *http.Request) {
243+
})
244+
245+
err := http.ListenAndServe(":"+port, nil)
246+
if err != nil {
247+
panic(err)
248+
}
249+
}
250+
251+
func decompress(payload []byte) ([]byte, error) {
252+
reader, err := gzip.NewReader(bytes.NewReader(payload))
253+
if err != nil {
254+
return nil, err
255+
}
256+
257+
var buffer bytes.Buffer
258+
_, err = buffer.ReadFrom(reader)
259+
if err != nil {
260+
return nil, err
261+
}
262+
263+
return buffer.Bytes(), nil
264+
}

integration_tests/recorder-extension/package.json

Lines changed: 0 additions & 10 deletions
This file was deleted.

0 commit comments

Comments
 (0)