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