github.com/instana/go-sensor@v1.62.2-0.20240520081010-4919868049e1/fargate_agent_test.go (about) 1 // (c) Copyright IBM Corp. 2021 2 // (c) Copyright Instana Inc. 2020 3 4 //go:build fargate && integration 5 // +build fargate,integration 6 7 package instana_test 8 9 import ( 10 "context" 11 "encoding/json" 12 "log" 13 "net/http" 14 "net/http/httptest" 15 "os" 16 "testing" 17 "time" 18 19 instana "github.com/instana/go-sensor" 20 "github.com/stretchr/testify/assert" 21 "github.com/stretchr/testify/require" 22 ) 23 24 var agent *serverlessAgent 25 26 func TestMain(m *testing.M) { 27 teardownEnv := setupAWSFargateEnv() 28 defer teardownEnv() 29 30 teardownSrv := setupMetadataServer() 31 defer teardownSrv() 32 33 defer restoreEnvVarFunc("INSTANA_AGENT_KEY") 34 os.Setenv("INSTANA_AGENT_KEY", "testkey1") 35 36 defer restoreEnvVarFunc("INSTANA_ZONE") 37 os.Setenv("INSTANA_ZONE", "testzone") 38 39 defer restoreEnvVarFunc("INSTANA_TAGS") 40 os.Setenv("INSTANA_TAGS", "key1=value1,key2") 41 42 defer restoreEnvVarFunc("INSTANA_SECRETS") 43 os.Setenv("INSTANA_SECRETS", "contains-ignore-case:key,password,secret,classified") 44 45 defer restoreEnvVarFunc("CLASSIFIED_DATA") 46 os.Setenv("CLASSIFIED_DATA", "classified") 47 48 var err error 49 agent, err = setupServerlessAgent() 50 if err != nil { 51 log.Fatalf("failed to initialize serverless agent: %s", err) 52 } 53 54 instana.InitSensor(instana.DefaultOptions()) 55 56 os.Exit(m.Run()) 57 } 58 59 func TestFargateAgent_SendMetrics(t *testing.T) { 60 defer agent.Reset() 61 62 require.Eventually(t, func() bool { return len(agent.Bundles) > 0 }, 2*time.Second, 500*time.Millisecond) 63 64 collected := agent.Bundles[0] 65 66 assert.Equal(t, "arn:aws:ecs:us-east-2:012345678910:task/9781c248-0edd-4cdb-9a93-f63cb662a5d3::nginx-curl", collected.Header.Get("X-Instana-Host")) 67 assert.Equal(t, "testkey1", collected.Header.Get("X-Instana-Key")) 68 assert.NotEmpty(t, collected.Header.Get("X-Instana-Time")) 69 70 var payload struct { 71 Metrics struct { 72 Plugins []struct { 73 Name string `json:"name"` 74 EntityID string `json:"entityId"` 75 Data map[string]interface{} `json:"data"` 76 } `json:"plugins"` 77 } `json:"metrics"` 78 } 79 require.NoError(t, json.Unmarshal(collected.Body, &payload)) 80 81 pluginData := make(map[string][]serverlessAgentPluginPayload) 82 for _, plugin := range payload.Metrics.Plugins { 83 pluginData[plugin.Name] = append(pluginData[plugin.Name], serverlessAgentPluginPayload{plugin.EntityID, plugin.Data}) 84 } 85 86 t.Run("AWS ECS Task plugin payload", func(t *testing.T) { 87 require.Len(t, pluginData["com.instana.plugin.aws.ecs.task"], 1) 88 d := pluginData["com.instana.plugin.aws.ecs.task"][0] 89 90 assert.NotEmpty(t, d.EntityID) 91 assert.Equal(t, d.Data["taskArn"], d.EntityID) 92 93 assert.Equal(t, "testzone", d.Data["instanaZone"]) 94 assert.Equal(t, map[string]interface{}{"key1": "value1", "key2": nil}, d.Data["tags"]) 95 assert.Equal(t, "default", d.Data["clusterArn"]) 96 assert.Equal(t, "nginx", d.Data["taskDefinition"]) 97 assert.Equal(t, "5", d.Data["taskDefinitionVersion"]) 98 }) 99 100 t.Run("AWS ECS Container plugin payload", func(t *testing.T) { 101 require.Len(t, pluginData["com.instana.plugin.aws.ecs.container"], 2) 102 103 containers := make(map[string]serverlessAgentPluginPayload) 104 for _, container := range pluginData["com.instana.plugin.aws.ecs.container"] { 105 containers[container.EntityID] = container 106 } 107 108 t.Run("instrumented", func(t *testing.T) { 109 d := containers["arn:aws:ecs:us-east-2:012345678910:task/9781c248-0edd-4cdb-9a93-f63cb662a5d3::nginx-curl"] 110 require.NotEmpty(t, d) 111 112 assert.NotEmpty(t, d.EntityID) 113 114 require.IsType(t, d.Data["taskArn"], "") 115 require.IsType(t, d.Data["containerName"], "") 116 assert.Equal(t, d.Data["taskArn"].(string)+"::"+d.Data["containerName"].(string), d.EntityID) 117 118 if assert.NotEmpty(t, d.Data["taskArn"]) { 119 require.NotEmpty(t, pluginData["com.instana.plugin.aws.ecs.task"]) 120 assert.Equal(t, pluginData["com.instana.plugin.aws.ecs.task"][0].EntityID, d.Data["taskArn"]) 121 } 122 123 assert.Equal(t, true, d.Data["instrumented"]) 124 assert.Equal(t, "go", d.Data["runtime"]) 125 assert.Equal(t, "43481a6ce4842eec8fe72fc28500c6b52edcc0917f105b83379f88cac1ff3946", d.Data["dockerId"]) 126 }) 127 128 t.Run("non-instrumented", func(t *testing.T) { 129 d := containers["arn:aws:ecs:us-east-2:012345678910:task/9781c248-0edd-4cdb-9a93-f63cb662a5d3::~internal~ecs~pause"] 130 require.NotEmpty(t, d) 131 132 assert.NotEmpty(t, d.EntityID) 133 134 require.IsType(t, d.Data["taskArn"], "") 135 require.IsType(t, d.Data["containerName"], "") 136 assert.Equal(t, d.Data["taskArn"].(string)+"::"+d.Data["containerName"].(string), d.EntityID) 137 138 if assert.NotEmpty(t, d.Data["taskArn"]) { 139 require.NotEmpty(t, pluginData["com.instana.plugin.aws.ecs.task"]) 140 assert.Equal(t, pluginData["com.instana.plugin.aws.ecs.task"][0].EntityID, d.Data["taskArn"]) 141 } 142 143 assert.Nil(t, d.Data["instrumented"]) 144 assert.Empty(t, d.Data["runtime"]) 145 assert.Equal(t, "731a0d6a3b4210e2448339bc7015aaa79bfe4fa256384f4102db86ef94cbbc4c", d.Data["dockerId"]) 146 }) 147 }) 148 149 t.Run("Docker plugin payload", func(t *testing.T) { 150 require.Len(t, pluginData["com.instana.plugin.docker"], 2) 151 152 containers := make(map[string]serverlessAgentPluginPayload) 153 for _, container := range pluginData["com.instana.plugin.docker"] { 154 containers[container.EntityID] = container 155 } 156 157 t.Run("instrumented", func(t *testing.T) { 158 d := containers["arn:aws:ecs:us-east-2:012345678910:task/9781c248-0edd-4cdb-9a93-f63cb662a5d3::nginx-curl"] 159 require.NotEmpty(t, d) 160 161 assert.NotEmpty(t, d.EntityID) 162 assert.Equal(t, "43481a6ce4842eec8fe72fc28500c6b52edcc0917f105b83379f88cac1ff3946", d.Data["Id"]) 163 }) 164 165 t.Run("non-instrumented", func(t *testing.T) { 166 d := containers["arn:aws:ecs:us-east-2:012345678910:task/9781c248-0edd-4cdb-9a93-f63cb662a5d3::~internal~ecs~pause"] 167 require.NotEmpty(t, d) 168 169 assert.NotEmpty(t, d.EntityID) 170 assert.Equal(t, "731a0d6a3b4210e2448339bc7015aaa79bfe4fa256384f4102db86ef94cbbc4c", d.Data["Id"]) 171 }) 172 }) 173 174 t.Run("Process plugin payload", func(t *testing.T) { 175 require.Len(t, pluginData["com.instana.plugin.process"], 1) 176 d := pluginData["com.instana.plugin.process"][0] 177 178 assert.NotEmpty(t, d.EntityID) 179 180 assert.Equal(t, "docker", d.Data["containerType"]) 181 assert.Equal(t, "43481a6ce4842eec8fe72fc28500c6b52edcc0917f105b83379f88cac1ff3946", d.Data["container"]) 182 if assert.IsType(t, map[string]interface{}{}, d.Data["env"]) { 183 env := d.Data["env"].(map[string]interface{}) 184 185 assert.Equal(t, os.Getenv("INSTANA_ZONE"), env["INSTANA_ZONE"]) 186 assert.Equal(t, os.Getenv("INSTANA_TAGS"), env["INSTANA_TAGS"]) 187 assert.Equal(t, os.Getenv("INSTANA_AGENT_KEY"), env["INSTANA_AGENT_KEY"]) 188 189 assert.Equal(t, "<redacted>", env["INSTANA_SECRETS"]) 190 assert.Equal(t, "<redacted>", env["CLASSIFIED_DATA"]) 191 } 192 }) 193 194 t.Run("Go process plugin payload", func(t *testing.T) { 195 require.Len(t, pluginData["com.instana.plugin.golang"], 1) 196 d := pluginData["com.instana.plugin.golang"][0] 197 198 assert.NotEmpty(t, d.EntityID) 199 200 require.NotEmpty(t, pluginData["com.instana.plugin.process"]) 201 assert.Equal(t, pluginData["com.instana.plugin.process"][0].EntityID, d.EntityID) 202 203 assert.NotEmpty(t, d.Data["metrics"]) 204 }) 205 } 206 207 func TestFargateAgent_SendSpans(t *testing.T) { 208 defer agent.Reset() 209 210 sensor := instana.NewSensor("testing") 211 212 sp := sensor.Tracer().StartSpan("entry") 213 sp.SetTag("value", "42") 214 sp.Finish() 215 216 require.Eventually(t, func() bool { 217 if len(agent.Bundles) == 0 { 218 return false 219 } 220 221 for _, bundle := range agent.Bundles { 222 var payload struct { 223 Spans []json.RawMessage `json:"spans"` 224 } 225 226 json.Unmarshal(bundle.Body, &payload) 227 if len(payload.Spans) > 0 { 228 return true 229 } 230 } 231 232 return false 233 }, 4*time.Second, 500*time.Millisecond) 234 235 var spans []map[string]json.RawMessage 236 for _, bundle := range agent.Bundles { 237 var payload struct { 238 Spans []map[string]json.RawMessage `json:"spans"` 239 } 240 241 require.NoError(t, json.Unmarshal(bundle.Body, &payload), "%s", string(bundle.Body)) 242 spans = append(spans, payload.Spans...) 243 } 244 245 require.Len(t, spans, 1) 246 assert.JSONEq(t, `{"hl": true, "cp": "aws", "e": "arn:aws:ecs:us-east-2:012345678910:task/9781c248-0edd-4cdb-9a93-f63cb662a5d3::nginx-curl"}`, string(spans[0]["f"])) 247 } 248 249 func TestFargateAgent_FlushSpans(t *testing.T) { 250 defer agent.Reset() 251 252 tracer := instana.NewTracer() 253 sensor := instana.NewSensorWithTracer(tracer) 254 255 sp := sensor.Tracer().StartSpan("entry") 256 sp.SetTag("value", "42") 257 sp.Finish() 258 259 ctx, cancel := context.WithTimeout(context.Background(), time.Second) 260 defer cancel() 261 262 require.NoError(t, tracer.Flush(ctx)) 263 } 264 265 func setupAWSFargateEnv() func() { 266 teardown := restoreEnvVarFunc("AWS_EXECUTION_ENV") 267 os.Setenv("AWS_EXECUTION_ENV", "AWS_ECS_FARGATE") 268 269 return teardown 270 } 271 272 func setupMetadataServer() func() { 273 mux := http.NewServeMux() 274 mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) { 275 http.ServeFile(w, req, "aws/testdata/container_metadata.json") 276 }) 277 mux.HandleFunc("/task", func(w http.ResponseWriter, req *http.Request) { 278 http.ServeFile(w, req, "aws/testdata/task_metadata.json") 279 }) 280 mux.HandleFunc("/task/stats", func(w http.ResponseWriter, req *http.Request) { 281 http.ServeFile(w, req, "aws/testdata/task_stats.json") 282 }) 283 284 srv := httptest.NewServer(mux) 285 286 teardown := restoreEnvVarFunc("ECS_CONTAINER_METADATA_URI") 287 os.Setenv("ECS_CONTAINER_METADATA_URI", srv.URL) 288 289 return func() { 290 teardown() 291 srv.Close() 292 } 293 }