diff --git a/profiler/profiler_test.go b/profiler/profiler_test.go index 844078948b..4755ed899f 100644 --- a/profiler/profiler_test.go +++ b/profiler/profiler_test.go @@ -30,6 +30,7 @@ import ( "gopkg.in/DataDog/dd-trace-go.v1/internal/globalconfig" "gopkg.in/DataDog/dd-trace-go.v1/internal/httpmem" "gopkg.in/DataDog/dd-trace-go.v1/internal/log" + "gopkg.in/DataDog/dd-trace-go.v1/internal/orchestrion" "gopkg.in/DataDog/dd-trace-go.v1/internal/traceprof" "gopkg.in/DataDog/dd-trace-go.v1/internal/version" @@ -748,3 +749,33 @@ func TestUDSDefault(t *testing.T) { <-profiles } + +func TestOrchestrionProfileInfo(t *testing.T) { + testCases := []struct { + env string + want string + }{ + {want: "manual"}, + {env: "1", want: "manual"}, + {env: "true", want: "manual"}, + {env: "auto", want: "auto"}, + } + for _, tc := range testCases { + t.Run(fmt.Sprintf("env=\"%s\"", tc.env), func(t *testing.T) { + t.Setenv("DD_PROFILING_ENABLED", tc.env) + p := doOneShortProfileUpload(t) + info := p.event.Info.Profiler + t.Logf("%+v", info) + if got := info.Activation; got != tc.want { + t.Errorf("wanted profiler activation \"%s\", got %s", tc.want, got) + } + want := "none" + if orchestrion.Enabled() { + want = "orchestrion" + } + if got := info.SSI.Mechanism; got != want { + t.Errorf("wanted profiler injected = %v, got %v", want, got) + } + }) + } +} diff --git a/profiler/upload.go b/profiler/upload.go index 29160ee040..a8b98f6560 100644 --- a/profiler/upload.go +++ b/profiler/upload.go @@ -16,10 +16,12 @@ import ( "mime/multipart" "net/http" "net/textproto" + "os" "strings" "time" "gopkg.in/DataDog/dd-trace-go.v1/internal/log" + "gopkg.in/DataDog/dd-trace-go.v1/internal/orchestrion" ) // maxRetries specifies the maximum number of retries to have when an error occurs. @@ -144,6 +146,20 @@ type uploadEvent struct { Version string `json:"version"` EndpointCounts map[string]uint64 `json:"endpoint_counts,omitempty"` CustomAttributes []string `json:"custom_attributes,omitempty"` + Info struct { + Profiler profilerInfo `json:"profiler"` + } `json:"info"` +} + +// profilerInfo holds profiler-specific information which should be attached to +// the event for backend consumption +type profilerInfo struct { + SSI struct { + Mechanism string `json:"mechanism,omitempty"` + } `json:"ssi"` + // Activation distinguishes how the profiler was enabled, either "auto" + // (env var set via admission controller) or "manual" + Activation string `json:"activation"` } // encode encodes the profile as a multipart mime request. @@ -167,6 +183,22 @@ func encode(bat batch, tags []string) (contentType string, body io.Reader, err e CustomAttributes: bat.customAttributes, } + // DD_PROFILING_ENABLED is only used to enable profiling when added with + // Orchestrion. The "auto" value comes from the Datadog Kubernetes + // admission controller. Otherwise, the client library doesn't care + // about the value and assumes it was something "truthy", or this code + // wouldn't run. We just track it to be consistent with other languages + if os.Getenv("DD_PROFILING_ENABLED") == "auto" { + event.Info.Profiler.Activation = "auto" + } else { + event.Info.Profiler.Activation = "manual" + } + if orchestrion.Enabled() { + event.Info.Profiler.SSI.Mechanism = "orchestrion" + } else { + event.Info.Profiler.SSI.Mechanism = "none" + } + for _, p := range bat.profiles { event.Attachments = append(event.Attachments, p.name) f, err := mw.CreateFormFile(p.name, p.name)