diff --git a/Makefile b/Makefile index 8b5172718..9d6d8a65b 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ MODULES = $(filter-out $(EXCLUDE_DIRS) ./example/% , $(shell find . -name go.mod LINTER ?= $(shell go env GOPATH)/bin/golangci-lint # The list of Go build tags as they are specified in respective integration test files -INTEGRATION_TESTS = fargate gcr lambda azure +INTEGRATION_TESTS = fargate gcr lambda azure generic_serverless ifeq ($(RUN_LINTER),yes) test: $(LINTER) diff --git a/docker-compose.yaml b/docker-compose.yaml index 1c8ec2b09..b7df297a3 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -13,32 +13,32 @@ services: volumes: - ./example/gorm-postgres/init.sql:/docker-entrypoint-initdb.d/init.sql - rabbitmq: - image: rabbitmq:3.7.8-alpine - ports: - - 5671:5671 - - 5672:5672 + # rabbitmq: + # image: rabbitmq:3.7.8-alpine + # ports: + # - 5671:5671 + # - 5672:5672 - couchbase-server-sandbox: - image: couchbase/server-sandbox:7.1.1 - ports: - - 8091-8094:8091-8094 - - 11210:11210 + # couchbase-server-sandbox: + # image: couchbase/server-sandbox:7.1.1 + # ports: + # - 8091-8094:8091-8094 + # - 11210:11210 - redis: - image: redis - ports: - - 6379:6379 - command: ["redis-server", "--requirepass", "redispw"] + # redis: + # image: redis + # ports: + # - 6379:6379 + # command: ["redis-server", "--requirepass", "redispw"] - mysql: - image: mysql:8.0.1 - platform: linux/amd64 - ports: - - 3306:3306 - environment: - MYSQL_ROOT_PASSWORD: gopw - MYSQL_DATABASE: godb - MYSQL_USER: go - MYSQL_PASSWORD: gopw - MYSQL_ROOT_HOST: 0.0.0.0 + # mysql: + # image: mysql:8.0.1 + # platform: linux/amd64 + # ports: + # - 3306:3306 + # environment: + # MYSQL_ROOT_PASSWORD: gopw + # MYSQL_DATABASE: godb + # MYSQL_USER: go + # MYSQL_PASSWORD: gopw + # MYSQL_ROOT_HOST: 0.0.0.0 diff --git a/example/fiber-eg/go.mod b/example/fiber-eg/go.mod new file mode 100644 index 000000000..8c44f2c97 --- /dev/null +++ b/example/fiber-eg/go.mod @@ -0,0 +1,42 @@ +module fiber-example + +go 1.21.3 + +require ( + github.com/gofiber/fiber/v2 v2.52.5 + github.com/instana/go-sensor v1.63.1 + github.com/instana/go-sensor/instrumentation/instafiber v0.14.0 + github.com/instana/go-sensor/instrumentation/instagorm v1.12.1 + gorm.io/driver/postgres v1.5.9 + gorm.io/gorm v1.25.11 +) + +require ( + github.com/andybalholm/brotli v1.0.5 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect + github.com/jackc/pgx/v5 v5.5.5 // indirect + github.com/jackc/puddle/v2 v2.2.1 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect + github.com/klauspost/compress v1.17.0 // indirect + github.com/looplab/fsm v1.0.1 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/opentracing/opentracing-go v1.2.0 // indirect + github.com/rivo/uniseg v0.2.0 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasthttp v1.51.0 // indirect + github.com/valyala/tcplisten v1.0.0 // indirect + golang.org/x/crypto v0.17.0 // indirect + golang.org/x/sync v0.1.0 // indirect + golang.org/x/sys v0.15.0 // indirect + golang.org/x/text v0.14.0 // indirect +) + +replace ( + github.com/instana/go-sensor => ../../ + github.com/instana/go-sensor/instrumentation/instafiber => ../../instrumentation/instafiber +) diff --git a/example/fiber-eg/go.sum b/example/fiber-eg/go.sum new file mode 100644 index 000000000..a4151dbd4 --- /dev/null +++ b/example/fiber-eg/go.sum @@ -0,0 +1,73 @@ +github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= +github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gofiber/fiber/v2 v2.52.5 h1:tWoP1MJQjGEe4GB5TUGOi7P2E0ZMMRx5ZTG4rT+yGMo= +github.com/gofiber/fiber/v2 v2.52.5/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/instana/go-sensor/instrumentation/instagorm v1.12.1 h1:6ETN2GbW4jRpizHV0guyjGCG4odoBv2xO75R5Yh+1WU= +github.com/instana/go-sensor/instrumentation/instagorm v1.12.1/go.mod h1:An2/M3xF9qwd/FyR3TMoqyCsbSh04WmOTTnrs+wSN38= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw= +github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A= +github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= +github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= +github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/looplab/fsm v1.0.1 h1:OEW0ORrIx095N/6lgoGkFkotqH6s7vaFPsgjLAaF5QU= +github.com/looplab/fsm v1.0.1/go.mod h1:PmD3fFvQEIsjMEfvZdrCDZ6y8VwKTwWNjlpEr6IKPO4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI= +github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= +github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= +github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA= +github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g= +github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= +github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= +golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= +golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/driver/postgres v1.5.9 h1:DkegyItji119OlcaLjqN11kHoUgZ/j13E0jkJZgD6A8= +gorm.io/driver/postgres v1.5.9/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI= +gorm.io/driver/sqlite v1.5.0 h1:zKYbzRCpBrT1bNijRnxLDJWPjVfImGEn0lSnUY5gZ+c= +gorm.io/driver/sqlite v1.5.0/go.mod h1:kDMDfntV9u/vuMmz8APHtHF0b4nyBB7sfCieC6G8k8I= +gorm.io/gorm v1.25.11 h1:/Wfyg1B/je1hnDx3sMkX+gAlxrlZpn6X0BXRlwXlvHg= +gorm.io/gorm v1.25.11/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ= diff --git a/example/fiber-eg/main.go b/example/fiber-eg/main.go new file mode 100644 index 000000000..d3558e38f --- /dev/null +++ b/example/fiber-eg/main.go @@ -0,0 +1,70 @@ +// (c) Copyright IBM Corp. 2023 + +//go:build go1.18 +// +build go1.18 + +package main + +import ( + "log" + "os" + + "github.com/gofiber/fiber/v2" + instana "github.com/instana/go-sensor" + "github.com/instana/go-sensor/instrumentation/instafiber" + "github.com/instana/go-sensor/instrumentation/instagorm" + "gorm.io/driver/postgres" + "gorm.io/gorm" +) + +var db *gorm.DB + +func main() { + + os.Setenv("INSTANA_TIMEOUT", "2000") + + // Create a sensor for instana instrumentation + sensor := instana.InitCollector(&instana.Options{ + Service: "nithin-fb-example-with-host-agent", + LogLevel: instana.Debug, + }) + + var err error + dsn := "host=localhost user=postgres password=mysecretpassword dbname=postgres port=5432 sslmode=disable" + db, err = gorm.Open(postgres.Open(dsn), &gorm.Config{}) + + if err != nil { + panic(err) + } + + instagorm.Instrument(db, sensor, dsn) + + app := fiber.New() + + // Use the instafiber.TraceHandler for instrumenting the handler + app.Get("/greet", instafiber.TraceHandler(sensor, "greet", "/greet", hello)) + + // Start server + log.Fatal(app.Listen(":3000")) + + // test() +} + +func hello(c *fiber.Ctx) error { + + var stud student + + db.WithContext(c.UserContext()).First(&stud) + + return c.SendString("Hello " + stud.StudentName + "!") +} + +type student struct { + StudentName string `gorm:"column:studentname"` + StudentID uint `gorm:"primarykey,column:studentid"` +} + +// implementing the schema.Tabler interface +func (student) TableName() string { + return "student" +} diff --git a/go.mod b/go.mod index 4aced19a6..3cc2d8796 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/instana/go-sensor go 1.21 require ( + github.com/google/uuid v1.6.0 github.com/looplab/fsm v1.0.1 github.com/opentracing/opentracing-go v1.2.0 github.com/stretchr/testify v1.8.1 diff --git a/go.sum b/go.sum index 466a27ea8..238d40587 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/looplab/fsm v1.0.1 h1:OEW0ORrIx095N/6lgoGkFkotqH6s7vaFPsgjLAaF5QU= github.com/looplab/fsm v1.0.1/go.mod h1:PmD3fFvQEIsjMEfvZdrCDZ6y8VwKTwWNjlpEr6IKPO4= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= diff --git a/local_serverless_agent.go b/local_serverless_agent.go new file mode 100644 index 000000000..f6ea1d4cd --- /dev/null +++ b/local_serverless_agent.go @@ -0,0 +1,167 @@ +// (c) Copyright IBM Corp. 2022 + +package instana + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "os" + "sync" + "time" + + "github.com/google/uuid" + "github.com/instana/go-sensor/acceptor" + "github.com/instana/go-sensor/autoprofile" +) + +const ( + flushPeriodForGenericInSec = 2 +) + +type localAgent struct { + Endpoint string + Key string + PluginName string + PID int + + snapshot serverlessSnapshot + + mu sync.Mutex + spanQueue []Span + + client *http.Client + logger LeveledLogger +} + +func newLocalAgent(acceptorEndpoint, agentKey string, client *http.Client, logger LeveledLogger) *localAgent { + if logger == nil { + logger = defaultLogger + } + + if client == nil { + client = http.DefaultClient + // TODO: defaultServerlessTimeout is increased from 500 millisecond to 2 second + // as serverless API latency is high. This should be reduced once latency is minimized. + client.Timeout = 2 * time.Second + } + + logger.Debug("initializing local serverless agent") + + agent := &localAgent{ + Endpoint: acceptorEndpoint, + Key: agentKey, + PID: os.Getpid(), + client: client, + logger: logger, + } + + go func() { + t := time.NewTicker(flushPeriodForGenericInSec * time.Second) + defer t.Stop() + + for range t.C { + if err := agent.Flush(context.Background()); err != nil { + agent.logger.Error("failed to post collected data: ", err) + } + } + }() + + return agent +} + +func (a *localAgent) Ready() bool { return true } + +func (a *localAgent) SendMetrics(acceptor.Metrics) error { return nil } + +func (a *localAgent) SendEvent(*EventData) error { return nil } + +func (a *localAgent) SendSpans(spans []Span) error { + a.enqueueSpans(spans) + return nil +} + +func (a *localAgent) SendProfiles([]autoprofile.Profile) error { return nil } + +func (a *localAgent) Flush(ctx context.Context) error { + + from := newServerlessAgentFromS("Generic_Agent"+uuid.New().String(), "local") + + payload := struct { + Spans []Span `json:"spans,omitempty"` + }{} + + a.mu.Lock() + payload.Spans = make([]Span, len(a.spanQueue)) + copy(payload.Spans, a.spanQueue) + a.spanQueue = a.spanQueue[:0] + a.mu.Unlock() + + for i := range payload.Spans { + payload.Spans[i].From = from + } + + buf := bytes.NewBuffer(nil) + if err := json.NewEncoder(buf).Encode(payload); err != nil { + return fmt.Errorf("failed to marshal traces payload: %s", err) + } + + payloadSize := buf.Len() + if payloadSize > maxContentLength { + a.logger.Warn(fmt.Sprintf("failed to send the spans. Payload size: %d exceeded max size: %d", payloadSize, maxContentLength)) + return payloadTooLargeErr + } + + req, err := http.NewRequest(http.MethodPost, a.Endpoint+"/bundle", buf) + if err != nil { + a.enqueueSpans(payload.Spans) + return fmt.Errorf("failed to prepare send traces request: %s", err) + } + + req.Header.Set("Content-Type", "application/json") + + if err := a.sendRequest(req.WithContext(ctx)); err != nil { + a.enqueueSpans(payload.Spans) + return fmt.Errorf("failed to send traces, will retry later: %dsec. Error details: %s", + flushPeriodForGenericInSec, err.Error()) + } + + return nil +} + +func (a *localAgent) enqueueSpans(spans []Span) { + a.mu.Lock() + defer a.mu.Unlock() + + a.spanQueue = append(a.spanQueue, spans...) +} + +func (a *localAgent) sendRequest(req *http.Request) error { + req.Header.Set("X-Instana-Host", a.snapshot.Host) + req.Header.Set("X-Instana-Key", a.Key) + + resp, err := a.client.Do(req) + if err != nil { + return fmt.Errorf("failed to send request to the serverless agent: %s", err) + } + + defer resp.Body.Close() + + if resp.StatusCode >= http.StatusBadRequest { + respBody, err := io.ReadAll(resp.Body) + if err != nil { + a.logger.Debug("failed to read serverless agent response: ", err.Error()) + return err + } + + a.logger.Info("serverless agent has responded with ", resp.Status, ": ", string(respBody)) + return err + } + + io.CopyN(io.Discard, resp.Body, 1<<20) + + return nil +} diff --git a/local_serverless_agent_test.go b/local_serverless_agent_test.go new file mode 100644 index 000000000..b25ef3f25 --- /dev/null +++ b/local_serverless_agent_test.go @@ -0,0 +1,106 @@ +// (c) Copyright IBM Corp. 2024 + +//go:build generic_serverless && integration +// +build generic_serverless,integration + +package instana_test + +import ( + "context" + "encoding/json" + "log" + "os" + "testing" + "time" + + instana "github.com/instana/go-sensor" + "github.com/stretchr/testify/require" +) + +var agent *serverlessAgent + +func TestMain(m *testing.M) { + teardownInstanaEnv := setupInstanaEnv() + defer teardownInstanaEnv() + + var err error + agent, err = setupServerlessAgent() + if err != nil { + log.Fatalf("failed to initialize serverless agent: %s", err) + } + + os.Exit(m.Run()) +} + +func TestLocalServerlessAgent_SendSpans(t *testing.T) { + defer agent.Reset() + + tracer := instana.NewTracer() + sensor := instana.NewSensorWithTracer(tracer) + defer instana.ShutdownSensor() + + sp := sensor.Tracer().StartSpan("local_serverless") + sp.Finish() + + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + + require.NoError(t, tracer.Flush(ctx)) + require.Len(t, agent.Bundles, 1) + + var spans []map[string]json.RawMessage + for _, bundle := range agent.Bundles { + var payload struct { + Spans []map[string]json.RawMessage `json:"spans"` + } + + require.NoError(t, json.Unmarshal(bundle.Body, &payload), "%s", string(bundle.Body)) + spans = append(spans, payload.Spans...) + } + + require.Len(t, spans, 1) + // assert.JSONEq(t, `{"hl": true, "cp": "azure", "e": "/subscriptions/testgh05-3f0d-4bf9-8f53-209408003632/resourceGroups/testresourcegroup/providers/Microsoft.App/containerapps/azureapp"}`, string(spans[0]["f"])) +} + +func TestLocalServerlessAgent_SendSpans_Error(t *testing.T) { + defer agent.Reset() + + tracer := instana.NewTracer() + sensor := instana.NewSensorWithTracer(tracer) + defer instana.ShutdownSensor() + + sp := sensor.Tracer().StartSpan("http") + sp.SetTag("returnError", "true") + sp.Finish() + + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + require.NoError(t, tracer.Flush(ctx)) + require.Len(t, agent.Bundles, 0) +} + +func setupInstanaEnv() func() { + var teardownFuncs []func() + + teardownFuncs = append(teardownFuncs, restoreEnvVarFunc("INSTANA_AGENT_KEY")) + os.Setenv("INSTANA_AGENT_KEY", "testkey1") + + teardownFuncs = append(teardownFuncs, restoreEnvVarFunc("INSTANA_ZONE")) + os.Setenv("INSTANA_ZONE", "testzone") + + teardownFuncs = append(teardownFuncs, restoreEnvVarFunc("INSTANA_TAGS")) + os.Setenv("INSTANA_TAGS", "key1=value1,key2") + + teardownFuncs = append(teardownFuncs, restoreEnvVarFunc("INSTANA_SECRETS")) + os.Setenv("INSTANA_SECRETS", "contains-ignore-case:key,password,secret,classified") + + teardownFuncs = append(teardownFuncs, restoreEnvVarFunc("CLASSIFIED_DATA")) + os.Setenv("CLASSIFIED_DATA", "classified") + + return func() { + for _, f := range teardownFuncs { + f() + } + } +} diff --git a/sensor.go b/sensor.go index 1f4a1c131..5155d886e 100644 --- a/sensor.go +++ b/sensor.go @@ -314,6 +314,6 @@ func newServerlessAgent(serviceName, agentEndpoint, agentKey string, os.Getenv(containerAppHostName) != "": return newAzureAgent(agentEndpoint, agentKey, client, logger) default: - return nil + return newLocalAgent(agentEndpoint, agentKey, client, logger) } }